summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy3
m---------.mason0
-rw-r--r--.mention-bot5
-rw-r--r--.travis.yml39
-rw-r--r--.ycm_extra_conf.py5
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--INSTALL.md2
-rw-r--r--Makefile116
-rw-r--r--README.md4
-rw-r--r--benchmark/parse/filter.cpp48
-rwxr-xr-xconfigure5
-rw-r--r--include/mbgl/annotation/annotation.hpp13
-rw-r--r--include/mbgl/gl/gl.hpp10
-rw-r--r--include/mbgl/gl/gl_helper.hpp4
-rw-r--r--include/mbgl/gl/gl_values.hpp134
-rw-r--r--include/mbgl/map/camera.hpp30
-rw-r--r--include/mbgl/map/map.hpp10
-rw-r--r--include/mbgl/map/mode.hpp17
-rw-r--r--include/mbgl/map/update.hpp12
-rw-r--r--include/mbgl/map/view.hpp1
-rw-r--r--include/mbgl/platform/darwin/settings_nsuserdefaults.hpp27
-rw-r--r--include/mbgl/platform/default/glfw_view.hpp4
-rw-r--r--include/mbgl/platform/default/headless_view.hpp4
-rw-r--r--include/mbgl/platform/event.hpp31
-rw-r--r--include/mbgl/platform/log.hpp10
-rw-r--r--include/mbgl/platform/platform.hpp6
-rw-r--r--include/mbgl/storage/default_file_source.hpp4
-rw-r--r--include/mbgl/storage/file_source.hpp8
-rw-r--r--include/mbgl/storage/network_status.hpp5
-rw-r--r--include/mbgl/storage/offline.hpp10
-rw-r--r--include/mbgl/storage/resource.hpp4
-rw-r--r--include/mbgl/storage/response.hpp2
-rw-r--r--include/mbgl/style/conversion.hpp95
-rw-r--r--include/mbgl/style/conversion/constant.hpp174
-rw-r--r--include/mbgl/style/conversion/filter.hpp150
-rw-r--r--include/mbgl/style/conversion/function.hpp69
-rw-r--r--include/mbgl/style/conversion/geojson.hpp15
-rw-r--r--include/mbgl/style/conversion/layer.hpp208
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp136
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp.ejs47
-rw-r--r--include/mbgl/style/conversion/property_setter.hpp57
-rw-r--r--include/mbgl/style/conversion/property_value.hpp36
-rw-r--r--include/mbgl/style/conversion/source.hpp182
-rw-r--r--include/mbgl/style/conversion/tileset.hpp67
-rw-r--r--include/mbgl/style/filter.hpp44
-rw-r--r--include/mbgl/style/filter_evaluator.hpp (renamed from src/mbgl/style/filter_evaluator.hpp)69
-rw-r--r--include/mbgl/style/function.hpp4
-rw-r--r--include/mbgl/style/layer.hpp5
-rw-r--r--include/mbgl/style/layers/background_layer.hpp6
-rw-r--r--include/mbgl/style/layers/circle_layer.hpp20
-rw-r--r--include/mbgl/style/layers/fill_layer.hpp19
-rw-r--r--include/mbgl/style/layers/layer.hpp.ejs75
-rw-r--r--include/mbgl/style/layers/line_layer.hpp25
-rw-r--r--include/mbgl/style/layers/raster_layer.hpp18
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp50
-rw-r--r--include/mbgl/style/source.hpp63
-rw-r--r--include/mbgl/style/sources/geojson_source.hpp54
-rw-r--r--include/mbgl/style/sources/raster_source.hpp20
-rw-r--r--include/mbgl/style/sources/vector_source.hpp20
-rw-r--r--include/mbgl/style/transition_options.hpp10
-rw-r--r--include/mbgl/style/types.hpp25
-rw-r--r--include/mbgl/util/char_array_buffer.hpp55
-rw-r--r--include/mbgl/util/color.hpp34
-rw-r--r--include/mbgl/util/constants.hpp7
-rw-r--r--include/mbgl/util/convert.hpp17
-rw-r--r--include/mbgl/util/enum.hpp60
-rw-r--r--include/mbgl/util/exception.hpp16
-rw-r--r--include/mbgl/util/feature.hpp12
-rw-r--r--include/mbgl/util/geo.hpp32
-rw-r--r--include/mbgl/util/geojson.hpp10
-rw-r--r--include/mbgl/util/geometry.hpp24
-rw-r--r--include/mbgl/util/projection.hpp14
-rw-r--r--include/mbgl/util/range.hpp25
-rw-r--r--include/mbgl/util/run_loop.hpp10
-rw-r--r--include/mbgl/util/tileset.hpp20
-rw-r--r--include/mbgl/util/traits.hpp4
-rw-r--r--include/mbgl/util/unitbezier.hpp39
-rw-r--r--mbgl.gypi56
-rw-r--r--package.json9
-rw-r--r--platform/android/CHANGELOG.md65
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapboxAccountManager.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java17
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java76
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java8
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java29
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java29
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java140
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java336
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MultiPoint.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polygon.java39
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polyline.java26
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java189
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/ConversionException.java22
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxAccountManagerNotStartedException.java23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/CustomLayer.java23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java470
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java230
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java101
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java121
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java2
-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/UiSettings.java45
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java224
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java120
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java63
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java71
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java73
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java135
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java150
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java115
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Function.java85
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java112
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LayoutProperty.java9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java191
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/NoSuchLayerException.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PaintProperty.java9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java237
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java1300
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java56
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java100
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java508
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs107
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property.java.ejs53
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs78
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java44
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java33
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/TileSet.java290
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java45
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/GzipRequestInterceptor.java54
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEvent.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java76
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java27
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/default_markerview.pngbin0 -> 1669 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/default_markerview.pngbin0 -> 1115 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/default_markerview.pngbin0 -> 2163 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/default_markerview.pngbin0 -> 3163 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/default_markerview.pngbin0 -> 4071 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/view_image_marker.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/build.gradle10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BaseMapboxMapTest.java36
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BaseTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BaseTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BulkMarkerActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BulkMarkerActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/DirectionsActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DirectionsActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/DynamicMarkerChangeActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DynamicMarkerChangeActivityTest.java)6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/GeocoderActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/GeocoderActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/InfoWindowActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/InfoWindowAdapterActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowAdapterActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/LatLngBoundsActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/LatLngBoundsActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/ManualZoomActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ManualZoomActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MapFragmentActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapFragmentActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MapPaddingActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapPaddingActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MaxMinZoomActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MaxMinZoomActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MyLocationTrackingModeActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MyLocationTrackingModeActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/PolylineActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/PolylineActivityTest.java)4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/ScrollByActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScrollByActivityTest.java)2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/SupportMapFragmentActivityTest.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/SupportMapFragmentActivityTest.java)2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/DrawerUtils.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/DrawerUtils.java)2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/GestureUtils.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/GestureUtils.java)2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/OnMapReadyIdlingResource.java57
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/ScreenshotUtil.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ScreenshotUtil.java)2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/ViewUtils.java (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ViewUtils.java)2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/RotateTest.java67
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/TiltTest.java67
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/ZoomTest.java74
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapUtils.java21
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/BackgroundLayerTest.java142
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/CircleLayerTest.java255
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/FillLayerTest.java287
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/LineLayerTest.java428
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RasterLayerTest.java242
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleBackgroundLayerTest.java61
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleTests.java91
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleTimingTests.java45
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/SymbolLayerTest.java1283
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/layer.junit.ejs146
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScreenshotActivityTest.java124
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/BaseMainActivityTest.java46
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/CompassViewTest.java48
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleRotateTest.java29
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleZoomTest.java28
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/utils/ViewAssertion.java17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml94
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java176
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java56
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java51
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java285
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewScaleActivity.java141
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java92
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java74
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/directions/DirectionsActivity.java104
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java111
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java89
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java112
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java169
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java16
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java238
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SurfaceViewMediaControlActivity.java142
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/navigation/LocationPickerActivity.java467
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java366
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java69
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java87
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java68
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java52
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerView.java11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java79
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerView.java18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerViewOptions.java92
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/anim/pulse.xml24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/ic_drawer.pngbin0 -> 2829 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/icon.pngbin1587 -> 19772 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/ic_drawer.pngbin0 -> 2820 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/icon.pngbin913 -> 11003 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/ic_drawer.pngbin0 -> 2836 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/icon.pngbin1587 -> 30669 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_drawer.pngbin0 -> 202 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/icon.pngbin2382 -> 58564 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_car_top.pngbin0 -> 19879 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_taxi_top.pngbin0 -> 11275 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/icon.pngbin3018 -> 93272 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_clear_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_car_black_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_run_black_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_print_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_infowindow_adapter_dynamic.xml24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_picker.xml41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view_scale.xml60
-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_press_for_marker.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_print.xml45
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_runtime_style.xml19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_surfaceview_mediacontrols.xml31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml30
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/drawer_navigation_drawer.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_pulse_marker.xml21
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polygon.xml20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polyline.xml16
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml45
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/amsterdam.geojson2283
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/attrs.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/dimens.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/styles.xml19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-w820dp/dimens.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml56
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java16
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java61
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/constants/StyleVersionTest.java1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java21
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java44
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/StyleInitializerTest.java75
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java90
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java29
-rw-r--r--platform/android/bitrise.yml2
-rw-r--r--platform/android/build.gradle2
-rw-r--r--platform/android/platform.gyp17
-rw-r--r--platform/android/scripts/configure.sh7
-rwxr-xr-xplatform/android/scripts/debug.sh2
-rw-r--r--platform/android/scripts/generate-style-code.js206
-rw-r--r--platform/android/src/asset_file_source.cpp2
-rw-r--r--platform/android/src/async_task.cpp4
-rw-r--r--platform/android/src/conversion/constant.hpp95
-rw-r--r--platform/android/src/conversion/conversion.hpp50
-rw-r--r--platform/android/src/java_types.cpp37
-rw-r--r--platform/android/src/java_types.hpp35
-rwxr-xr-xplatform/android/src/jni.cpp273
-rw-r--r--platform/android/src/jni.hpp1
-rwxr-xr-xplatform/android/src/native_map_view.cpp33
-rwxr-xr-xplatform/android/src/native_map_view.hpp3
-rw-r--r--platform/android/src/run_loop.cpp70
-rw-r--r--platform/android/src/run_loop_impl.hpp13
-rw-r--r--platform/android/src/style/android_conversion.hpp91
-rw-r--r--platform/android/src/style/android_geojson.hpp48
-rw-r--r--platform/android/src/style/conversion/function.hpp52
-rw-r--r--platform/android/src/style/conversion/property_value.hpp38
-rw-r--r--platform/android/src/style/conversion/types.hpp98
-rw-r--r--platform/android/src/style/conversion/types.hpp.ejs40
-rw-r--r--platform/android/src/style/conversion/types_string_values.hpp209
-rw-r--r--platform/android/src/style/conversion/types_string_values.hpp.ejs48
-rw-r--r--platform/android/src/style/layers/background_layer.cpp67
-rw-r--r--platform/android/src/style/layers/background_layer.hpp39
-rw-r--r--platform/android/src/style/layers/circle_layer.cpp95
-rw-r--r--platform/android/src/style/layers/circle_layer.hpp47
-rw-r--r--platform/android/src/style/layers/custom_layer.cpp57
-rw-r--r--platform/android/src/style/layers/custom_layer.hpp32
-rw-r--r--platform/android/src/style/layers/fill_layer.cpp95
-rw-r--r--platform/android/src/style/layers/fill_layer.hpp47
-rw-r--r--platform/android/src/style/layers/layer.cpp171
-rw-r--r--platform/android/src/style/layers/layer.cpp.ejs69
-rw-r--r--platform/android/src/style/layers/layer.hpp80
-rw-r--r--platform/android/src/style/layers/layer.hpp.ejs45
-rw-r--r--platform/android/src/style/layers/layers.cpp64
-rw-r--r--platform/android/src/style/layers/layers.hpp20
-rw-r--r--platform/android/src/style/layers/line_layer.cpp144
-rw-r--r--platform/android/src/style/layers/line_layer.hpp61
-rw-r--r--platform/android/src/style/layers/raster_layer.cpp95
-rw-r--r--platform/android/src/style/layers/raster_layer.hpp47
-rw-r--r--platform/android/src/style/layers/symbol_layer.cpp382
-rw-r--r--platform/android/src/style/layers/symbol_layer.hpp129
-rw-r--r--platform/android/src/style/sources/sources.cpp28
-rw-r--r--platform/android/src/style/sources/sources.hpp14
-rw-r--r--platform/android/src/style/value.cpp67
-rw-r--r--platform/android/src/style/value.hpp35
-rw-r--r--platform/android/src/thread.cpp37
-rw-r--r--platform/android/src/timer.cpp5
-rw-r--r--platform/darwin/src/MGLFeature.h18
-rw-r--r--platform/darwin/src/MGLFeature.mm4
-rw-r--r--platform/darwin/src/MGLMultiPoint.mm4
-rw-r--r--platform/darwin/src/MGLPolygon.mm6
-rw-r--r--platform/darwin/src/MGLPolyline.mm6
-rw-r--r--platform/darwin/src/headless_view_cgl.cpp2
-rw-r--r--platform/darwin/src/log_nslog.mm3
-rw-r--r--platform/darwin/src/nsthread.mm13
-rw-r--r--platform/darwin/src/settings_nsuserdefaults.mm60
-rw-r--r--platform/darwin/test/MGLFeatureTests.mm19
-rw-r--r--platform/default/asset_file_source.cpp6
-rw-r--r--platform/default/glfw_view.cpp35
-rw-r--r--platform/default/headless_view_glx.cpp4
-rw-r--r--platform/default/http_file_source.cpp10
-rw-r--r--platform/default/image.cpp14
-rw-r--r--platform/default/jpeg_reader.cpp40
-rw-r--r--platform/default/log_stderr.cpp3
-rw-r--r--platform/default/mbgl/storage/offline.cpp20
-rw-r--r--platform/default/mbgl/storage/offline_database.cpp66
-rw-r--r--platform/default/mbgl/storage/offline_database.hpp7
-rw-r--r--platform/default/mbgl/storage/offline_download.cpp120
-rw-r--r--platform/default/mbgl/storage/offline_download.hpp2
-rw-r--r--platform/default/online_file_source.cpp8
-rw-r--r--platform/default/png_reader.cpp31
-rw-r--r--platform/default/run_loop.cpp6
-rw-r--r--platform/default/settings_json.cpp4
-rw-r--r--platform/default/sqlite3.hpp4
-rw-r--r--platform/default/string_stdlib.cpp19
-rw-r--r--platform/default/thread.cpp29
-rw-r--r--platform/ios/CHANGELOG.md58
-rw-r--r--platform/ios/DEVELOPING.md15
-rw-r--r--platform/ios/Mapbox.playground/Contents.swift115
-rw-r--r--platform/ios/app/MBXAppDelegate.h2
-rw-r--r--platform/ios/app/MBXViewController.m278
-rw-r--r--platform/ios/benchmark/MBXBenchAppDelegate.h2
-rw-r--r--platform/ios/docs/doc-README.md2
-rw-r--r--platform/ios/docs/pod-README.md2
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj34
-rw-r--r--platform/ios/scripts/configure.sh7
-rwxr-xr-xplatform/ios/scripts/package.sh19
-rw-r--r--platform/ios/src/MGLAnnotationImage.h24
-rw-r--r--platform/ios/src/MGLCalloutView.h36
-rw-r--r--platform/ios/src/MGLCompactCalloutView.h5
-rw-r--r--platform/ios/src/MGLMapView.h59
-rw-r--r--platform/ios/src/MGLMapView.mm28
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h8
-rw-r--r--platform/ios/src/MGLUserLocation.h18
-rw-r--r--platform/ios/src/MGLUserLocationAnnotationView.h2
-rw-r--r--platform/linux/scripts/configure.sh9
-rw-r--r--platform/macos/CHANGELOG.md28
-rw-r--r--platform/macos/DEVELOPING.md15
-rw-r--r--platform/macos/INSTALL.md18
-rw-r--r--platform/macos/app/AppDelegate.m18
-rw-r--r--platform/macos/app/Base.lproj/MainMenu.xib116
-rw-r--r--platform/macos/app/MapDocument.m42
-rw-r--r--platform/macos/app/wms.json21
-rw-r--r--platform/macos/docs/doc-README.md2
-rw-r--r--platform/macos/docs/pod-README.md2
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj12
-rw-r--r--platform/macos/macos.xcworkspace/xcshareddata/xcschemes/test.xcscheme6
-rw-r--r--platform/macos/scripts/configure.sh7
-rwxr-xr-xplatform/macos/scripts/deploy-packages.sh83
-rwxr-xr-xplatform/macos/scripts/package.sh2
-rw-r--r--platform/macos/src/MGLMapView.h36
-rw-r--r--platform/macos/src/MGLMapView.mm56
-rw-r--r--platform/node/CHANGELOG.md63
-rw-r--r--platform/node/README.md16
-rwxr-xr-xplatform/node/scripts/after_script.sh26
-rw-r--r--platform/node/src/node_conversion.hpp113
-rw-r--r--platform/node/src/node_feature.cpp7
-rw-r--r--platform/node/src/node_feature.hpp2
-rw-r--r--platform/node/src/node_geojson.hpp14
-rw-r--r--platform/node/src/node_log.cpp8
-rw-r--r--platform/node/src/node_map.cpp323
-rw-r--r--platform/node/src/node_map.hpp37
-rw-r--r--platform/node/src/node_mapbox_gl_native.cpp12
-rw-r--r--platform/node/src/node_request.cpp140
-rw-r--r--platform/node/src/node_request.hpp30
-rw-r--r--platform/node/test/memory.test.js108
-rw-r--r--platform/node/test/suite_implementation.js42
-rw-r--r--platform/qt/app/mapwindow.cpp38
-rw-r--r--platform/qt/app/qmapboxgl.gypi3
-rw-r--r--platform/qt/app/source.geojson253
-rw-r--r--platform/qt/app/source.qrc5
-rw-r--r--platform/qt/bitrise-qt4.yml2
-rw-r--r--platform/qt/bitrise-qt5.yml2
-rw-r--r--platform/qt/include/QQuickMapboxGLStyleProperty1
-rw-r--r--platform/qt/include/qmapbox.hpp10
-rw-r--r--platform/qt/include/qmapboxgl.hpp14
-rw-r--r--platform/qt/include/qquickmapboxgl.hpp22
-rw-r--r--platform/qt/include/qquickmapboxglstyleproperty.hpp75
-rw-r--r--platform/qt/platform.gyp28
-rw-r--r--platform/qt/qmlapp/main.cpp5
-rw-r--r--platform/qt/qmlapp/main.qml121
-rw-r--r--platform/qt/scripts/configure.sh24
-rw-r--r--platform/qt/src/async_task_impl.hpp6
-rw-r--r--platform/qt/src/http_file_source.hpp8
-rw-r--r--platform/qt/src/http_request.cpp5
-rw-r--r--platform/qt/src/http_request.hpp4
-rw-r--r--platform/qt/src/qmapboxgl.cpp199
-rw-r--r--platform/qt/src/qmapboxgl_p.hpp15
-rw-r--r--platform/qt/src/qquickmapboxgl.cpp28
-rw-r--r--platform/qt/src/qquickmapboxglrenderer.cpp26
-rw-r--r--platform/qt/src/qquickmapboxglrenderer.hpp18
-rw-r--r--platform/qt/src/qquickmapboxglstyleproperty.cpp118
-rw-r--r--platform/qt/src/qt_conversion.hpp106
-rw-r--r--platform/qt/src/qt_geojson.hpp49
-rw-r--r--platform/qt/src/run_loop_impl.hpp4
-rw-r--r--platform/qt/src/string_stdlib.cpp24
-rw-r--r--platform/qt/src/timer_impl.hpp6
-rwxr-xr-xscripts/build-shaders.py5
-rwxr-xr-xscripts/build-version.py2
-rwxr-xr-xscripts/clang-tidy.sh21
-rwxr-xr-xscripts/clang-tools.sh66
-rw-r--r--scripts/generate-style-code.js317
-rwxr-xr-xscripts/travis_setup.sh4
-rwxr-xr-xscripts/valgrind.sh16
-rw-r--r--scripts/valgrind.sup159
-rw-r--r--src/csscolorparser/csscolorparser.cpp6
-rw-r--r--src/mbgl/algorithm/generate_clip_ids.cpp1
-rw-r--r--src/mbgl/algorithm/update_renderables.hpp64
-rw-r--r--src/mbgl/annotation/annotation_manager.cpp94
-rw-r--r--src/mbgl/annotation/annotation_manager.hpp25
-rw-r--r--src/mbgl/annotation/annotation_source.cpp30
-rw-r--r--src/mbgl/annotation/annotation_source.hpp28
-rw-r--r--src/mbgl/annotation/annotation_tile.cpp39
-rw-r--r--src/mbgl/annotation/annotation_tile.hpp46
-rw-r--r--src/mbgl/annotation/fill_annotation_impl.cpp9
-rw-r--r--src/mbgl/annotation/fill_annotation_impl.hpp2
-rw-r--r--src/mbgl/annotation/line_annotation_impl.cpp8
-rw-r--r--src/mbgl/annotation/line_annotation_impl.hpp2
-rw-r--r--src/mbgl/annotation/shape_annotation_impl.cpp110
-rw-r--r--src/mbgl/annotation/shape_annotation_impl.hpp36
-rw-r--r--src/mbgl/annotation/style_sourced_annotation_impl.cpp13
-rw-r--r--src/mbgl/annotation/style_sourced_annotation_impl.hpp2
-rw-r--r--src/mbgl/annotation/symbol_annotation_impl.cpp5
-rw-r--r--src/mbgl/annotation/symbol_annotation_impl.hpp4
-rw-r--r--src/mbgl/geometry/buffer.hpp8
-rw-r--r--src/mbgl/geometry/circle_buffer.cpp4
-rw-r--r--src/mbgl/geometry/debug_font_buffer.cpp4
-rw-r--r--src/mbgl/geometry/elements_buffer.cpp4
-rw-r--r--src/mbgl/geometry/feature_index.cpp10
-rw-r--r--src/mbgl/geometry/feature_index.hpp10
-rw-r--r--src/mbgl/geometry/fill_buffer.cpp4
-rw-r--r--src/mbgl/geometry/glyph_atlas.cpp27
-rw-r--r--src/mbgl/geometry/glyph_atlas.hpp16
-rw-r--r--src/mbgl/geometry/icon_buffer.cpp3
-rw-r--r--src/mbgl/geometry/icon_buffer.hpp2
-rw-r--r--src/mbgl/geometry/line_atlas.cpp35
-rw-r--r--src/mbgl/geometry/line_atlas.hpp12
-rw-r--r--src/mbgl/geometry/line_buffer.cpp4
-rw-r--r--src/mbgl/geometry/static_vertex_buffer.cpp18
-rw-r--r--src/mbgl/geometry/static_vertex_buffer.hpp17
-rw-r--r--src/mbgl/geometry/text_buffer.cpp3
-rw-r--r--src/mbgl/geometry/text_buffer.hpp2
-rw-r--r--src/mbgl/geometry/vao.cpp5
-rw-r--r--src/mbgl/geometry/vao.hpp14
-rw-r--r--src/mbgl/gl/gl.cpp4
-rw-r--r--src/mbgl/gl/gl_config.cpp4
-rw-r--r--src/mbgl/gl/gl_config.hpp18
-rw-r--r--src/mbgl/gl/object_store.cpp23
-rw-r--r--src/mbgl/gl/object_store.hpp36
-rw-r--r--src/mbgl/gl/texture_pool.cpp75
-rw-r--r--src/mbgl/gl/texture_pool.hpp38
-rw-r--r--src/mbgl/map/camera.cpp1
-rw-r--r--src/mbgl/map/map.cpp56
-rw-r--r--src/mbgl/map/transform.cpp74
-rw-r--r--src/mbgl/map/transform.hpp6
-rw-r--r--src/mbgl/map/transform_state.cpp10
-rw-r--r--src/mbgl/map/transform_state.hpp7
-rw-r--r--src/mbgl/platform/event.cpp34
-rw-r--r--src/mbgl/platform/log.cpp11
-rw-r--r--src/mbgl/renderer/bucket.hpp23
-rw-r--r--src/mbgl/renderer/circle_bucket.cpp10
-rw-r--r--src/mbgl/renderer/circle_bucket.hpp6
-rw-r--r--src/mbgl/renderer/debug_bucket.cpp4
-rw-r--r--src/mbgl/renderer/debug_bucket.hpp4
-rw-r--r--src/mbgl/renderer/fill_bucket.cpp37
-rw-r--r--src/mbgl/renderer/fill_bucket.hpp18
-rw-r--r--src/mbgl/renderer/frame_history.cpp22
-rw-r--r--src/mbgl/renderer/frame_history.hpp8
-rw-r--r--src/mbgl/renderer/line_bucket.cpp22
-rw-r--r--src/mbgl/renderer/line_bucket.hpp14
-rw-r--r--src/mbgl/renderer/paint_parameters.hpp12
-rw-r--r--src/mbgl/renderer/painter.cpp151
-rw-r--r--src/mbgl/renderer/painter.hpp133
-rw-r--r--src/mbgl/renderer/painter_background.cpp103
-rw-r--r--src/mbgl/renderer/painter_circle.cpp58
-rw-r--r--src/mbgl/renderer/painter_clipping.cpp18
-rw-r--r--src/mbgl/renderer/painter_debug.cpp59
-rw-r--r--src/mbgl/renderer/painter_fill.cpp240
-rw-r--r--src/mbgl/renderer/painter_line.cpp173
-rw-r--r--src/mbgl/renderer/painter_raster.cpp44
-rw-r--r--src/mbgl/renderer/painter_symbol.cpp154
-rw-r--r--src/mbgl/renderer/raster_bucket.cpp27
-rw-r--r--src/mbgl/renderer/raster_bucket.hpp11
-rw-r--r--src/mbgl/renderer/render_item.hpp10
-rw-r--r--src/mbgl/renderer/render_pass.hpp10
-rw-r--r--src/mbgl/renderer/render_tile.cpp34
-rw-r--r--src/mbgl/renderer/render_tile.hpp34
-rw-r--r--src/mbgl/renderer/symbol_bucket.cpp61
-rw-r--r--src/mbgl/renderer/symbol_bucket.hpp20
-rw-r--r--src/mbgl/shader/circle_shader.cpp13
-rw-r--r--src/mbgl/shader/circle_shader.hpp6
-rw-r--r--src/mbgl/shader/collision_box_shader.cpp14
-rw-r--r--src/mbgl/shader/collision_box_shader.hpp4
-rw-r--r--src/mbgl/shader/icon_shader.cpp16
-rw-r--r--src/mbgl/shader/icon_shader.hpp9
-rw-r--r--src/mbgl/shader/line_shader.cpp14
-rw-r--r--src/mbgl/shader/line_shader.hpp8
-rw-r--r--src/mbgl/shader/linepattern_shader.cpp14
-rw-r--r--src/mbgl/shader/linepattern_shader.hpp6
-rw-r--r--src/mbgl/shader/linesdf_shader.cpp14
-rw-r--r--src/mbgl/shader/linesdf_shader.hpp8
-rw-r--r--src/mbgl/shader/outline_shader.cpp13
-rw-r--r--src/mbgl/shader/outline_shader.hpp5
-rw-r--r--src/mbgl/shader/outlinepattern_shader.cpp17
-rw-r--r--src/mbgl/shader/outlinepattern_shader.hpp31
-rw-r--r--src/mbgl/shader/pattern_shader.cpp17
-rw-r--r--src/mbgl/shader/pattern_shader.hpp29
-rw-r--r--src/mbgl/shader/plain_shader.cpp13
-rw-r--r--src/mbgl/shader/plain_shader.hpp5
-rw-r--r--src/mbgl/shader/raster_shader.cpp20
-rw-r--r--src/mbgl/shader/raster_shader.hpp12
-rw-r--r--src/mbgl/shader/sdf_shader.cpp32
-rw-r--r--src/mbgl/shader/sdf_shader.hpp26
-rw-r--r--src/mbgl/shader/shader.cpp61
-rw-r--r--src/mbgl/shader/shader.hpp26
-rw-r--r--src/mbgl/shader/shaders.hpp59
-rw-r--r--src/mbgl/shader/uniform.cpp7
-rw-r--r--src/mbgl/sprite/sprite_atlas.cpp28
-rw-r--r--src/mbgl/sprite/sprite_atlas.hpp38
-rw-r--r--src/mbgl/sprite/sprite_parser.cpp6
-rw-r--r--src/mbgl/storage/network_status.cpp2
-rw-r--r--src/mbgl/storage/resource.cpp40
-rw-r--r--src/mbgl/storage/response.cpp6
-rw-r--r--src/mbgl/style/bucket_parameters.cpp7
-rw-r--r--src/mbgl/style/bucket_parameters.hpp6
-rw-r--r--src/mbgl/style/calculation_parameters.hpp12
-rw-r--r--src/mbgl/style/cascade_parameters.hpp2
-rw-r--r--src/mbgl/style/layer.cpp8
-rw-r--r--src/mbgl/style/layer_impl.cpp14
-rw-r--r--src/mbgl/style/layer_impl.hpp13
-rw-r--r--src/mbgl/style/layers/background_layer.cpp20
-rw-r--r--src/mbgl/style/layers/background_layer_impl.cpp4
-rw-r--r--src/mbgl/style/layers/background_layer_impl.hpp4
-rw-r--r--src/mbgl/style/layers/background_layer_properties.cpp6
-rw-r--r--src/mbgl/style/layers/background_layer_properties.hpp5
-rw-r--r--src/mbgl/style/layers/circle_layer.cpp52
-rw-r--r--src/mbgl/style/layers/circle_layer_impl.cpp6
-rw-r--r--src/mbgl/style/layers/circle_layer_impl.hpp4
-rw-r--r--src/mbgl/style/layers/circle_layer_properties.cpp11
-rw-r--r--src/mbgl/style/layers/circle_layer_properties.hpp6
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.cpp5
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.hpp4
-rw-r--r--src/mbgl/style/layers/fill_layer.cpp48
-rw-r--r--src/mbgl/style/layers/fill_layer_impl.cpp6
-rw-r--r--src/mbgl/style/layers/fill_layer_impl.hpp4
-rw-r--r--src/mbgl/style/layers/fill_layer_properties.cpp10
-rw-r--r--src/mbgl/style/layers/fill_layer_properties.hpp7
-rw-r--r--src/mbgl/style/layers/layer.cpp.ejs100
-rw-r--r--src/mbgl/style/layers/layer_properties.cpp.ejs38
-rw-r--r--src/mbgl/style/layers/layer_properties.hpp.ejs54
-rw-r--r--src/mbgl/style/layers/line_layer.cpp60
-rw-r--r--src/mbgl/style/layers/line_layer_impl.cpp10
-rw-r--r--src/mbgl/style/layers/line_layer_impl.hpp4
-rw-r--r--src/mbgl/style/layers/line_layer_properties.cpp20
-rw-r--r--src/mbgl/style/layers/line_layer_properties.hpp6
-rw-r--r--src/mbgl/style/layers/raster_layer.cpp44
-rw-r--r--src/mbgl/style/layers/raster_layer_impl.cpp4
-rw-r--r--src/mbgl/style/layers/raster_layer_impl.hpp4
-rw-r--r--src/mbgl/style/layers/raster_layer_properties.cpp10
-rw-r--r--src/mbgl/style/layers/raster_layer_properties.hpp3
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp105
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.cpp21
-rw-r--r--src/mbgl/style/layers/symbol_layer_impl.hpp4
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.cpp54
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.hpp19
-rw-r--r--src/mbgl/style/layout_property.hpp7
-rw-r--r--src/mbgl/style/observer.hpp6
-rw-r--r--src/mbgl/style/paint_property.hpp65
-rw-r--r--src/mbgl/style/parser.cpp523
-rw-r--r--src/mbgl/style/parser.hpp9
-rw-r--r--src/mbgl/style/property_evaluator.cpp48
-rw-r--r--src/mbgl/style/property_evaluator.hpp10
-rw-r--r--src/mbgl/style/property_parsing.cpp263
-rw-r--r--src/mbgl/style/property_parsing.hpp98
-rw-r--r--src/mbgl/style/rapidjson_conversion.hpp94
-rw-r--r--src/mbgl/style/source.cpp420
-rw-r--r--src/mbgl/style/source.hpp112
-rw-r--r--src/mbgl/style/source_impl.cpp287
-rw-r--r--src/mbgl/style/source_impl.hpp101
-rw-r--r--src/mbgl/style/source_observer.hpp2
-rw-r--r--src/mbgl/style/sources/geojson_source.cpp26
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.cpp142
-rw-r--r--src/mbgl/style/sources/geojson_source_impl.hpp40
-rw-r--r--src/mbgl/style/sources/raster_source.cpp12
-rw-r--r--src/mbgl/style/sources/raster_source_impl.cpp19
-rw-r--r--src/mbgl/style/sources/raster_source_impl.hpp18
-rw-r--r--src/mbgl/style/sources/vector_source.cpp12
-rw-r--r--src/mbgl/style/sources/vector_source_impl.cpp17
-rw-r--r--src/mbgl/style/sources/vector_source_impl.hpp18
-rw-r--r--src/mbgl/style/style.cpp93
-rw-r--r--src/mbgl/style/style.hpp9
-rw-r--r--src/mbgl/style/tile_source_impl.cpp118
-rw-r--r--src/mbgl/style/tile_source_impl.hpp48
-rw-r--r--src/mbgl/style/types.cpp92
-rw-r--r--src/mbgl/style/update_parameters.hpp6
-rw-r--r--src/mbgl/text/check_max_angle.hpp2
-rw-r--r--src/mbgl/text/collision_feature.cpp4
-rw-r--r--src/mbgl/text/collision_feature.hpp12
-rw-r--r--src/mbgl/text/collision_tile.cpp2
-rw-r--r--src/mbgl/text/get_anchors.cpp10
-rw-r--r--src/mbgl/text/get_anchors.hpp2
-rw-r--r--src/mbgl/text/glyph.hpp13
-rw-r--r--src/mbgl/text/glyph_pbf.cpp2
-rw-r--r--src/mbgl/text/glyph_pbf.hpp4
-rw-r--r--src/mbgl/text/glyph_set.cpp6
-rw-r--r--src/mbgl/text/glyph_set.hpp2
-rw-r--r--src/mbgl/text/placement_config.hpp6
-rw-r--r--src/mbgl/text/quads.cpp68
-rw-r--r--src/mbgl/text/quads.hpp28
-rw-r--r--src/mbgl/text/shaping.hpp6
-rw-r--r--src/mbgl/tile/geojson_tile.cpp197
-rw-r--r--src/mbgl/tile/geojson_tile.hpp73
-rw-r--r--src/mbgl/tile/geometry_tile.cpp342
-rw-r--r--src/mbgl/tile/geometry_tile.hpp139
-rw-r--r--src/mbgl/tile/geometry_tile_data.cpp217
-rw-r--r--src/mbgl/tile/geometry_tile_data.hpp143
-rw-r--r--src/mbgl/tile/raster_tile.cpp71
-rw-r--r--src/mbgl/tile/raster_tile.hpp46
-rw-r--r--src/mbgl/tile/raster_tile_data.cpp71
-rw-r--r--src/mbgl/tile/raster_tile_data.hpp39
-rw-r--r--src/mbgl/tile/tile.cpp35
-rw-r--r--src/mbgl/tile/tile.hpp111
-rw-r--r--src/mbgl/tile/tile_cache.cpp30
-rw-r--r--src/mbgl/tile/tile_cache.hpp8
-rw-r--r--src/mbgl/tile/tile_data.cpp25
-rw-r--r--src/mbgl/tile/tile_data.hpp87
-rw-r--r--src/mbgl/tile/tile_id.hpp23
-rw-r--r--src/mbgl/tile/tile_loader.hpp62
-rw-r--r--src/mbgl/tile/tile_loader_impl.hpp113
-rw-r--r--src/mbgl/tile/tile_observer.hpp18
-rw-r--r--src/mbgl/tile/tile_worker.cpp37
-rw-r--r--src/mbgl/tile/tile_worker.hpp20
-rw-r--r--src/mbgl/tile/vector_tile.cpp122
-rw-r--r--src/mbgl/tile/vector_tile.hpp83
-rw-r--r--src/mbgl/tile/vector_tile_data.cpp215
-rw-r--r--src/mbgl/tile/vector_tile_data.hpp75
-rw-r--r--src/mbgl/util/atomic.hpp45
-rw-r--r--src/mbgl/util/clip_id.hpp10
-rw-r--r--src/mbgl/util/color.cpp20
-rw-r--r--src/mbgl/util/convert.cpp9
-rw-r--r--src/mbgl/util/dtoa.cpp10
-rw-r--r--src/mbgl/util/dtoa.hpp9
-rw-r--r--src/mbgl/util/exclusive.hpp10
-rw-r--r--src/mbgl/util/geo.cpp4
-rw-r--r--src/mbgl/util/grid_index.cpp21
-rw-r--r--src/mbgl/util/grid_index.hpp2
-rw-r--r--src/mbgl/util/interpolate.hpp14
-rw-r--r--src/mbgl/util/intersection_tests.cpp4
-rw-r--r--src/mbgl/util/intersection_tests.hpp4
-rw-r--r--src/mbgl/util/io.hpp2
-rw-r--r--src/mbgl/util/mat2.cpp6
-rw-r--r--src/mbgl/util/mat3.cpp6
-rw-r--r--src/mbgl/util/math.cpp6
-rw-r--r--src/mbgl/util/math.hpp26
-rw-r--r--src/mbgl/util/ptr.hpp6
-rw-r--r--src/mbgl/util/raster.cpp88
-rw-r--r--src/mbgl/util/raster.hpp53
-rw-r--r--src/mbgl/util/rect.hpp10
-rw-r--r--src/mbgl/util/stopwatch.cpp15
-rw-r--r--src/mbgl/util/stopwatch.hpp16
-rw-r--r--src/mbgl/util/thread.hpp9
-rw-r--r--src/mbgl/util/thread_context.cpp5
-rw-r--r--src/mbgl/util/thread_context.hpp2
-rw-r--r--src/mbgl/util/thread_local.hpp10
-rw-r--r--src/mbgl/util/tile_coordinate.cpp1
-rw-r--r--src/mbgl/util/tileset.hpp24
-rw-r--r--src/mbgl/util/utf.hpp2
-rw-r--r--src/mbgl/util/version_info.cpp2
-rw-r--r--src/mbgl/util/work_task.cpp0
-rw-r--r--src/mbgl/util/worker.cpp10
-rw-r--r--src/mbgl/util/worker.hpp2
-rw-r--r--test/algorithm/mock.hpp16
-rw-r--r--test/algorithm/update_renderables.cpp1079
-rw-r--r--test/api/annotations.cpp47
-rw-r--r--test/api/api_misuse.cpp2
-rw-r--r--test/api/custom_layer.cpp2
-rw-r--r--test/api/render_missing.cpp2
-rw-r--r--test/api/repeated_render.cpp8
-rw-r--r--test/api/set_style.cpp2
-rw-r--r--test/fixtures/annotations/fill_annotation_max_zoom/expected.pngbin0 -> 1569 bytes
-rw-r--r--test/fixtures/annotations/line_annotation_max_zoom/expected.pngbin0 -> 2893 bytes
-rw-r--r--test/fixtures/annotations/update_icon/expected.pngbin2949 -> 2545 bytes
-rw-r--r--test/fixtures/annotations/update_point/expected.pngbin2504 -> 2554 bytes
-rw-r--r--test/fixtures/resources/glyphs.pbfbin74722 -> 75351 bytes
-rw-r--r--test/fixtures/resources/raster.pngbin12146 -> 0 bytes
-rw-r--r--test/fixtures/resources/raster.tilebin0 -> 95464 bytes
-rw-r--r--test/fixtures/resources/source_raster.json2
-rw-r--r--test/fixtures/resources/source_vector.json2
-rw-r--r--test/fixtures/resources/sprite.json2
-rw-r--r--test/fixtures/resources/sprite.pngbin39041 -> 92278 bytes
-rw-r--r--test/fixtures/resources/style.json40
-rw-r--r--test/fixtures/resources/style_raster.json1
-rw-r--r--test/fixtures/resources/style_vector.json1
-rw-r--r--test/fixtures/resources/vector.pbfbin103021 -> 0 bytes
-rw-r--r--test/fixtures/resources/vector.tilebin0 -> 51856 bytes
-rw-r--r--test/fixtures/style_parser/circle-blur.info.json2
-rw-r--r--test/fixtures/style_parser/circle-blur.style.json2
-rw-r--r--test/fixtures/style_parser/circle-color.info.json2
-rw-r--r--test/fixtures/style_parser/circle-color.style.json2
-rw-r--r--test/fixtures/style_parser/circle-opacity.info.json2
-rw-r--r--test/fixtures/style_parser/circle-opacity.style.json2
-rw-r--r--test/fixtures/style_parser/circle-radius.info.json2
-rw-r--r--test/fixtures/style_parser/circle-radius.style.json2
-rw-r--r--test/fixtures/style_parser/function-numeric.info.json2
-rw-r--r--test/fixtures/style_parser/function-type.info.json2
-rw-r--r--test/fixtures/style_parser/geojson-data-inline.style.json2
-rw-r--r--test/fixtures/style_parser/geojson-invalid-data.info.json2
-rw-r--r--test/fixtures/style_parser/geojson-missing-data.info.json2
-rw-r--r--test/fixtures/style_parser/line-opacity.info.json2
-rw-r--r--test/fixtures/style_parser/line-opacity.style.json2
-rw-r--r--test/fixtures/style_parser/line-translate.info.json2
-rw-r--r--test/fixtures/style_parser/line-translate.style.json2
-rw-r--r--test/fixtures/style_parser/line-width.info.json2
-rw-r--r--test/fixtures/style_parser/line-width.style.json2
-rw-r--r--test/fixtures/style_parser/non-object.info.json7
-rw-r--r--test/fixtures/style_parser/non-object.style.json1
-rw-r--r--test/fixtures/style_parser/stop-zoom-value.info.json2
-rw-r--r--test/fixtures/style_parser/stops-array.info.json2
-rw-r--r--test/fixtures/style_parser/text-size.info.json2
-rw-r--r--test/fixtures/style_parser/text-size.style.json2
-rw-r--r--test/fixtures/style_parser/version-not-number.info.json7
-rw-r--r--test/fixtures/style_parser/version-not-number.style.json1
-rw-r--r--test/gl/object.cpp100
-rw-r--r--test/include/mbgl/test.hpp2
-rw-r--r--test/map/map.cpp5
-rw-r--r--test/sprite/sprite_atlas.cpp12
-rw-r--r--test/sprite/sprite_image.cpp8
-rw-r--r--test/sprite/sprite_parser.cpp20
-rw-r--r--test/sprite/sprite_store.cpp13
-rw-r--r--test/src/mbgl/test/fixture_log_observer.cpp8
-rw-r--r--test/src/mbgl/test/fixture_log_observer.hpp6
-rw-r--r--test/src/mbgl/test/stub_file_source.cpp17
-rw-r--r--test/src/mbgl/test/stub_file_source.hpp3
-rw-r--r--test/src/mbgl/test/stub_style_observer.hpp6
-rw-r--r--test/src/mbgl/test/test.cpp2
-rw-r--r--test/src/mbgl/test/util.hpp4
-rw-r--r--test/storage/asset_file_source.cpp2
-rw-r--r--test/storage/headers.cpp18
-rw-r--r--test/storage/offline.cpp24
-rw-r--r--test/storage/offline_database.cpp62
-rw-r--r--test/storage/offline_download.cpp50
-rw-r--r--test/storage/resource.cpp56
-rw-r--r--test/style/filter.cpp172
-rw-r--r--test/style/source.cpp138
-rw-r--r--test/style/style.cpp10
-rw-r--r--test/style/style_layer.cpp209
-rw-r--r--test/style/style_parser.cpp44
-rw-r--r--test/style/tile_source.cpp37
-rw-r--r--test/test.gypi12
-rw-r--r--test/text/quads.cpp265
-rw-r--r--test/tile/geometry_tile_data.cpp (renamed from test/tile/geometry_tile.cpp)22
-rw-r--r--test/tile/tile_id.cpp70
-rw-r--r--test/util/async_task.cpp8
-rw-r--r--test/util/image.cpp12
-rw-r--r--test/util/mapbox.cpp1
-rw-r--r--test/util/memory.cpp228
-rw-r--r--test/util/projection.cpp68
-rw-r--r--test/util/text_conversions.cpp9
-rw-r--r--test/util/thread_local.cpp6
813 files changed, 31101 insertions, 9317 deletions
diff --git a/.clang-tidy b/.clang-tidy
index b32f768630..4358e3bb3f 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1 +1,2 @@
-Checks: '-clang-analyzer-security.insecureAPI.rand'
+Checks: 'modernize-*,misc-static-assert,llvm-namespace-comment,-clang-analyzer-security.insecureAPI.rand,-clang-analyzer-core.uninitialized.UndefReturn,-clang-analyzer-core.StackAddressEscape,-clang-analyzer-core.CallAndMessage'
+HeaderFilterRegex: '\/mbgl\/'
diff --git a/.mason b/.mason
-Subproject ec5ff7cc35d1dec325a00f4d8feff7fa3f6da2a
+Subproject fc590c762474eae5bb78c3c825247face0aed9a
diff --git a/.mention-bot b/.mention-bot
new file mode 100644
index 0000000000..bea6176a98
--- /dev/null
+++ b/.mention-bot
@@ -0,0 +1,5 @@
+{
+ "requiredOrgs": ["mapbox"],
+ "message": "@pullRequester, thanks for your PR! By analyzing this pull request, we identified @reviewers to be potential reviewers.",
+ "numFilesToCheck": 10
+}
diff --git a/.travis.yml b/.travis.yml
index 5ae5baac60..211a6f9f81 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,13 +18,13 @@ addons_shortcuts:
addons_qt4: &qt4
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
- packages: [ 'gdb', 'g++-5', 'gcc-5',
+ packages: [ 'g++-5', 'gcc-5', 'libjemalloc-dev',
'mesa-utils', 'qt4-default' ]
addons_qt5: &qt5
apt:
sources: [ 'ubuntu-toolchain-r-test' ]
- packages: [ 'gdb', 'g++-5', 'gcc-5',
- 'mesa-utils', 'qt5-default', 'libqt5opengl5-dev', 'qtdeclarative5-dev', 'qtpositioning5-dev', 'qtlocation5-dev' ]
+ packages: [ 'g++-5', 'gcc-5',
+ 'mesa-utils', 'libc6-dbg', 'qt5-default', 'libqt5opengl5-dev', 'qtdeclarative5-dev', 'qtpositioning5-dev', 'qtlocation5-dev' ]
env:
global:
@@ -50,19 +50,23 @@ after_script:
matrix:
include:
- # Clang 3.5 - Release - Node
+ # Clang 3.5 - Debug - Node
- os: linux
sudo: required
dist: trusty
language: node
- compiler: "node4-clang35-release"
- env: BUILDTYPE=Release _CXX=clang++-3.5 _CC=clang-3.5
+ compiler: "node4-clang35-debug"
+ env: _CXX=clang++-3.5 _CC=clang-3.5
addons: *clang35
+ before_script:
+ - export PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)")
+ - export PUBLISH=$([[ ${TAG} == "node-v${PACKAGE_JSON_VERSION}" ]] && echo 1 || echo 0)
+ - export BUILDTYPE=$([[ ${PUBLISH} ]] && echo "Release" || echo "Debug")
script:
- nvm install 4
- nvm use 4
- make node
- - make test-node
+ - if [[ ! ${PUBLISH} ]]; then make test-node; fi
after_script:
- ccache --show-stats
- ./platform/node/scripts/after_script.sh ${TRAVIS_JOB_NUMBER} ${TRAVIS_TAG:-}
@@ -104,15 +108,28 @@ matrix:
env: BUILDTYPE=Release _CXX=clang++-3.5 _CC=clang-3.5
addons: *clang35
+ # Clang 3.5 - tidy
+ - os: linux
+ sudo: required
+ dist: trusty
+ language: cpp
+ compiler: "tidy-clang35-release"
+ env: BUILDTYPE=Release _CXX=clang++-3.5 _CC=clang-3.5
+ addons: *clang35
+ script:
+ - make tidy
+
# Qt 4 - Release
- os: linux
- sudo: false
+ sudo: required
+ dist: trusty
language: cpp
compiler: "qt4-gcc5-release"
env: BUILDTYPE=Release _CXX=g++-5 _CC=gcc-5
addons: *qt4
script:
- - make qt-app test-qt
+ - make qt-app
+ - LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so make test-qt-Memory.*:*.Load
# Qt 5 - Release
- os: linux
@@ -123,7 +140,9 @@ matrix:
env: BUILDTYPE=Release _CXX=g++-5 _CC=gcc-5
addons: *qt5
script:
- - make qt-app qt-qml-app test-qt
+ - make qt-app
+ - make qt-qml-app
+ - make test-valgrind-qt--*.Load
cache:
directories:
diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py
index f9b40af237..7a02a5e14d 100644
--- a/.ycm_extra_conf.py
+++ b/.ycm_extra_conf.py
@@ -33,11 +33,12 @@
import os
import subprocess
+import string
import ycm_core
compilation_database_folders = [
- 'build/linux-x86_64/Release',
- 'build/macos/Release',
+ 'build/linux-x86_64/Debug',
+ 'build/macos/Debug',
]
subprocess.call(['make compdb'], shell=True)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3bb6186890..ab1da40592 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -16,6 +16,8 @@ If you want to contribute code:
1. Prefix your commit messages with the platform(s) your changes affect: `[core]`, `[android]`, `[ios]`, `[macos]`, `[node]`, or `[qt]`.
+Please note the special instructions for contributing new source code files, asset files, or user-facing strings to the [iOS SDK](platform/ios/DEVELOPING.md#contributing) or [macOS SDK](platform/macos/DEVELOPING.md#contributing).
+
# Code of conduct
Everyone is invited to participate in Mapbox’s open source projects and public discussions: we want to create a welcoming and friendly environment. Harassment of participants or other unethical and unprofessional behavior will not be tolerated in our spaces. The [Contributor Covenant](http://contributor-covenant.org) applies to all projects under the Mapbox organization and we ask that you please read [the full text](http://contributor-covenant.org/version/1/2/0/).
diff --git a/INSTALL.md b/INSTALL.md
index 66cfe49f44..0c12f6866e 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -39,7 +39,7 @@ dependencies:
### Additional Dependencies for macOS
- - Apple Command Line Tools (available at [Apple Developer](https://developer.apple.com/downloads))
+ - Apple Command Line Tools (available at [Apple Developer](https://developer.apple.com/download/more/))
- [Homebrew](http://brew.sh)
- [Cask](http://caskroom.io/) (if building for Android)
- [xcpretty](https://github.com/supermarin/xcpretty) (`gem install xcpretty`)
diff --git a/Makefile b/Makefile
index d646943c87..fff56794d7 100644
--- a/Makefile
+++ b/Makefile
@@ -2,21 +2,35 @@ export BUILDTYPE ?= Debug
ifeq ($(shell uname -s), Darwin)
HOST_PLATFORM = macos
+ HOST_PLATFORM_VERSION = $(shell uname -m)
export JOBS ?= $(shell sysctl -n hw.ncpu)
else ifeq ($(shell uname -s), Linux)
HOST_PLATFORM = linux
+ HOST_PLATFORM_VERSION = $(shell uname -m)
export JOBS ?= $(shell grep --count processor /proc/cpuinfo)
else
$(error Cannot determine host platform)
endif
+ifeq ($(MASON_PLATFORM),)
+ BUILD_PLATFORM = $(HOST_PLATFORM)
+else
+ BUILD_PLATFORM = $(MASON_PLATFORM)
+endif
+
+ifeq ($(MASON_PLATFORM_VERSION),)
+ BUILD_PLATFORM_VERSION = $(HOST_PLATFORM_VERSION)
+else
+ BUILD_PLATFORM_VERSION = $(MASON_PLATFORM_VERSION)
+endif
+
ifeq ($(V), 1)
export XCPRETTY
else
export XCPRETTY ?= | xcpretty
endif
-default: test-$(HOST_PLATFORM)
+default: test-$(BUILD_PLATFORM)
ifneq (,$(wildcard .git/.))
.mason/mason:
@@ -25,6 +39,7 @@ else
.mason/mason: ;
endif
+.NOTPARALLEL: node_modules
node_modules: package.json
npm update # Install dependencies but don't run our own install script.
@@ -152,6 +167,9 @@ idocument:
ANDROID_ENV = platform/android/scripts/toolchain.sh
ANDROID_ABIS = arm-v5 arm-v7 arm-v8 x86 x86-64 mips
+style-code-android:
+ node platform/android/scripts/generate-style-code.js
+
define ANDROID_RULES
build/android-$1/config.gypi: platform/android/scripts/configure.sh $(CONFIG_DEPENDENCIES)
$$(shell $(ANDROID_ENV) $1) ./configure $$< $$@ android $1
@@ -163,7 +181,7 @@ build/android-$1/Makefile: platform/android/platform.gyp build/android-$1/config
android-lib-$1: build/android-$1/Makefile
$$(shell $(ANDROID_ENV) $1) $(MAKE) -j$(JOBS) -C build/android-$1 all
-android-$1: android-lib-$1
+android-$1: android-lib-$1 style-code-android
cd platform/android && ./gradlew --parallel --max-workers=$(JOBS) assemble$(BUILDTYPE)
apackage: android-lib-$1
@@ -182,13 +200,17 @@ apackage:
#### Node targets #####################################################
NODE_PRE_GYP = $(shell npm bin)/node-pre-gyp
-NODE_OUTPUT_PATH = build/node-$(HOST_PLATFORM)-$(shell uname -m)
+NODE_OUTPUT_PATH = build/node-$(BUILD_PLATFORM)-$(BUILD_PLATFORM_VERSION)
-$(NODE_OUTPUT_PATH)/config.gypi: platform/$(HOST_PLATFORM)/scripts/configure.sh $(CONFIG_DEPENDENCIES)
- ./configure $< $@ $(HOST_PLATFORM) $(shell uname -m)
+ifeq ($(BUILDTYPE), Debug)
+ NODE_DEBUG = "--debug"
+endif
+
+$(NODE_OUTPUT_PATH)/config.gypi: platform/$(BUILD_PLATFORM)/scripts/configure.sh $(CONFIG_DEPENDENCIES)
+ ./configure $< $@ $(BUILD_PLATFORM) $(BUILD_PLATFORM_VERSION)
node: $(NODE_OUTPUT_PATH)/config.gypi node_modules $(GYP_DEPENDENCIES)
- $(NODE_PRE_GYP) configure --clang -- -I$< \
+ $(NODE_PRE_GYP) configure --clang $(NODE_DEBUG) -- -I$< \
-Dcoverage= -Dlibuv_cflags= -Dlibuv_ldflags= -Dlibuv_static_libs=
$(NODE_PRE_GYP) build --clang
@@ -203,21 +225,31 @@ xnode: $(NODE_OUTPUT_PATH)/config.gypi $(GYP_DEPENDENCIES)
.PHONY: test-node
test-node: node
npm test
+ npm run test-memory
npm run test-suite
#### Qt targets #####################################################
-QT_OUTPUT_PATH = build/qt-$(HOST_PLATFORM)-$(shell uname -m)
+QT_OUTPUT_PATH = build/qt-$(BUILD_PLATFORM)-$(BUILD_PLATFORM_VERSION)
QT_MAKEFILE = $(QT_OUTPUT_PATH)/Makefile
# Cross compilation support
-QT_ENV = $(shell MASON_PLATFORM_VERSION=$(shell uname -m) ./platform/qt/scripts/toolchain.sh)
+QT_ENV = $(shell MASON_PLATFORM_VERSION=$(BUILD_PLATFORM_VERSION) ./platform/qt/scripts/toolchain.sh)
$(QT_OUTPUT_PATH)/config.gypi: platform/qt/scripts/configure.sh $(CONFIG_DEPENDENCIES)
- $(QT_ENV) ./configure $< $@ $(HOST_PLATFORM) $(shell uname -m)
+ $(QT_ENV) ./configure $< $@ $(BUILD_PLATFORM) $(BUILD_PLATFORM_VERSION)
+
+GYP_FLAVOR = make
+ifneq ($(HOST_PLATFORM),$(BUILD_PLATFORM))
+ ifeq ($(BUILD_PLATFORM), linux)
+ GYP_FLAVOR = make-linux
+ else ifeq ($(BUILD_PLATFORM), macos)
+ GYP_FLAVOR = make-mac
+ endif
+endif
$(QT_MAKEFILE): platform/qt/platform.gyp $(QT_OUTPUT_PATH)/config.gypi $(GYP_DEPENDENCIES)
- $(QT_ENV) $(GYP) -f make -I $(QT_OUTPUT_PATH)/config.gypi \
+ $(QT_ENV) $(GYP) -f $(GYP_FLAVOR) -I $(QT_OUTPUT_PATH)/config.gypi \
--generator-output=$(QT_OUTPUT_PATH) $<
qt-lib: $(QT_MAKEFILE)
@@ -229,9 +261,11 @@ qt-app: $(QT_MAKEFILE)
qt-qml-app: $(QT_MAKEFILE)
$(QT_ENV) $(MAKE) -j$(JOBS) -C $(QT_OUTPUT_PATH) qt-qml-app
-test-qt: $(QT_MAKEFILE) node_modules
+test-qt-%: $(QT_MAKEFILE) node_modules
$(QT_ENV) $(MAKE) -j$(JOBS) -C $(QT_OUTPUT_PATH) test
- $(GDB) $(QT_OUTPUT_PATH)/$(BUILDTYPE)/test --gtest_catch_exceptions=0 --gtest_filter=*
+ $(GDB) $(QT_OUTPUT_PATH)/$(BUILDTYPE)/test --gtest_catch_exceptions=0 --gtest_filter=$*
+
+test-qt: test-qt-*
run-qt-app: qt-app
cd $(QT_OUTPUT_PATH)/$(BUILDTYPE) && ./qmapboxgl
@@ -239,13 +273,24 @@ run-qt-app: qt-app
run-qt-qml-app: qt-qml-app
cd $(QT_OUTPUT_PATH)/$(BUILDTYPE) && ./qquickmapboxgl
+test-valgrind-qt-%: $(QT_MAKEFILE) node_modules
+ $(QT_ENV) $(MAKE) -j$(JOBS) -C $(QT_OUTPUT_PATH) test
+ .mason/mason install valgrind latest
+ ./scripts/valgrind.sh $(QT_OUTPUT_PATH)/$(BUILDTYPE)/test --gtest_catch_exceptions=0 --gtest_filter=$*
+
+test-valgrind-qt: test-valgrind-qt-*
+
+run-valgrind-qt-app: qt-app
+ .mason/mason install valgrind latest
+ ./scripts/valgrind.sh $(QT_OUTPUT_PATH)/$(BUILDTYPE)/qmapboxgl --test -platform offscreen
+
#### Linux targets #####################################################
-LINUX_OUTPUT_PATH = build/linux-$(shell uname -m)
+LINUX_OUTPUT_PATH = build/linux-$(BUILD_PLATFORM_VERSION)
LINUX_MAKEFILE = $(LINUX_OUTPUT_PATH)/Makefile
$(LINUX_OUTPUT_PATH)/config.gypi: platform/linux/scripts/configure.sh $(CONFIG_DEPENDENCIES)
- ./configure $< $@ linux $(shell uname -m)
+ ./configure $< $@ linux $(BUILD_PLATFORM_VERSION)
$(LINUX_MAKEFILE): platform/linux/platform.gyp $(LINUX_OUTPUT_PATH)/config.gypi $(GYP_DEPENDENCIES)
$(GYP) -f make -I $(LINUX_OUTPUT_PATH)/config.gypi \
@@ -270,9 +315,6 @@ test: $(LINUX_MAKEFILE)
run-glfw-app: glfw-app
cd $(LINUX_OUTPUT_PATH)/$(BUILDTYPE) && ./mapbox-glfw
-run-valgrind-glfw-app: glfw-app
- cd $(LINUX_OUTPUT_PATH)/$(BUILDTYPE) && valgrind --leak-check=full --suppressions=../../../scripts/valgrind.sup ./mapbox-glfw
-
ifneq (,$(shell which gdb))
GDB = gdb -batch -return-child-result -ex 'set print thread-events off' -ex 'run' -ex 'thread apply all bt' --args
endif
@@ -280,19 +322,49 @@ endif
test-%: test
$(GDB) $(LINUX_OUTPUT_PATH)/$(BUILDTYPE)/test --gtest_catch_exceptions=0 --gtest_filter=$*
-check: test
+coverage: test
scripts/collect-coverage.sh $(LINUX_OUTPUT_PATH)/$(BUILDTYPE)
# Generates a compilation database with ninja for use in clang tooling
-compdb: platform/linux/platform.gyp $(LINUX_OUTPUT_PATH)/config.gypi
+compdb: node_modules compdb-$(BUILD_PLATFORM)
+
+compdb-linux: platform/linux/platform.gyp $(LINUX_OUTPUT_PATH)/config.gypi
$(GYP) -f ninja -I $(LINUX_OUTPUT_PATH)/config.gypi \
--generator-output=$(LINUX_OUTPUT_PATH) $<
deps/ninja/ninja-linux -C $(LINUX_OUTPUT_PATH)/$(BUILDTYPE) \
-t compdb cc cc_s cxx objc objcxx > $(LINUX_OUTPUT_PATH)/$(BUILDTYPE)/compile_commands.json
-tidy: compdb
- deps/ninja/ninja-linux -C $(LINUX_OUTPUT_PATH)/$(BUILDTYPE) version shaders
- scripts/clang-tidy.sh $(LINUX_OUTPUT_PATH)/$(BUILDTYPE)
+compdb-macos: platform/macos/platform.gyp $(MACOS_OUTPUT_PATH)/config.gypi
+ $(GYP) -f ninja -I $(MACOS_OUTPUT_PATH)/config.gypi \
+ --generator-output=$(MACOS_OUTPUT_PATH) $<
+ deps/ninja/ninja-macos -C $(MACOS_OUTPUT_PATH)/$(BUILDTYPE) \
+ -t compdb cc cc_s cxx objc objcxx > $(MACOS_OUTPUT_PATH)/$(BUILDTYPE)/compile_commands.json
+
+tidy: compdb tidy-$(BUILD_PLATFORM)
+
+clang-tools-linux:
+ if test -z $(CLANG_TIDY); then .mason/mason install clang-tidy 3.8.0; fi
+ if test -z $(CLANG_FORMAT); then .mason/mason install clang-format 3.8.0; fi
+ deps/ninja/ninja-linux -C $(LINUX_OUTPUT_PATH)/$(BUILDTYPE) headers
+
+tidy-linux: clang-tools-linux
+ scripts/clang-tools.sh $(LINUX_OUTPUT_PATH)/$(BUILDTYPE)
+
+clang-tools-macos:
+ if test -z $(CLANG_TIDY); then .mason/mason install clang-tidy 3.8.0; fi
+ if test -z $(CLANG_FORMAT); then .mason/mason install clang-format 3.8.0; fi
+ deps/ninja/ninja-macos -C $(MACOS_OUTPUT_PATH)/$(BUILDTYPE) headers
+
+tidy-macos: clang-tools-macos
+ scripts/clang-tools.sh $(MACOS_OUTPUT_PATH)/$(BUILDTYPE)
+
+check: compdb check-$(BUILD_PLATFORM)
+
+check-linux: clang-tools-linux
+ scripts/clang-tools.sh $(LINUX_OUTPUT_PATH)/$(BUILDTYPE) --diff
+
+check-macos: clang-tools-macos
+ scripts/clang-tools.sh $(MACOS_OUTPUT_PATH)/$(BUILDTYPE) --diff
#### Miscellaneous targets #####################################################
diff --git a/README.md b/README.md
index 3808f31ded..b4beb9752a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Mapbox GL Native
-A library for embedding interactive, customizable vector maps into native applications on multiple platforms. It takes stylesheets that conform to the [Mapbox GL Style Specification](https://github.com/mapbox/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://github.com/mapbox/vector-tile-spec), and renders them using OpenGL. [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) is the WebGL-based counterpart, designed for use on the Web.
+A library for embedding interactive, customizable vector maps into native applications on multiple platforms. It takes stylesheets that conform to the [Mapbox Style Specification](https://github.com/mapbox/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://github.com/mapbox/vector-tile-spec), and renders them using OpenGL. [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) is the WebGL-based counterpart, designed for use on the Web.
## The Mapbox GL ecosystem
@@ -11,7 +11,7 @@ SDK | Languages | Build status
[Mapbox GL Native](INSTALL.md) | C++14 | [![Travis](https://travis-ci.org/mapbox/mapbox-gl-native.svg?branch=master)](https://travis-ci.org/mapbox/mapbox-gl-native/builds) [![Coverage Status](https://coveralls.io/repos/github/mapbox/mapbox-gl-native/badge.svg?branch=master)](https://coveralls.io/github/mapbox/mapbox-gl-native?branch=master)
[Mapbox Android SDK](platform/android/) | Java | [![Bitrise](https://www.bitrise.io/app/79cdcbdc42de4303.svg?token=_InPF8bII6W7J6kFr-L8QQ&branch=master)](https://www.bitrise.io/app/79cdcbdc42de4303)
[Mapbox iOS SDK](platform/ios/) | Objective-C or Swift | [![Bitrise](https://www.bitrise.io/app/7514e4cf3da2cc57.svg?token=OwqZE5rSBR9MVWNr_lf4sA&branch=master)](https://www.bitrise.io/app/7514e4cf3da2cc57)
-[Mapbox macOS SDK](platform/macos/) | Objective-C or Swift | [![Bitrise](https://www.bitrise.io/app/155ef7da24b38dcd.svg?token=4KSOw_gd6WxTnvGE2rMttg&branch=master)](https://www.bitrise.io/app/155ef7da24b38dcd)
+[Mapbox macOS SDK](platform/macos/) | Objective-C, Swift, or AppleScript | [![Bitrise](https://www.bitrise.io/app/155ef7da24b38dcd.svg?token=4KSOw_gd6WxTnvGE2rMttg&branch=master)](https://www.bitrise.io/app/155ef7da24b38dcd)
[node-mapbox-gl-native](platform/node/) | Node.js | [![Linux](https://travis-ci.org/mapbox/mapbox-gl-native.svg?branch=master)](https://travis-ci.org/mapbox/mapbox-gl-native/builds) [![macOS](https://www.bitrise.io/app/55e3a9bf71202106.svg?token=5qf5ZUcKVN3LDnHhW7rO0w)](https://www.bitrise.io/app/55e3a9bf71202106)
[Mapbox Qt SDK](platform/qt) | C++03 | [![Travis](https://travis-ci.org/mapbox/mapbox-gl-native.svg?branch=master)](https://travis-ci.org/mapbox/mapbox-gl-native/builds) [![Bitrise](https://www.bitrise.io/app/96cfbc97e0245c22.svg?token=GxsqIOGPXhn0F23sSVSsYA&branch=master)](https://www.bitrise.io/app/96cfbc97e0245c22)
diff --git a/benchmark/parse/filter.cpp b/benchmark/parse/filter.cpp
index 9e47bcaca9..5214f682d6 100644
--- a/benchmark/parse/filter.cpp
+++ b/benchmark/parse/filter.cpp
@@ -2,47 +2,19 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/style/parser.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <rapidjson/document.h>
-#include <map>
-
using namespace mbgl;
-typedef std::multimap<std::string, Value> Properties;
-
-class StubFeature : public GeometryTileFeature {
-public:
- inline StubFeature(const Properties& properties_, FeatureType type_)
- : properties(properties_), type(type_) {
- }
-
- optional<Value> getValue(const std::string& key) const override {
- auto it = properties.find(key);
- if (it == properties.end())
- return optional<Value>();
- return it->second;
- }
-
- FeatureType getType() const override {
- return type;
- }
-
- GeometryCollection getGeometries() const override {
- return GeometryCollection();
- }
-
-private:
- const Properties properties;
- FeatureType type;
-};
-
style::Filter parse(const char* expression) {
rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
doc.Parse<0>(expression);
- return style::parseFilter(doc);
+ return *style::conversion::convert<style::Filter>(doc);
}
static void Parse_Filter(benchmark::State& state) {
@@ -53,11 +25,15 @@ static void Parse_Filter(benchmark::State& state) {
static void Parse_EvaluateFilter(benchmark::State& state) {
const style::Filter filter = parse(R"FILTER(["==", "foo", "bar"])FILTER");
- const Properties properties = { { "foo", std::string("bar") } };
- const StubFeature feature(properties, FeatureType::Unknown);
+ const PropertyMap properties = { { "foo", std::string("bar") } };
while (state.KeepRunning()) {
- style::Filter::visit(filter, style::FilterEvaluator{ feature });
+ filter(FeatureType::Unknown, {}, [&] (const std::string& key) -> optional<Value> {
+ auto it = properties.find(key);
+ if (it == properties.end())
+ return {};
+ return it->second;
+ });
}
}
diff --git a/configure b/configure
index 3c90c5ebbe..c3a3ae3191 100755
--- a/configure
+++ b/configure
@@ -118,7 +118,10 @@ print_flags zlib static_libs cflags ldflags
print_flags nunicode static_libs cflags ldflags
print_flags libzip static_libs cflags ldflags
print_flags geometry cflags
-print_flags geojsonvt static_libs cflags ldflags
+print_flags geojson static_libs cflags ldflags
+print_flags geojsonvt cflags
+print_flags supercluster cflags
+print_flags kdbush cflags
print_flags variant cflags
print_flags rapidjson static_libs cflags ldflags
print_flags gtest static_libs cflags ldflags
diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp
index 6cb26e6a82..a72c65b8c4 100644
--- a/include/mbgl/annotation/annotation.hpp
+++ b/include/mbgl/annotation/annotation.hpp
@@ -3,6 +3,7 @@
#include <mbgl/util/geometry.hpp>
#include <mbgl/util/variant.hpp>
#include <mbgl/util/color.hpp>
+#include <mbgl/style/property_value.hpp>
#include <cstdint>
#include <vector>
@@ -28,17 +29,17 @@ using ShapeAnnotationGeometry = variant<
class LineAnnotation {
public:
ShapeAnnotationGeometry geometry;
- float opacity = 1;
- float width = 1;
- Color color = {{ 0, 0, 0, 1 }};
+ style::PropertyValue<float> opacity { 1.0f };
+ style::PropertyValue<float> width { 1.0f };
+ style::PropertyValue<Color> color { Color::black() };
};
class FillAnnotation {
public:
ShapeAnnotationGeometry geometry;
- float opacity = 1;
- Color color = {{ 0, 0, 0, 1 }};
- Color outlineColor = {{ 0, 0, 0, -1 }};
+ style::PropertyValue<float> opacity { 1.0f };
+ style::PropertyValue<Color> color { Color::black() };
+ style::PropertyValue<Color> outlineColor {};
};
// An annotation whose type and properties are sourced from a style layer.
diff --git a/include/mbgl/gl/gl.hpp b/include/mbgl/gl/gl.hpp
index 6d601d8a23..d033fef549 100644
--- a/include/mbgl/gl/gl.hpp
+++ b/include/mbgl/gl/gl.hpp
@@ -45,9 +45,9 @@ namespace gl {
GLsizei length,
const GLchar *message,
const void *userParam);
-
+
template <class... Args> void mbx_trapExtension(const char *name, Args... args);
-
+
void mbx_trapExtension(const char *);
void mbx_trapExtension(const char *, GLint, const char *);
void mbx_trapExtension(const char *, GLsizei, GLuint *);
@@ -58,16 +58,16 @@ namespace gl {
void mbx_trapExtension(const char *, GLuint, GLuint, GLuint, GLuint, GLint, const char *, const void*);
void mbx_trapExtension(const char *name, GLuint array);
#endif
-
+
struct Error : ::std::runtime_error {
- inline Error(GLenum err, const std::string &msg) : ::std::runtime_error(msg), code(err) {};
+ Error(GLenum err, const std::string &msg) : ::std::runtime_error(msg), code(err) {};
const GLenum code;
};
void checkError(const char *cmd, const char *file, int line);
#if defined(DEBUG)
-#define MBGL_CHECK_ERROR(cmd) ([&]() { struct __MBGL_C_E { inline ~__MBGL_C_E() { ::mbgl::gl::checkError(#cmd, __FILE__, __LINE__); } } __MBGL_C_E; return cmd; }())
+#define MBGL_CHECK_ERROR(cmd) ([&]() { struct __MBGL_C_E { ~__MBGL_C_E() { ::mbgl::gl::checkError(#cmd, __FILE__, __LINE__); } } __MBGL_C_E; return cmd; }())
#else
#define MBGL_CHECK_ERROR(cmd) (cmd)
#endif
diff --git a/include/mbgl/gl/gl_helper.hpp b/include/mbgl/gl/gl_helper.hpp
index 893c73e0c3..9d46d018c3 100644
--- a/include/mbgl/gl/gl_helper.hpp
+++ b/include/mbgl/gl/gl_helper.hpp
@@ -6,9 +6,9 @@ namespace gl {
template <typename T>
class Preserve {
public:
- inline Preserve() : data(T::Get()) {
+ Preserve() : data(T::Get()) {
}
- inline ~Preserve() {
+ ~Preserve() {
T::Set(data);
}
diff --git a/include/mbgl/gl/gl_values.hpp b/include/mbgl/gl/gl_values.hpp
index 4204ca8e13..29a5281cca 100644
--- a/include/mbgl/gl/gl_values.hpp
+++ b/include/mbgl/gl/gl_values.hpp
@@ -3,8 +3,10 @@
#include <cstdint>
#include <tuple>
#include <array>
+#include <cassert>
#include <mbgl/gl/gl.hpp>
+#include <mbgl/util/color.hpp>
namespace mbgl {
namespace gl {
@@ -12,10 +14,10 @@ namespace gl {
struct ClearDepth {
using Type = GLfloat;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glClearDepth(value));
}
- inline static Type Get() {
+ static Type Get() {
Type clearDepth;
MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepth));
return clearDepth;
@@ -23,29 +25,25 @@ struct ClearDepth {
};
struct ClearColor {
- struct Type { GLfloat r, g, b, a; };
+ using Type = Color;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glClearColor(value.r, value.g, value.b, value.a));
}
- inline static Type Get() {
+ static Type Get() {
GLfloat floats[4];
MBGL_CHECK_ERROR(glGetFloatv(GL_COLOR_CLEAR_VALUE, floats));
return { floats[0], floats[1], floats[2], floats[3] };
}
};
-inline bool operator!=(const ClearColor::Type& a, const ClearColor::Type& b) {
- return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a;
-}
-
struct ClearStencil {
using Type = GLint;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glClearStencil(value));
}
- inline static Type Get() {
+ static Type Get() {
Type clearStencil;
MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &clearStencil));
return clearStencil;
@@ -55,10 +53,10 @@ struct ClearStencil {
struct StencilMask {
using Type = GLuint;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glStencilMask(value));
}
- inline static Type Get() {
+ static Type Get() {
GLint stencilMask;
MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_WRITEMASK, &stencilMask));
return stencilMask;
@@ -68,10 +66,10 @@ struct StencilMask {
struct DepthMask {
using Type = GLboolean;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glDepthMask(value));
}
- inline static Type Get() {
+ static Type Get() {
Type depthMask;
MBGL_CHECK_ERROR(glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask));
return depthMask;
@@ -81,10 +79,10 @@ struct DepthMask {
struct ColorMask {
struct Type { bool r, g, b, a; };
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glColorMask(value.r, value.g, value.b, value.a));
}
- inline static Type Get() {
+ static Type Get() {
GLboolean bools[4];
MBGL_CHECK_ERROR(glGetBooleanv(GL_COLOR_WRITEMASK, bools));
return { static_cast<bool>(bools[0]), static_cast<bool>(bools[1]),
@@ -92,17 +90,17 @@ struct ColorMask {
}
};
-inline bool operator!=(const ColorMask::Type& a, const ColorMask::Type& b) {
+constexpr bool operator!=(const ColorMask::Type& a, const ColorMask::Type& b) {
return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a;
}
struct StencilFunc {
struct Type { GLenum func; GLint ref; GLuint mask; };
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glStencilFunc(value.func, value.ref, value.mask));
}
- inline static Type Get() {
+ static Type Get() {
GLint func, ref, mask;
MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_FUNC, &func));
MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_REF, &ref));
@@ -111,17 +109,17 @@ struct StencilFunc {
}
};
-inline bool operator!=(const StencilFunc::Type& a, const StencilFunc::Type& b) {
+constexpr bool operator!=(const StencilFunc::Type& a, const StencilFunc::Type& b) {
return a.func != b.func || a.ref != b.ref || a.mask != b.mask;
}
struct StencilTest {
using Type = bool;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(value ? glEnable(GL_STENCIL_TEST) : glDisable(GL_STENCIL_TEST));
}
- inline static Type Get() {
+ static Type Get() {
Type stencilTest;
MBGL_CHECK_ERROR(stencilTest = glIsEnabled(GL_STENCIL_TEST));
return stencilTest;
@@ -131,10 +129,10 @@ struct StencilTest {
struct StencilOp {
struct Type { GLenum sfail, dpfail, dppass; };
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glStencilOp(value.sfail, value.dpfail, value.dppass));
}
- inline static Type Get() {
+ static Type Get() {
GLint sfail, dpfail, dppass;
MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_FAIL, &sfail));
MBGL_CHECK_ERROR(glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &dpfail));
@@ -143,30 +141,34 @@ struct StencilOp {
}
};
+constexpr bool operator!=(const StencilOp::Type& a, const StencilOp::Type& b) {
+ return a.sfail != b.sfail || a.dpfail != b.dpfail || a.dppass != b.dppass;
+}
+
struct DepthRange {
struct Type { GLfloat near, far; };
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glDepthRange(value.near, value.far));
}
- inline static Type Get() {
+ static Type Get() {
GLfloat floats[2];
MBGL_CHECK_ERROR(glGetFloatv(GL_DEPTH_RANGE, floats));
return { floats[0], floats[1] };
}
};
-inline bool operator!=(const DepthRange::Type& a, const DepthRange::Type& b) {
+constexpr bool operator!=(const DepthRange::Type& a, const DepthRange::Type& b) {
return a.near != b.near || a.far != b.far;
}
struct DepthTest {
using Type = bool;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(value ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST));
}
- inline static Type Get() {
+ static Type Get() {
Type depthTest;
MBGL_CHECK_ERROR(depthTest = glIsEnabled(GL_DEPTH_TEST));
return depthTest;
@@ -176,10 +178,10 @@ struct DepthTest {
struct DepthFunc {
using Type = GLenum;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glDepthFunc(value));
}
- inline static Type Get() {
+ static Type Get() {
GLint depthFunc;
MBGL_CHECK_ERROR(glGetIntegerv(GL_DEPTH_FUNC, &depthFunc));
return depthFunc;
@@ -189,10 +191,10 @@ struct DepthFunc {
struct Blend {
using Type = bool;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(value ? glEnable(GL_BLEND) : glDisable(GL_BLEND));
}
- inline static Type Get() {
+ static Type Get() {
Type blend;
MBGL_CHECK_ERROR(blend = glIsEnabled(GL_BLEND));
return blend;
@@ -202,10 +204,10 @@ struct Blend {
struct BlendFunc {
struct Type { GLenum sfactor, dfactor; };
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glBlendFunc(value.sfactor, value.dfactor));
}
- inline static Type Get() {
+ static Type Get() {
GLint sfactor, dfactor;
MBGL_CHECK_ERROR(glGetIntegerv(GL_BLEND_SRC_ALPHA, &sfactor));
MBGL_CHECK_ERROR(glGetIntegerv(GL_BLEND_DST_ALPHA, &dfactor));
@@ -213,13 +215,30 @@ struct BlendFunc {
}
};
+constexpr bool operator!=(const BlendFunc::Type& a, const BlendFunc::Type& b) {
+ return a.sfactor != b.sfactor || a.dfactor != b.dfactor;
+}
+
+struct BlendColor {
+ using Type = Color;
+ static const Type Default;
+ inline static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glBlendColor(value.r, value.g, value.b, value.a));
+ }
+ inline static Type Get() {
+ GLfloat floats[4];
+ MBGL_CHECK_ERROR(glGetFloatv(GL_BLEND_COLOR, floats));
+ return { floats[0], floats[1], floats[2], floats[3] };
+ }
+};
+
struct Program {
using Type = GLuint;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glUseProgram(value));
}
- inline static Type Get() {
+ static Type Get() {
GLint program;
MBGL_CHECK_ERROR(glGetIntegerv(GL_CURRENT_PROGRAM, &program));
return program;
@@ -229,10 +248,10 @@ struct Program {
struct LineWidth {
using Type = GLfloat;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glLineWidth(value));
}
- inline static Type Get() {
+ static Type Get() {
Type lineWidth;
MBGL_CHECK_ERROR(glGetFloatv(GL_LINE_WIDTH, &lineWidth));
return lineWidth;
@@ -240,15 +259,15 @@ struct LineWidth {
};
struct ActiveTexture {
- using Type = GLint;
+ using Type = uint8_t;
static const Type Default;
- inline static void Set(const Type& value) {
- MBGL_CHECK_ERROR(glActiveTexture(value));
+ static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + value));
}
- inline static Type Get() {
- Type activeTexture;
+ static Type Get() {
+ GLint activeTexture;
MBGL_CHECK_ERROR(glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture));
- return activeTexture;
+ return activeTexture - GL_TEXTURE0;
}
};
@@ -257,10 +276,10 @@ struct ActiveTexture {
struct PixelZoom {
struct Type { GLfloat xfactor; GLfloat yfactor; };
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glPixelZoom(value.xfactor, value.yfactor));
}
- inline static Type Get() {
+ static Type Get() {
Type value;
MBGL_CHECK_ERROR(glGetFloatv(GL_ZOOM_X, &value.xfactor));
MBGL_CHECK_ERROR(glGetFloatv(GL_ZOOM_Y, &value.yfactor));
@@ -268,17 +287,17 @@ struct PixelZoom {
}
};
-inline bool operator!=(const PixelZoom::Type& a, const PixelZoom::Type& b) {
+constexpr bool operator!=(const PixelZoom::Type& a, const PixelZoom::Type& b) {
return a.xfactor != b.xfactor || a.yfactor != b.yfactor;
}
struct RasterPos {
using Type = std::array<GLdouble, 4>;
static const Type Default;
- inline static void Set(const Type& value) {
+ static void Set(const Type& value) {
MBGL_CHECK_ERROR(glRasterPos4d(value[0], value[1], value[2], value[3]));
}
- inline static Type Get() {
+ static Type Get() {
Type pos;
MBGL_CHECK_ERROR(glGetDoublev(GL_CURRENT_RASTER_POSITION, pos.data()));
return pos;
@@ -287,5 +306,18 @@ struct RasterPos {
#endif // GL_ES_VERSION_2_0
+struct BindTexture {
+ using Type = GLuint;
+ static const Type Default;
+ static void Set(const Type& value) {
+ MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, value));
+ }
+ static Type Get() {
+ GLint texture;
+ MBGL_CHECK_ERROR(glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture));
+ return texture;
+ }
+};
+
} // namespace gl
} // namespace mbgl
diff --git a/include/mbgl/map/camera.hpp b/include/mbgl/map/camera.hpp
index 4520321655..1c389577f0 100644
--- a/include/mbgl/map/camera.hpp
+++ b/include/mbgl/map/camera.hpp
@@ -14,23 +14,23 @@ namespace mbgl {
struct CameraOptions {
/** Coordinate at the center of the map. */
optional<LatLng> center;
-
+
/** Padding around the interior of the view that affects the frame of
reference for `center`. */
optional<EdgeInsets> padding;
-
+
/** Point of reference for `zoom` and `angle`, assuming an origin at the
top-left corner of the view. */
optional<ScreenCoordinate> anchor;
-
+
/** Zero-based zoom level. Constrained to the minimum and maximum zoom
levels. */
optional<double> zoom;
-
+
/** Bearing, measured in radians counterclockwise from true north. Wrapped
to [−π rad, π rad). */
optional<double> angle;
-
+
/** Pitch toward the horizon measured in radians, with 0 rad resulting in a
two-dimensional map. */
optional<double> pitch;
@@ -42,35 +42,35 @@ struct CameraOptions {
struct AnimationOptions {
/** Time to animate to the viewpoint defined herein. */
optional<Duration> duration;
-
+
/** Average velocity of a flyTo() transition, measured in screenfuls per
second, assuming a linear timing curve.
-
+
A <i>screenful</i> is the visible span in pixels. It does not correspond
to a fixed physical distance but rather varies by zoom level. */
optional<double> velocity;
-
+
/** Zero-based zoom level at the peak of the flyTo() transition’s flight
path. */
optional<double> minZoom;
-
+
/** The easing timing curve of the transition. */
optional<mbgl::util::UnitBezier> easing;
-
+
/** A function that is called on each frame of the transition, just before a
screen update, except on the last frame. The first parameter indicates
the elapsed time as a percentage of the duration. */
std::function<void(double)> transitionFrameFn;
-
+
/** A function that is called once on the last frame of the transition, just
before the corresponding screen update. */
std::function<void()> transitionFinishFn;
-
+
/** Creates an animation with no options specified. */
- inline AnimationOptions() {}
-
+ AnimationOptions() {}
+
/** Creates an animation with the specified duration. */
- inline AnimationOptions(Duration d)
+ AnimationOptions(Duration d)
: duration(d) {}
};
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index f805680b7b..0e0c04ff0f 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -26,8 +26,9 @@ struct CameraOptions;
struct AnimationOptions;
namespace style {
+class Source;
class Layer;
-}
+} // namespace style
class Map : private util::noncopyable {
public:
@@ -149,6 +150,13 @@ public:
AnnotationIDs getPointAnnotationsInBounds(const LatLngBounds&);
+ // Sources
+ style::Source* getSource(const std::string& sourceID);
+ void addSource(std::unique_ptr<style::Source>);
+ void removeSource(const std::string& sourceID);
+
+ // Layers
+ style::Layer* getLayer(const std::string& layerID);
void addLayer(std::unique_ptr<style::Layer>, const optional<std::string>& beforeLayerID = {});
void removeLayer(const std::string& layerID);
diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp
index 074bc3f08e..c9b9f60953 100644
--- a/include/mbgl/map/mode.hpp
+++ b/include/mbgl/map/mode.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <mbgl/util/traits.hpp>
+
#include <cstdint>
namespace mbgl {
@@ -41,24 +43,23 @@ enum class MapDebugOptions : EnumType {
ParseStatus = 1 << 2,
Timestamps = 1 << 3,
Collision = 1 << 4,
- Wireframe = 1 << 5,
+ Overdraw = 1 << 5,
// FIXME: https://github.com/mapbox/mapbox-gl-native/issues/5117
#ifndef GL_ES_VERSION_2_0
StencilClip = 1 << 6,
#endif // GL_ES_VERSION_2_0
};
-inline MapDebugOptions operator| (const MapDebugOptions& lhs, const MapDebugOptions& rhs) {
- return MapDebugOptions(static_cast<EnumType>(lhs) | static_cast<EnumType>(rhs));
+constexpr MapDebugOptions operator|(MapDebugOptions lhs, MapDebugOptions rhs) {
+ return MapDebugOptions(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs));
}
-inline MapDebugOptions& operator|=(MapDebugOptions& lhs, const MapDebugOptions& rhs) {
- lhs = lhs | rhs;
- return lhs;
+constexpr MapDebugOptions& operator|=(MapDebugOptions& lhs, MapDebugOptions rhs) {
+ return (lhs = lhs | rhs);
}
-inline bool operator& (const MapDebugOptions& lhs, const MapDebugOptions& rhs) {
- return static_cast<EnumType>(lhs) & static_cast<EnumType>(rhs);
+constexpr bool operator&(MapDebugOptions lhs, MapDebugOptions rhs) {
+ return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
}
} // namespace mbgl
diff --git a/include/mbgl/map/update.hpp b/include/mbgl/map/update.hpp
index 3915545bb0..36ce59c01d 100644
--- a/include/mbgl/map/update.hpp
+++ b/include/mbgl/map/update.hpp
@@ -13,19 +13,19 @@ enum class Update : uint8_t {
RecalculateStyle = 1 << 3,
RenderStill = 1 << 4,
Repaint = 1 << 5,
- Annotations = 1 << 6,
+ AnnotationStyle = 1 << 6,
+ AnnotationData = 1 << 7,
};
-inline Update operator| (const Update& lhs, const Update& rhs) {
+constexpr Update operator|(Update lhs, Update rhs) {
return Update(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs));
}
-inline Update& operator|=(Update& lhs, const Update& rhs) {
- lhs = lhs | rhs;
- return lhs;
+constexpr Update& operator|=(Update& lhs, const Update& rhs) {
+ return (lhs = lhs | rhs);
}
-inline bool operator& (const Update& lhs, const Update& rhs) {
+constexpr bool operator& (Update lhs, Update rhs) {
return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
}
diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp
index 9f12ee5fb6..fa6f91d910 100644
--- a/include/mbgl/map/view.hpp
+++ b/include/mbgl/map/view.hpp
@@ -4,6 +4,7 @@
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/image.hpp>
+#include <array>
#include <functional>
#include <memory>
diff --git a/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp b/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp
deleted file mode 100644
index b0ca060b85..0000000000
--- a/include/mbgl/platform/darwin/settings_nsuserdefaults.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#import <mbgl/ios/MGLTypes.h>
-
-namespace mbgl {
-
-class Settings_NSUserDefaults {
-public:
- Settings_NSUserDefaults();
- void load();
- void save();
- void clear();
-
-public:
- double longitude = 0;
- double latitude = 0;
- double zoom = 0;
- double bearing = 0;
- double pitch = 0;
-
- MGLUserTrackingMode userTrackingMode = MGLUserTrackingModeNone;
- bool showsUserLocation = false;
-
- uint32_t debug = 0;
-};
-
-}
diff --git a/include/mbgl/platform/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp
index b1204ce96d..38ab922414 100644
--- a/include/mbgl/platform/default/glfw_view.hpp
+++ b/include/mbgl/platform/default/glfw_view.hpp
@@ -14,7 +14,7 @@
class GLFWView : public mbgl::View {
public:
GLFWView(bool fullscreen = false, bool benchmark = false);
- ~GLFWView();
+ ~GLFWView() override;
float getPixelRatio() const override;
std::array<uint16_t, 2> getSize() const override;
@@ -44,6 +44,7 @@ public:
void report(float duration);
private:
+ mbgl::Color makeRandomColor() const;
mbgl::Point<double> makeRandomPoint() const;
static std::shared_ptr<const mbgl::SpriteImage>
makeSpriteImage(int width, int height, float pixelRatio);
@@ -51,6 +52,7 @@ private:
void nextOrientation();
void addRandomPointAnnotations(int count);
+ void addRandomLineAnnotations(int count);
void addRandomShapeAnnotations(int count);
void addRandomCustomPointAnnotations(int count);
diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp
index d787eb1691..e3acc8e379 100644
--- a/include/mbgl/platform/default/headless_view.hpp
+++ b/include/mbgl/platform/default/headless_view.hpp
@@ -31,7 +31,7 @@ class HeadlessView : public View {
public:
HeadlessView(float pixelRatio, uint16_t width = 256, uint16_t height = 256);
HeadlessView(std::shared_ptr<HeadlessDisplay> display, float pixelRatio, uint16_t width = 256, uint16_t height = 256);
- ~HeadlessView();
+ ~HeadlessView() override;
float getPixelRatio() const override;
std::array<uint16_t, 2> getSize() const override;
@@ -74,7 +74,7 @@ private:
#if MBGL_USE_GLX
Display *xDisplay = nullptr;
GLXFBConfig *fbConfigs = nullptr;
- GLXContext glContext = 0;
+ GLXContext glContext = nullptr;
GLXPbuffer glxPbuffer = 0;
#endif
diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp
index 0b3b79df6b..ee1ce6fa4e 100644
--- a/include/mbgl/platform/event.hpp
+++ b/include/mbgl/platform/event.hpp
@@ -1,7 +1,5 @@
#pragma once
-#include <mbgl/util/enum.hpp>
-
#include <cstdint>
namespace mbgl {
@@ -13,14 +11,6 @@ enum class EventSeverity : uint8_t {
Error,
};
-MBGL_DEFINE_ENUM_CLASS(EventSeverityClass, EventSeverity, {
- { EventSeverity::Debug, "DEBUG" },
- { EventSeverity::Info, "INFO" },
- { EventSeverity::Warning, "WARNING" },
- { EventSeverity::Error, "ERROR" },
- { EventSeverity(-1), "UNKNOWN" },
-});
-
enum class Event : uint8_t {
General,
Setup,
@@ -40,27 +30,6 @@ enum class Event : uint8_t {
Glyph,
};
-MBGL_DEFINE_ENUM_CLASS(EventClass, Event, {
- { Event::General, "General" },
- { Event::Setup, "Setup" },
- { Event::Shader, "Shader" },
- { Event::ParseStyle, "ParseStyle" },
- { Event::ParseTile, "ParseTile" },
- { Event::Render, "Render" },
- { Event::Style, "Style" },
- { Event::Database, "Database" },
- { Event::HttpRequest, "HttpRequest" },
- { Event::Sprite, "Sprite" },
- { Event::Image, "Image" },
- { Event::OpenGL, "OpenGL" },
- { Event::JNI, "JNI" },
- { Event::Android, "Android" },
- { Event::Crash, "Crash" },
- { Event::Glyph, "Glyph" },
- { Event(-1), "Unknown" },
-});
-
-
struct EventPermutation {
const EventSeverity severity;
const Event event;
diff --git a/include/mbgl/platform/log.hpp b/include/mbgl/platform/log.hpp
index 48b9e3dc70..d5bb1c2fcc 100644
--- a/include/mbgl/platform/log.hpp
+++ b/include/mbgl/platform/log.hpp
@@ -37,27 +37,27 @@ private:
public:
template <typename ...Args>
- static inline void Debug(Event event, Args&& ...args) {
+ static void Debug(Event event, Args&& ...args) {
Record(EventSeverity::Debug, event, ::std::forward<Args>(args)...);
}
template <typename ...Args>
- static inline void Info(Event event, Args&& ...args) {
+ static void Info(Event event, Args&& ...args) {
Record(EventSeverity::Info, event, ::std::forward<Args>(args)...);
}
template <typename ...Args>
- static inline void Warning(Event event, Args&& ...args) {
+ static void Warning(Event event, Args&& ...args) {
Record(EventSeverity::Warning, event, ::std::forward<Args>(args)...);
}
template <typename ...Args>
- static inline void Error(Event event, Args&& ...args) {
+ static void Error(Event event, Args&& ...args) {
Record(EventSeverity::Error, event, ::std::forward<Args>(args)...);
}
template <typename ...Args>
- static inline void Record(EventSeverity severity, Event event, Args&& ...args) {
+ static void Record(EventSeverity severity, Event event, Args&& ...args) {
if (!includes(severity, disabledEventSeverities) &&
!includes(event, disabledEvents) &&
!includes({ severity, event }, disabledEventPermutations)) {
diff --git a/include/mbgl/platform/platform.hpp b/include/mbgl/platform/platform.hpp
index 59ba7f97f3..cc8327c470 100644
--- a/include/mbgl/platform/platform.hpp
+++ b/include/mbgl/platform/platform.hpp
@@ -14,6 +14,12 @@ std::string uppercase(const std::string &string);
// Lowercase a string, potentially using platform-specific routines.
std::string lowercase(const std::string &string);
+// Gets the name of the current thread.
+std::string getCurrentThreadName();
+
+// Set the name of the current thread, truncated at 15.
+void setCurrentThreadName(const std::string& name);
+
// Makes the current thread low priority.
void makeThreadLowPriority();
diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp
index e77befda1b..235e738254 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -26,6 +26,10 @@ public:
uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE);
~DefaultFileSource() override;
+ bool supportsOptionalRequests() const override {
+ return true;
+ }
+
void setAccessToken(const std::string&);
std::string getAccessToken() const;
diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp
index 88e0336187..404c683fdb 100644
--- a/include/mbgl/storage/file_source.hpp
+++ b/include/mbgl/storage/file_source.hpp
@@ -23,6 +23,14 @@ public:
// If the request is cancelled before the callback is executed, the callback will
// not be executed.
virtual std::unique_ptr<AsyncRequest> request(const Resource&, Callback) = 0;
+
+ // When a file source supports optional requests, it must return true.
+ // Optional requests are requests that aren't as urgent, but could be useful, e.g.
+ // to cover part of the map while loading. The FileSource should only do cheap actions to
+ // retrieve the data, e.g. load it from a cache, but not from the internet.
+ virtual bool supportsOptionalRequests() const {
+ return false;
+ }
};
} // namespace mbgl
diff --git a/include/mbgl/storage/network_status.hpp b/include/mbgl/storage/network_status.hpp
index 1b5471a44e..d7f502a3b2 100644
--- a/include/mbgl/storage/network_status.hpp
+++ b/include/mbgl/storage/network_status.hpp
@@ -1,7 +1,6 @@
#pragma once
-#include <mbgl/util/atomic.hpp>
-
+#include <atomic>
#include <mutex>
#include <set>
@@ -27,7 +26,7 @@ public:
static void Unsubscribe(util::AsyncTask* async);
private:
- static util::Atomic<bool> online;
+ static std::atomic<bool> online;
static std::mutex mtx;
static std::set<util::AsyncTask*> observers;
};
diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp
index 990c8470bb..818cfe2ba5 100644
--- a/include/mbgl/storage/offline.hpp
+++ b/include/mbgl/storage/offline.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/util/geo.hpp>
+#include <mbgl/util/range.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/storage/response.hpp>
@@ -12,7 +13,6 @@
namespace mbgl {
class TileID;
-class Tileset;
/*
* An offline region defined by a style URL, geographic bounding box, zoom range, and
@@ -27,10 +27,10 @@ class Tileset;
*/
class OfflineTilePyramidRegionDefinition {
public:
- OfflineTilePyramidRegionDefinition(const std::string&, const LatLngBounds&, double, double, float);
+ OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float);
/* Private */
- std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const Tileset&) const;
+ std::vector<CanonicalTileID> tileCover(SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
const std::string styleURL;
const LatLngBounds bounds;
@@ -200,8 +200,8 @@ private:
friend class OfflineDatabase;
OfflineRegion(int64_t id,
- const OfflineRegionDefinition&,
- const OfflineRegionMetadata&);
+ OfflineRegionDefinition,
+ OfflineRegionMetadata);
const int64_t id;
const OfflineRegionDefinition definition;
diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp
index a75de380a1..31df069952 100644
--- a/include/mbgl/storage/resource.hpp
+++ b/include/mbgl/storage/resource.hpp
@@ -33,10 +33,10 @@ public:
Required = true,
};
- Resource(Kind kind_, const std::string& url_, optional<TileData> tileData_ = {}, Necessity necessity_ = Required)
+ Resource(Kind kind_, std::string url_, optional<TileData> tileData_ = {}, Necessity necessity_ = Required)
: kind(kind_),
necessity(necessity_),
- url(url_),
+ url(std::move(url_)),
tileData(std::move(tileData_)) {
}
diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp
index 83227573cf..6c79f5e181 100644
--- a/include/mbgl/storage/response.hpp
+++ b/include/mbgl/storage/response.hpp
@@ -49,7 +49,7 @@ public:
std::string message;
public:
- Error(Reason, const std::string& = "");
+ Error(Reason, std::string = "");
};
std::ostream& operator<<(std::ostream&, Response::Error::Reason);
diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp
new file mode 100644
index 0000000000..e53adcb942
--- /dev/null
+++ b/include/mbgl/style/conversion.hpp
@@ -0,0 +1,95 @@
+#pragma once
+
+#include <mbgl/util/variant.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+/*
+ The `conversion` namespace defines conversions from a templated type `V` representing a JSON
+ object conforming to the schema defined by the Mapbox Style Specification, to the various C++
+ types that form the C++ model of that domain:
+
+ * `std::unique_ptr<Source>`
+ * `std::unique_ptr<Layer>`
+ * `Filter`
+ * `PropertyValue<T>`
+
+ A single template function serves as the public interface:
+
+ template <class T, class V>
+ Result<T> convert(const V& value);
+
+ Where `T` is one of the above types. If the conversion fails, the `Error` variant of `Result` is
+ returned, which includes diagnostic text suitable for presentation to a library user. Otherwise,
+ the `T` variant of `Result` is returned.
+
+ The implementation of `convert` requires that the following are legal expressions for a value `v`
+ of type `const V&`:
+
+ * `isUndefined(v)` -- returns a boolean indication whether `v` is undefined or a JSON null
+
+ * `isArray(v)` -- returns a boolean indicating whether `v` represents a JSON array
+ * `arrayLength(v)` -- called only if `isArray(v)`; returns a size_t length
+ * `arrayMember(v)` -- called only if `isArray(v)`; returns `V` or `V&`
+
+ * `isObject(v)` -- returns a boolean indicating whether `v` represents a JSON object
+ * `objectMember(v, name)` -- called only if `isObject(v)`; `name` is `const char *`; return value:
+ * is true when evaluated in a boolean context iff the named member exists
+ * is convertable to a `V` or `V&` when dereferenced
+ * `eachMember(v, [] (const std::string&, const V&) -> optional<Error> {...})` -- called
+ only if `isObject(v)`; calls the provided lambda once for each key and value of the object;
+ short-circuits if any call returns an `Error`
+
+ * `toBool(v)` -- returns `optional<bool>`, absence indicating `v` is not a JSON boolean
+ * `toNumber(v)` -- returns `optional<float>`, absence indicating `v` is not a JSON number
+ * `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string
+ * `toValue(v)` -- returns `optional<mbgl::Value>`, a variant type, for generic conversion,
+ absence indicating `v` is not a boolean, number, or string. Numbers should be converted to
+ unsigned integer, signed integer, or floating point, in descending preference.
+
+ The mbgl core implements these requirements for RapidJSON types, and the node bindings implement
+ them for v8 types.
+*/
+
+struct Error { std::string message; };
+
+template <class T>
+class Result : private variant<T, Error> {
+public:
+ using variant<T, Error>::variant;
+
+ explicit operator bool() const {
+ return this->template is<T>();
+ }
+
+ T& operator*() {
+ assert(this->template is<T>());
+ return this->template get<T>();
+ }
+
+ const T& operator*() const {
+ assert(this->template is<T>());
+ return this->template get<T>();
+ }
+
+ const Error& error() const {
+ assert(this->template is<Error>());
+ return this->template get<Error>();
+ }
+};
+
+template <class T, class Enable = void>
+struct Converter;
+
+template <class T, class V, class...Args>
+Result<T> convert(const V& value, Args&&...args) {
+ return Converter<T>()(value, std::forward<Args>(args)...);
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp
new file mode 100644
index 0000000000..05bf968f4d
--- /dev/null
+++ b/include/mbgl/style/conversion/constant.hpp
@@ -0,0 +1,174 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/util/enum.hpp>
+
+#include <array>
+#include <string>
+#include <vector>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<bool> {
+ template <class V>
+ Result<bool> operator()(const V& value) const {
+ optional<bool> converted = toBool(value);
+ if (!converted) {
+ return Error { "value must be a boolean" };
+ }
+ return *converted;
+ }
+};
+
+template <>
+struct Converter<float> {
+ template <class V>
+ Result<float> operator()(const V& value) const {
+ optional<float> converted = toNumber(value);
+ if (!converted) {
+ return Error { "value must be a number" };
+ }
+ return *converted;
+ }
+};
+
+template <>
+struct Converter<std::string> {
+ template <class V>
+ Result<std::string> operator()(const V& value) const {
+ optional<std::string> converted = toString(value);
+ if (!converted) {
+ return Error { "value must be a string" };
+ }
+ return *converted;
+ }
+};
+
+template <class T>
+struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> {
+ template <class V>
+ Result<T> operator()(const V& value) const {
+ optional<std::string> string = toString(value);
+ if (!string) {
+ return Error { "value must be a string" };
+ }
+
+ const auto result = Enum<T>::toEnum(*string);
+ if (!result) {
+ return Error { "value must be a valid enumeration value" };
+ }
+
+ return *result;
+ }
+};
+
+template <>
+struct Converter<Color> {
+ template <class V>
+ Result<Color> operator()(const V& value) const {
+ optional<std::string> string = toString(value);
+ if (!string) {
+ return Error { "value must be a string" };
+ }
+
+ optional<Color> color = Color::parse(*string);
+ if (!color) {
+ return Error { "value must be a valid color" };
+ }
+
+ return *color;
+ }
+};
+
+template <>
+struct Converter<std::array<float, 2>> {
+ template <class V>
+ Result<std::array<float, 2>> operator()(const V& value) const {
+ if (!isArray(value) || arrayLength(value) != 2) {
+ return Error { "value must be an array of two numbers" };
+ }
+
+ optional<float> first = toNumber(arrayMember(value, 0));
+ optional<float> second = toNumber(arrayMember(value, 1));
+ if (!first || !second) {
+ return Error { "value must be an array of two numbers" };
+ }
+
+ return std::array<float, 2> {{ *first, *second }};
+ }
+};
+
+template <>
+struct Converter<std::array<float, 4>> {
+ template <class V>
+ Result<std::array<float, 4>> operator()(const V& value) const {
+ if (!isArray(value) || arrayLength(value) != 4) {
+ return Error { "value must be an array of four numbers" };
+ }
+
+ optional<float> first = toNumber(arrayMember(value, 0));
+ optional<float> second = toNumber(arrayMember(value, 1));
+ optional<float> third = toNumber(arrayMember(value, 2));
+ optional<float> fourth = toNumber(arrayMember(value, 3));
+ if (!first || !second) {
+ return Error { "value must be an array of four numbers" };
+ }
+
+ return std::array<float, 4> {{ *first, *second, *third, *fourth }};
+ }
+};
+
+template <>
+struct Converter<std::vector<float>> {
+ template <class V>
+ Result<std::vector<float>> operator()(const V& value) const {
+ if (!isArray(value)) {
+ return Error { "value must be an array" };
+ }
+
+ std::vector<float> result;
+ result.reserve(arrayLength(value));
+
+ for (std::size_t i = 0; i < arrayLength(value); ++i) {
+ optional<float> number = toNumber(arrayMember(value, i));
+ if (!number) {
+ return Error { "value must be an array of numbers" };
+ }
+ result.push_back(*number);
+ }
+
+ return result;
+ }
+};
+
+template <>
+struct Converter<std::vector<std::string>> {
+ template <class V>
+ Result<std::vector<std::string>> operator()(const V& value) const {
+ if (!isArray(value)) {
+ return Error { "value must be an array" };
+ }
+
+ std::vector<std::string> result;
+ result.reserve(arrayLength(value));
+
+ for (std::size_t i = 0; i < arrayLength(value); ++i) {
+ optional<std::string> string = toString(arrayMember(value, i));
+ if (!string) {
+ return Error { "value must be an array of strings" };
+ }
+ result.push_back(*string);
+ }
+
+ return result;
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp
new file mode 100644
index 0000000000..3ab91e5bbc
--- /dev/null
+++ b/include/mbgl/style/conversion/filter.hpp
@@ -0,0 +1,150 @@
+#pragma once
+
+#include <mbgl/style/filter.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/util/geometry.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<Filter> {
+public:
+ template <class V>
+ Result<Filter> operator()(const V& value) const {
+ if (!isArray(value)) {
+ return Error { "filter expression must be an array" };
+ }
+
+ if (arrayLength(value) < 1) {
+ return Error { "filter expression must have at least 1 element" };
+ }
+
+ optional<std::string> op = toString(arrayMember(value, 0));
+ if (!op) {
+ return Error { "filter operator must be a string" };
+ }
+
+ if (*op == "==") {
+ return convertBinaryFilter<EqualsFilter>(value);
+ } else if (*op == "!=") {
+ return convertBinaryFilter<NotEqualsFilter>(value);
+ } else if (*op == ">") {
+ return convertBinaryFilter<GreaterThanFilter>(value);
+ } else if (*op == ">=") {
+ return convertBinaryFilter<GreaterThanEqualsFilter>(value);
+ } else if (*op == "<") {
+ return convertBinaryFilter<LessThanFilter>(value);
+ } else if (*op == "<=") {
+ return convertBinaryFilter<LessThanEqualsFilter>(value);
+ } else if (*op == "in") {
+ return convertSetFilter<InFilter>(value);
+ } else if (*op == "!in") {
+ return convertSetFilter<NotInFilter>(value);
+ } else if (*op == "all") {
+ return convertCompoundFilter<AllFilter>(value);
+ } else if (*op == "any") {
+ return convertCompoundFilter<AnyFilter>(value);
+ } else if (*op == "none") {
+ return convertCompoundFilter<NoneFilter>(value);
+ } else if (*op == "has") {
+ return convertUnaryFilter<HasFilter>(value);
+ } else if (*op == "!has") {
+ return convertUnaryFilter<NotHasFilter>(value);
+ }
+
+ return Error { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" };
+ }
+
+private:
+ Result<Value> normalizeValue(const std::string& key, const optional<Value>& value) const {
+ if (!value) {
+ return Error { "filter expression value must be a boolean, number, or string" };
+ } else if (key != "$type") {
+ return *value;
+ } else if (*value == std::string("Point")) {
+ return Value(uint64_t(FeatureType::Point));
+ } else if (*value == std::string("LineString")) {
+ return Value(uint64_t(FeatureType::LineString));
+ } else if (*value == std::string("Polygon")) {
+ return Value(uint64_t(FeatureType::Polygon));
+ } else {
+ return Error { "value for $type filter must be Point, LineString, or Polygon" };
+ }
+ }
+
+ template <class FilterType, class V>
+ Result<Filter> convertUnaryFilter(const V& value) const {
+ if (arrayLength(value) < 2) {
+ return Error { "filter expression must have 2 elements" };
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ return Error { "filter expression key must be a string" };
+ }
+
+ return FilterType { *key };
+ }
+
+ template <class FilterType, class V>
+ Result<Filter> convertBinaryFilter(const V& value) const {
+ if (arrayLength(value) < 3) {
+ return Error { "filter expression must have 3 elements" };
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ return Error { "filter expression key must be a string" };
+ }
+
+ Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, 2)));
+ if (!filterValue) {
+ return filterValue.error();
+ }
+
+ return FilterType { *key, *filterValue };
+ }
+
+ template <class FilterType, class V>
+ Result<Filter> convertSetFilter(const V& value) const {
+ if (arrayLength(value) < 2) {
+ return Error { "filter expression must at least 2 elements" };
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ return Error { "filter expression key must be a string" };
+ }
+
+ std::vector<Value> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, i)));
+ if (!filterValue) {
+ return filterValue.error();
+ }
+ values.push_back(*filterValue);
+ }
+
+ return FilterType { *key, std::move(values) };
+ }
+
+ template <class FilterType, class V>
+ Result<Filter> convertCompoundFilter(const V& value) const {
+ std::vector<Filter> filters;
+ for (std::size_t i = 1; i < arrayLength(value); ++i) {
+ Result<Filter> element = operator()(arrayMember(value, i));
+ if (!element) {
+ return element.error();
+ }
+ filters.push_back(*element);
+ }
+
+ return FilterType { std::move(filters) };
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp
new file mode 100644
index 0000000000..f14b5089be
--- /dev/null
+++ b/include/mbgl/style/conversion/function.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <mbgl/style/function.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <class T>
+struct Converter<Function<T>> {
+ template <class V>
+ Result<Function<T>> operator()(const V& value) const {
+ if (!isObject(value)) {
+ return Error { "function must be an object" };
+ }
+
+ auto stopsValue = objectMember(value, "stops");
+ if (!stopsValue) {
+ return Error { "function value must specify stops" };
+ }
+
+ if (!isArray(*stopsValue)) {
+ return Error { "function stops must be an array" };
+ }
+
+ std::vector<std::pair<float, T>> stops;
+ for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) {
+ const auto& stopValue = arrayMember(*stopsValue, i);
+
+ if (!isArray(stopValue)) {
+ return Error { "function stop must be an array" };
+ }
+
+ if (arrayLength(stopValue) != 2) {
+ return Error { "function stop must have two elements" };
+ }
+
+ optional<float> z = toNumber(arrayMember(stopValue, 0));
+ if (!z) {
+ return Error { "function stop zoom level must be a number" };
+ }
+
+ Result<T> v = convert<T>(arrayMember(stopValue, 1));
+ if (!v) {
+ return v.error();
+ }
+
+ stops.emplace_back(*z, *v);
+ }
+
+ auto baseValue = objectMember(value, "base");
+ if (!baseValue) {
+ return Function<T>(stops, 1.0f);
+ }
+
+ optional<float> base = toNumber(*baseValue);
+ if (!base) {
+ return Error { "function base must be a number"};
+ }
+
+ return Function<T>(stops, *base);
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/geojson.hpp b/include/mbgl/style/conversion/geojson.hpp
new file mode 100644
index 0000000000..ba10b3ecc8
--- /dev/null
+++ b/include/mbgl/style/conversion/geojson.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <class V>
+Result<GeoJSON> convertGeoJSON(const V& value);
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp
new file mode 100644
index 0000000000..0539dcf9ad
--- /dev/null
+++ b/include/mbgl/style/conversion/layer.hpp
@@ -0,0 +1,208 @@
+#pragma once
+
+#include <mbgl/style/layer.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/style/conversion/make_property_setters.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <class V>
+optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const V& value) {
+ static const auto setters = makeLayoutPropertySetters<V>();
+ auto it = setters.find(name);
+ if (it == setters.end()) {
+ return Error { "property not found" };
+ }
+ return it->second(layer, value);
+}
+
+template <class V>
+optional<Error> setPaintProperty(Layer& layer, const std::string& name, const V& value, const optional<std::string>& klass) {
+ static const auto setters = makePaintPropertySetters<V>();
+ auto it = setters.find(name);
+ if (it == setters.end()) {
+ return Error { "property not found" };
+ }
+ return it->second(layer, value, klass);
+}
+
+template <class V>
+optional<Error> setPaintProperties(Layer& layer, const V& value) {
+ return eachMember(value, [&] (const std::string& paintName, const V& paintValue) -> optional<Error> {
+ if (paintName.compare(0, 5, "paint") != 0) {
+ return {};
+ }
+
+ optional<std::string> klass;
+ if (paintName.compare(0, 6, "paint.") == 0) {
+ klass = paintName.substr(6);
+ }
+
+ return eachMember(paintValue, [&] (const std::string& k, const V& v) {
+ return setPaintProperty(layer, k, v, klass);
+ });
+ });
+}
+
+template <>
+struct Converter<std::unique_ptr<Layer>> {
+public:
+ template <class V>
+ Result<std::unique_ptr<Layer>> operator()(const V& value) const {
+ if (!isObject(value)) {
+ return Error { "layer must be an object" };
+ }
+
+ auto idValue = objectMember(value, "id");
+ if (!idValue) {
+ return Error { "layer must have an id" };
+ }
+
+ optional<std::string> id = toString(*idValue);
+ if (!id) {
+ return Error { "layer id must be a string" };
+ }
+
+ auto typeValue = objectMember(value, "type");
+ if (!typeValue) {
+ return Error { "layer must have a type" };
+ }
+
+ optional<std::string> type = toString(*typeValue);
+ if (!type) {
+ return Error { "layer type must be a string" };
+ }
+
+ Result<std::unique_ptr<Layer>> converted;
+
+ if (*type == "fill") {
+ converted = convertVectorLayer<FillLayer>(*id, value);
+ } else if (*type == "line") {
+ converted = convertVectorLayer<LineLayer>(*id, value);
+ } else if (*type == "circle") {
+ converted = convertVectorLayer<CircleLayer>(*id, value);
+ } else if (*type == "symbol") {
+ converted = convertVectorLayer<SymbolLayer>(*id, value);
+ } else if (*type == "raster") {
+ converted = convertRasterLayer(*id, value);
+ } else if (*type == "background") {
+ converted = convertBackgroundLayer(*id, value);
+ } else {
+ return Error { "invalid layer type" };
+ }
+
+ if (!converted) {
+ return converted;
+ }
+
+ std::unique_ptr<Layer> layer = std::move(*converted);
+
+ auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ optional<float> minzoom = toNumber(*minzoomValue);
+ if (!minzoom) {
+ return Error { "minzoom must be numeric" };
+ }
+ layer->setMinZoom(*minzoom);
+ }
+
+ auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ optional<float> maxzoom = toNumber(*maxzoomValue);
+ if (!maxzoom) {
+ return Error { "maxzoom must be numeric" };
+ }
+ layer->setMaxZoom(*maxzoom);
+ }
+
+ auto layoutValue = objectMember(value, "layout");
+ if (layoutValue) {
+ if (!isObject(*layoutValue)) {
+ return Error { "layout must be an object" };
+ }
+ optional<Error> error = eachMember(*layoutValue, [&] (const std::string& k, const V& v) {
+ return setLayoutProperty(*layer, k, v);
+ });
+ if (error) {
+ return *error;
+ }
+ }
+
+ optional<Error> error = setPaintProperties(*layer, value);
+ if (error) {
+ return *error;
+ }
+
+ return std::move(layer);
+ }
+
+private:
+ template <class LayerType, class V>
+ Result<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const V& value) const {
+ auto sourceValue = objectMember(value, "source");
+ if (!sourceValue) {
+ return Error { "layer must have a source" };
+ }
+
+ optional<std::string> source = toString(*sourceValue);
+ if (!source) {
+ return Error { "layer source must be a string" };
+ }
+
+ std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source);
+
+ auto sourceLayerValue = objectMember(value, "source-layer");
+ if (sourceLayerValue) {
+ optional<std::string> sourceLayer = toString(*sourceLayerValue);
+ if (!sourceLayer) {
+ return Error { "layer source-layer must be a string" };
+ }
+ layer->setSourceLayer(*sourceLayer);
+ }
+
+ auto filterValue = objectMember(value, "filter");
+ if (filterValue) {
+ Result<Filter> filter = convert<Filter>(*filterValue);
+ if (!filter) {
+ return filter.error();
+ }
+ layer->setFilter(*filter);
+ }
+
+ return std::move(layer);
+ }
+
+ template <class V>
+ Result<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const V& value) const {
+ auto sourceValue = objectMember(value, "source");
+ if (!sourceValue) {
+ return Error { "layer must have a source" };
+ }
+
+ optional<std::string> source = toString(*sourceValue);
+ if (!source) {
+ return Error { "layer source must be a string" };
+ }
+
+ return std::make_unique<RasterLayer>(id, *source);
+ }
+
+ template <class V>
+ Result<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const V&) const {
+ return std::make_unique<BackgroundLayer>(id);
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp
new file mode 100644
index 0000000000..a3ba5e5d5a
--- /dev/null
+++ b/include/mbgl/style/conversion/make_property_setters.hpp
@@ -0,0 +1,136 @@
+#pragma once
+
+// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
+
+#include <mbgl/style/conversion/property_setter.hpp>
+
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+
+#include <unordered_map>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <class V>
+auto makeLayoutPropertySetters() {
+ std::unordered_map<std::string, LayoutPropertySetter<V>> result;
+
+ result["visibility"] = &setVisibility<V>;
+
+
+ result["line-cap"] = makePropertySetter<V>(&LineLayer::setLineCap);
+ result["line-join"] = makePropertySetter<V>(&LineLayer::setLineJoin);
+ result["line-miter-limit"] = makePropertySetter<V>(&LineLayer::setLineMiterLimit);
+ result["line-round-limit"] = makePropertySetter<V>(&LineLayer::setLineRoundLimit);
+
+ result["symbol-placement"] = makePropertySetter<V>(&SymbolLayer::setSymbolPlacement);
+ result["symbol-spacing"] = makePropertySetter<V>(&SymbolLayer::setSymbolSpacing);
+ result["symbol-avoid-edges"] = makePropertySetter<V>(&SymbolLayer::setSymbolAvoidEdges);
+ result["icon-allow-overlap"] = makePropertySetter<V>(&SymbolLayer::setIconAllowOverlap);
+ result["icon-ignore-placement"] = makePropertySetter<V>(&SymbolLayer::setIconIgnorePlacement);
+ result["icon-optional"] = makePropertySetter<V>(&SymbolLayer::setIconOptional);
+ result["icon-rotation-alignment"] = makePropertySetter<V>(&SymbolLayer::setIconRotationAlignment);
+ result["icon-size"] = makePropertySetter<V>(&SymbolLayer::setIconSize);
+ result["icon-text-fit"] = makePropertySetter<V>(&SymbolLayer::setIconTextFit);
+ result["icon-text-fit-padding"] = makePropertySetter<V>(&SymbolLayer::setIconTextFitPadding);
+ result["icon-image"] = makePropertySetter<V>(&SymbolLayer::setIconImage);
+ result["icon-rotate"] = makePropertySetter<V>(&SymbolLayer::setIconRotate);
+ result["icon-padding"] = makePropertySetter<V>(&SymbolLayer::setIconPadding);
+ result["icon-keep-upright"] = makePropertySetter<V>(&SymbolLayer::setIconKeepUpright);
+ result["icon-offset"] = makePropertySetter<V>(&SymbolLayer::setIconOffset);
+ result["text-pitch-alignment"] = makePropertySetter<V>(&SymbolLayer::setTextPitchAlignment);
+ result["text-rotation-alignment"] = makePropertySetter<V>(&SymbolLayer::setTextRotationAlignment);
+ result["text-field"] = makePropertySetter<V>(&SymbolLayer::setTextField);
+ result["text-font"] = makePropertySetter<V>(&SymbolLayer::setTextFont);
+ result["text-size"] = makePropertySetter<V>(&SymbolLayer::setTextSize);
+ result["text-max-width"] = makePropertySetter<V>(&SymbolLayer::setTextMaxWidth);
+ result["text-line-height"] = makePropertySetter<V>(&SymbolLayer::setTextLineHeight);
+ result["text-letter-spacing"] = makePropertySetter<V>(&SymbolLayer::setTextLetterSpacing);
+ result["text-justify"] = makePropertySetter<V>(&SymbolLayer::setTextJustify);
+ result["text-anchor"] = makePropertySetter<V>(&SymbolLayer::setTextAnchor);
+ result["text-max-angle"] = makePropertySetter<V>(&SymbolLayer::setTextMaxAngle);
+ result["text-rotate"] = makePropertySetter<V>(&SymbolLayer::setTextRotate);
+ result["text-padding"] = makePropertySetter<V>(&SymbolLayer::setTextPadding);
+ result["text-keep-upright"] = makePropertySetter<V>(&SymbolLayer::setTextKeepUpright);
+ result["text-transform"] = makePropertySetter<V>(&SymbolLayer::setTextTransform);
+ result["text-offset"] = makePropertySetter<V>(&SymbolLayer::setTextOffset);
+ result["text-allow-overlap"] = makePropertySetter<V>(&SymbolLayer::setTextAllowOverlap);
+ result["text-ignore-placement"] = makePropertySetter<V>(&SymbolLayer::setTextIgnorePlacement);
+ result["text-optional"] = makePropertySetter<V>(&SymbolLayer::setTextOptional);
+
+
+
+
+ return result;
+}
+
+template <class V>
+auto makePaintPropertySetters() {
+ std::unordered_map<std::string, PaintPropertySetter<V>> result;
+
+ result["fill-antialias"] = makePropertySetter<V>(&FillLayer::setFillAntialias);
+ result["fill-opacity"] = makePropertySetter<V>(&FillLayer::setFillOpacity);
+ result["fill-color"] = makePropertySetter<V>(&FillLayer::setFillColor);
+ result["fill-outline-color"] = makePropertySetter<V>(&FillLayer::setFillOutlineColor);
+ result["fill-translate"] = makePropertySetter<V>(&FillLayer::setFillTranslate);
+ result["fill-translate-anchor"] = makePropertySetter<V>(&FillLayer::setFillTranslateAnchor);
+ result["fill-pattern"] = makePropertySetter<V>(&FillLayer::setFillPattern);
+
+ result["line-opacity"] = makePropertySetter<V>(&LineLayer::setLineOpacity);
+ result["line-color"] = makePropertySetter<V>(&LineLayer::setLineColor);
+ result["line-translate"] = makePropertySetter<V>(&LineLayer::setLineTranslate);
+ result["line-translate-anchor"] = makePropertySetter<V>(&LineLayer::setLineTranslateAnchor);
+ result["line-width"] = makePropertySetter<V>(&LineLayer::setLineWidth);
+ result["line-gap-width"] = makePropertySetter<V>(&LineLayer::setLineGapWidth);
+ result["line-offset"] = makePropertySetter<V>(&LineLayer::setLineOffset);
+ result["line-blur"] = makePropertySetter<V>(&LineLayer::setLineBlur);
+ result["line-dasharray"] = makePropertySetter<V>(&LineLayer::setLineDasharray);
+ result["line-pattern"] = makePropertySetter<V>(&LineLayer::setLinePattern);
+
+ result["icon-opacity"] = makePropertySetter<V>(&SymbolLayer::setIconOpacity);
+ result["icon-color"] = makePropertySetter<V>(&SymbolLayer::setIconColor);
+ result["icon-halo-color"] = makePropertySetter<V>(&SymbolLayer::setIconHaloColor);
+ result["icon-halo-width"] = makePropertySetter<V>(&SymbolLayer::setIconHaloWidth);
+ result["icon-halo-blur"] = makePropertySetter<V>(&SymbolLayer::setIconHaloBlur);
+ result["icon-translate"] = makePropertySetter<V>(&SymbolLayer::setIconTranslate);
+ result["icon-translate-anchor"] = makePropertySetter<V>(&SymbolLayer::setIconTranslateAnchor);
+ result["text-opacity"] = makePropertySetter<V>(&SymbolLayer::setTextOpacity);
+ result["text-color"] = makePropertySetter<V>(&SymbolLayer::setTextColor);
+ result["text-halo-color"] = makePropertySetter<V>(&SymbolLayer::setTextHaloColor);
+ result["text-halo-width"] = makePropertySetter<V>(&SymbolLayer::setTextHaloWidth);
+ result["text-halo-blur"] = makePropertySetter<V>(&SymbolLayer::setTextHaloBlur);
+ result["text-translate"] = makePropertySetter<V>(&SymbolLayer::setTextTranslate);
+ result["text-translate-anchor"] = makePropertySetter<V>(&SymbolLayer::setTextTranslateAnchor);
+
+ result["circle-radius"] = makePropertySetter<V>(&CircleLayer::setCircleRadius);
+ result["circle-color"] = makePropertySetter<V>(&CircleLayer::setCircleColor);
+ result["circle-blur"] = makePropertySetter<V>(&CircleLayer::setCircleBlur);
+ result["circle-opacity"] = makePropertySetter<V>(&CircleLayer::setCircleOpacity);
+ result["circle-translate"] = makePropertySetter<V>(&CircleLayer::setCircleTranslate);
+ result["circle-translate-anchor"] = makePropertySetter<V>(&CircleLayer::setCircleTranslateAnchor);
+ result["circle-pitch-scale"] = makePropertySetter<V>(&CircleLayer::setCirclePitchScale);
+
+ result["raster-opacity"] = makePropertySetter<V>(&RasterLayer::setRasterOpacity);
+ result["raster-hue-rotate"] = makePropertySetter<V>(&RasterLayer::setRasterHueRotate);
+ result["raster-brightness-min"] = makePropertySetter<V>(&RasterLayer::setRasterBrightnessMin);
+ result["raster-brightness-max"] = makePropertySetter<V>(&RasterLayer::setRasterBrightnessMax);
+ result["raster-saturation"] = makePropertySetter<V>(&RasterLayer::setRasterSaturation);
+ result["raster-contrast"] = makePropertySetter<V>(&RasterLayer::setRasterContrast);
+ result["raster-fade-duration"] = makePropertySetter<V>(&RasterLayer::setRasterFadeDuration);
+
+ result["background-color"] = makePropertySetter<V>(&BackgroundLayer::setBackgroundColor);
+ result["background-pattern"] = makePropertySetter<V>(&BackgroundLayer::setBackgroundPattern);
+ result["background-opacity"] = makePropertySetter<V>(&BackgroundLayer::setBackgroundOpacity);
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp.ejs b/include/mbgl/style/conversion/make_property_setters.hpp.ejs
new file mode 100644
index 0000000000..54e3958180
--- /dev/null
+++ b/include/mbgl/style/conversion/make_property_setters.hpp.ejs
@@ -0,0 +1,47 @@
+#pragma once
+
+// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
+
+#include <mbgl/style/conversion/property_setter.hpp>
+
+<% for (const layer of locals.layers) { -%>
+#include <mbgl/style/layers/<%- layer.type %>_layer.hpp>
+<% } -%>
+
+#include <unordered_map>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <class V>
+auto makeLayoutPropertySetters() {
+ std::unordered_map<std::string, LayoutPropertySetter<V>> result;
+
+ result["visibility"] = &setVisibility<V>;
+
+<% for (const layer of locals.layers) { -%>
+<% for (const property of layer.layoutProperties) { -%>
+ result["<%- property.name %>"] = makePropertySetter<V>(&<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>);
+<% } -%>
+
+<% } -%>
+ return result;
+}
+
+template <class V>
+auto makePaintPropertySetters() {
+ std::unordered_map<std::string, PaintPropertySetter<V>> result;
+
+<% for (const layer of locals.layers) { -%>
+<% for (const property of layer.paintProperties) { -%>
+ result["<%- property.name %>"] = makePropertySetter<V>(&<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>);
+<% } -%>
+
+<% } -%>
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/property_setter.hpp b/include/mbgl/style/conversion/property_setter.hpp
new file mode 100644
index 0000000000..1a601c7c1b
--- /dev/null
+++ b/include/mbgl/style/conversion/property_setter.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <mbgl/style/layer.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+#include <mbgl/style/conversion/property_value.hpp>
+
+#include <functional>
+#include <string>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <class V>
+using LayoutPropertySetter = std::function<optional<Error> (Layer&, const V&)>;
+
+template <class V>
+using PaintPropertySetter = std::function<optional<Error> (Layer&, const V&, const optional<std::string>&)>;
+
+template <class V, class L, class T, class...Args>
+auto makePropertySetter(void (L::*setter)(PropertyValue<T>, const Args&...args)) {
+ return [setter] (Layer& layer, const V& value, const Args&...args) -> optional<Error> {
+ L* typedLayer = layer.as<L>();
+ if (!typedLayer) {
+ return Error { "layer doesn't support this property" };
+ }
+
+ Result<PropertyValue<T>> typedValue = convert<PropertyValue<T>>(value);
+ if (!typedValue) {
+ return typedValue.error();
+ }
+
+ (typedLayer->*setter)(*typedValue, args...);
+ return {};
+ };
+}
+
+template <class V>
+optional<Error> setVisibility(Layer& layer, const V& value) {
+ if (isUndefined(value)) {
+ layer.setVisibility(VisibilityType::Visible);
+ return {};
+ }
+
+ Result<VisibilityType> visibility = convert<VisibilityType>(value);
+ if (!visibility) {
+ return visibility.error();
+ }
+
+ layer.setVisibility(*visibility);
+ return {};
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp
new file mode 100644
index 0000000000..de95b56155
--- /dev/null
+++ b/include/mbgl/style/conversion/property_value.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <class T>
+struct Converter<PropertyValue<T>> {
+ template <class V>
+ Result<PropertyValue<T>> operator()(const V& value) const {
+ if (isUndefined(value)) {
+ return {};
+ } else if (isObject(value)) {
+ Result<Function<T>> function = convert<Function<T>>(value);
+ if (!function) {
+ return function.error();
+ }
+ return *function;
+ } else {
+ Result<T> constant = convert<T>(value);
+ if (!constant) {
+ return constant.error();
+ }
+ return *constant;
+ }
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp
new file mode 100644
index 0000000000..c4b2fe303f
--- /dev/null
+++ b/include/mbgl/style/conversion/source.hpp
@@ -0,0 +1,182 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/style/conversion/tileset.hpp>
+#include <mbgl/style/source.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/vector_source.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<std::unique_ptr<Source>> {
+public:
+ template <class V>
+ Result<std::unique_ptr<Source>> operator()(const V& value, const std::string& id) const {
+ if (!isObject(value)) {
+ return Error{ "source must be an object" };
+ }
+
+ auto typeValue = objectMember(value, "type");
+ if (!typeValue) {
+ return Error{ "source must have a type" };
+ }
+
+ optional<std::string> type = toString(*typeValue);
+ if (!type) {
+ return Error{ "source type must be a string" };
+ }
+
+ if (*type == "raster") {
+ return convertRasterSource(id, value);
+ } else if (*type == "vector") {
+ return convertVectorSource(id, value);
+ } else if (*type == "geojson") {
+ return convertGeoJSONSource(id, value);
+ } else {
+ return Error{ "invalid source type" };
+ }
+ }
+
+private:
+ // A tile source can either specify a URL to TileJSON, or inline TileJSON.
+ template <class V>
+ Result<variant<std::string, Tileset>> convertURLOrTileset(const V& value) const {
+ auto urlVal = objectMember(value, "url");
+ if (!urlVal) {
+ Result<Tileset> tileset = convert<Tileset>(value);
+ if (!tileset) {
+ return tileset.error();
+ }
+ return *tileset;
+ }
+
+ optional<std::string> url = toString(*urlVal);
+ if (!url) {
+ return Error{ "source url must be a string" };
+ }
+
+ return *url;
+ }
+
+ template <class V>
+ Result<std::unique_ptr<Source>> convertRasterSource(const std::string& id,
+ const V& value) const {
+ Result<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value);
+ if (!urlOrTileset) {
+ return urlOrTileset.error();
+ }
+
+ uint16_t tileSize = util::tileSize;
+ auto tileSizeValue = objectMember(value, "tileSize");
+ if (tileSizeValue) {
+ optional<float> size = toNumber(*tileSizeValue);
+ if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) {
+ return Error{ "invalid tileSize" };
+ }
+ tileSize = *size;
+ }
+
+ return std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize);
+ }
+
+ template <class V>
+ Result<std::unique_ptr<Source>> convertVectorSource(const std::string& id,
+ const V& value) const {
+ Result<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value);
+ if (!urlOrTileset) {
+ return urlOrTileset.error();
+ }
+
+ return std::make_unique<VectorSource>(id, std::move(*urlOrTileset));
+ }
+
+ template <class V>
+ Result<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
+ const V& value) const {
+ auto dataValue = objectMember(value, "data");
+ if (!dataValue) {
+ return Error{ "GeoJSON source must have a data value" };
+ }
+
+ GeoJSONOptions options;
+
+ const auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ if (toNumber(*maxzoomValue)) {
+ options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue));
+ } else {
+ return Error{ "GeoJSON source maxzoom value must be a number" };
+ }
+ }
+
+ const auto bufferValue = objectMember(value, "buffer");
+ if (bufferValue) {
+ if (toNumber(*bufferValue)) {
+ options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
+ } else {
+ return Error{ "GeoJSON source buffer value must be a number" };
+ }
+ }
+
+ const auto toleranceValue = objectMember(value, "tolerance");
+ if (toleranceValue) {
+ if (toNumber(*toleranceValue)) {
+ options.tolerance = static_cast<double>(*toNumber(*toleranceValue));
+ } else {
+ return Error{ "GeoJSON source tolerance value must be a number" };
+ }
+ }
+
+ const auto clusterValue = objectMember(value, "cluster");
+ if (clusterValue) {
+ if (toBool(*clusterValue)) {
+ options.cluster = *toBool(*clusterValue);
+ } else {
+ return Error{ "GeoJSON source cluster value must be a boolean" };
+ }
+ }
+
+ const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom");
+ if (clusterMaxZoomValue) {
+ if (toNumber(*clusterMaxZoomValue)) {
+ options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue));
+ } else {
+ return Error{ "GeoJSON source clusterMaxZoom value must be a number" };
+ }
+ }
+
+ const auto clusterRadiusValue = objectMember(value, "clusterRadius");
+ if (clusterRadiusValue) {
+ if (toNumber(*clusterRadiusValue)) {
+ options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue));
+ } else {
+ return Error{ "GeoJSON source clusterRadius value must be a number" };
+ }
+ }
+
+ auto result = std::make_unique<GeoJSONSource>(id, options);
+
+ if (isObject(*dataValue)) {
+ Result<GeoJSON> geoJSON = convertGeoJSON(*dataValue);
+ if (!geoJSON) {
+ return geoJSON.error();
+ }
+ result->setGeoJSON(std::move(*geoJSON));
+ } else if (toString(*dataValue)) {
+ result->setURL(*toString(*dataValue));
+ } else {
+ return Error{ "GeoJSON data must be a URL or an object" };
+ }
+
+ return std::move(result);
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp
new file mode 100644
index 0000000000..46425597af
--- /dev/null
+++ b/include/mbgl/style/conversion/tileset.hpp
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <mbgl/util/tileset.hpp>
+#include <mbgl/style/conversion.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<Tileset> {
+public:
+ template <class V>
+ Result<Tileset> operator()(const V& value) const {
+ Tileset result;
+
+ auto tiles = objectMember(value, "tiles");
+ if (!tiles) {
+ return Error { "source must have tiles" };
+ }
+
+ if (!isArray(*tiles)) {
+ return Error { "source tiles must be an array" };
+ }
+
+ for (std::size_t i = 0; i < arrayLength(*tiles); i++) {
+ optional<std::string> urlTemplate = toString(arrayMember(*tiles, i));
+ if (!urlTemplate) {
+ return Error { "source tiles member must be a string" };
+ }
+ result.tiles.push_back(std::move(*urlTemplate));
+ }
+
+ auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ optional<float> minzoom = toNumber(*minzoomValue);
+ if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) {
+ return Error { "invalid minzoom" };
+ }
+ result.zoomRange.min = *minzoom;
+ }
+
+ auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ optional<float> maxzoom = toNumber(*maxzoomValue);
+ if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) {
+ return Error { "invalid maxzoom" };
+ }
+ result.zoomRange.max = *maxzoom;
+ }
+
+ auto attributionValue = objectMember(value, "attribution");
+ if (attributionValue) {
+ optional<std::string> attribution = toString(*attributionValue);
+ if (!attribution) {
+ return Error { "source attribution must be a string" };
+ }
+ result.attribution = std::move(*attribution);
+ }
+
+ return result;
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp
index 9cf84f5e1d..dd2b20cd0d 100644
--- a/include/mbgl/style/filter.hpp
+++ b/include/mbgl/style/filter.hpp
@@ -2,6 +2,7 @@
#include <mbgl/util/variant.hpp>
#include <mbgl/util/feature.hpp>
+#include <mbgl/util/geometry.hpp>
#include <string>
#include <vector>
@@ -9,22 +10,7 @@
namespace mbgl {
namespace style {
-typedef variant<
- class NullFilter,
- class EqualsFilter,
- class NotEqualsFilter,
- class LessThanFilter,
- class LessThanEqualsFilter,
- class GreaterThanFilter,
- class GreaterThanEqualsFilter,
- class InFilter,
- class NotInFilter,
- class AnyFilter,
- class AllFilter,
- class NoneFilter,
- class HasFilter,
- class NotHasFilter
- > Filter;
+class Filter;
class NullFilter {};
@@ -101,5 +87,31 @@ public:
std::string key;
};
+using FilterBase = variant<
+ class NullFilter,
+ class EqualsFilter,
+ class NotEqualsFilter,
+ class LessThanFilter,
+ class LessThanEqualsFilter,
+ class GreaterThanFilter,
+ class GreaterThanEqualsFilter,
+ class InFilter,
+ class NotInFilter,
+ class AnyFilter,
+ class AllFilter,
+ class NoneFilter,
+ class HasFilter,
+ class NotHasFilter>;
+
+class Filter : public FilterBase {
+public:
+ using FilterBase::FilterBase;
+
+ bool operator()(const Feature&) const;
+
+ template <class PropertyAccessor>
+ bool operator()(FeatureType type, optional<FeatureIdentifier> id, PropertyAccessor accessor) const;
+};
+
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp
index e03beaa4d0..793abe6da0 100644
--- a/src/mbgl/style/filter_evaluator.hpp
+++ b/include/mbgl/style/filter_evaluator.hpp
@@ -1,17 +1,33 @@
#pragma once
#include <mbgl/style/filter.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/util/geometry.hpp>
#include <type_traits>
namespace mbgl {
namespace style {
+/*
+ A visitor that evaluates a `Filter` for a given feature type and properties. For maximum
+ flexibility, it is templated on the PropertyAccessor type, which must be a callable type with
+ function signature `optional<Value> (const std::string&)`, returning the value for the given
+ key, if it exists.
+
+ Use via `Filter::operator()`. For example:
+
+ if (filter(feature.getType(), [&] (const std::string& key) { return feature.getValue(key); })) {
+ // matches the filter
+ } else {
+ // does not match
+ }
+*/
+template <class PropertyAccessor>
class FilterEvaluator {
public:
- FilterEvaluator(const GeometryTileFeature& feature_)
- : feature(feature_) {}
+ const FeatureType featureType;
+ const optional<FeatureIdentifier> featureIdentifier;
+ const PropertyAccessor propertyAccessor;
bool operator()(const NullFilter&) const {
return true;
@@ -107,10 +123,20 @@ public:
}
private:
- optional<Value> getValue(const std::string& key) const {
- return key == "$type"
- ? optional<Value>(uint64_t(feature.getType()))
- : feature.getValue(key);
+ optional<Value> getValue(const std::string& key_) const {
+ if (key_ == "$type") {
+ return optional<Value>(uint64_t(featureType));
+ } else if (key_ == "$id") {
+ if (featureIdentifier) {
+ return FeatureIdentifier::visit(*featureIdentifier, [] (auto id) {
+ return Value(std::move(id));
+ });
+ } else {
+ return optional<Value>();
+ }
+ } else {
+ return propertyAccessor(key_);
+ }
}
template <class Op>
@@ -136,13 +162,24 @@ private:
return false;
}
+ bool operator()(const NullValue&,
+ const NullValue&) const {
+ // Should be unreachable; null is not currently allowed by the style specification.
+ assert(false);
+ return false;
+ }
+
bool operator()(const std::vector<Value>&,
const std::vector<Value>&) const {
+ // Should be unreachable; nested values are not currently allowed by the style specification.
+ assert(false);
return false;
}
bool operator()(const std::unordered_map<std::string, Value>&,
const std::unordered_map<std::string, Value>&) const {
+ // Should be unreachable; nested values are not currently allowed by the style specification.
+ assert(false);
return false;
}
};
@@ -155,9 +192,21 @@ private:
bool equal(const Value& lhs, const Value& rhs) const {
return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; });
}
-
- const GeometryTileFeature& feature;
};
-} // namespace mbgl
+inline bool Filter::operator()(const Feature& feature) const {
+ return operator()(apply_visitor(ToFeatureType(), feature.geometry), feature.id, [&] (const std::string& key) -> optional<Value> {
+ auto it = feature.properties.find(key);
+ if (it == feature.properties.end())
+ return {};
+ return it->second;
+ });
+}
+
+template <class PropertyAccessor>
+bool Filter::operator()(FeatureType type, optional<FeatureIdentifier> id, PropertyAccessor accessor) const {
+ return FilterBase::visit(*this, FilterEvaluator<PropertyAccessor> { type, id, accessor });
+}
+
+} // namespace style
} // namespace mbgl
diff --git a/include/mbgl/style/function.hpp b/include/mbgl/style/function.hpp
index da2659c76a..44ffa31079 100644
--- a/include/mbgl/style/function.hpp
+++ b/include/mbgl/style/function.hpp
@@ -12,8 +12,8 @@ public:
using Stop = std::pair<float, T>;
using Stops = std::vector<Stop>;
- explicit Function(const Stops& stops_, float base_)
- : base(base_), stops(stops_) {}
+ explicit Function(Stops stops_, float base_)
+ : base(base_), stops(std::move(stops_)) {}
float getBase() const { return base; }
const std::vector<std::pair<float, T>>& getStops() const { return stops; }
diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp
index 1f4a6fdf35..1eff9eb3dc 100644
--- a/include/mbgl/style/layer.hpp
+++ b/include/mbgl/style/layer.hpp
@@ -55,11 +55,6 @@ public:
float getMaxZoom() const;
void setMaxZoom(float) const;
- // Create a new layer with the specified `id` and `ref`. All other properties
- // are copied from this layer.
- std::unique_ptr<Layer> copy(const std::string& id,
- const std::string& ref) const;
-
// Private implementation
class Impl;
const std::unique_ptr<Impl> baseImpl;
diff --git a/include/mbgl/style/layers/background_layer.hpp b/include/mbgl/style/layers/background_layer.hpp
index 2eb84ee499..ac97ec2e6d 100644
--- a/include/mbgl/style/layers/background_layer.hpp
+++ b/include/mbgl/style/layers/background_layer.hpp
@@ -19,13 +19,13 @@ public:
// Paint properties
PropertyValue<Color> getBackgroundColor() const;
- void setBackgroundColor(PropertyValue<Color>);
+ void setBackgroundColor(PropertyValue<Color>, const optional<std::string>& klass = {});
PropertyValue<std::string> getBackgroundPattern() const;
- void setBackgroundPattern(PropertyValue<std::string>);
+ void setBackgroundPattern(PropertyValue<std::string>, const optional<std::string>& klass = {});
PropertyValue<float> getBackgroundOpacity() const;
- void setBackgroundOpacity(PropertyValue<float>);
+ void setBackgroundOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
// Private implementation
diff --git a/include/mbgl/style/layers/circle_layer.hpp b/include/mbgl/style/layers/circle_layer.hpp
index 10d281b6ac..873f95bcff 100644
--- a/include/mbgl/style/layers/circle_layer.hpp
+++ b/include/mbgl/style/layers/circle_layer.hpp
@@ -13,14 +13,13 @@ namespace style {
class CircleLayer : public Layer {
public:
- CircleLayer(const std::string& layerID);
+ CircleLayer(const std::string& layerID, const std::string& sourceID);
~CircleLayer() final;
// Source
-
- void setSource(const std::string& sourceID, const std::string& sourceLayer);
const std::string& getSourceID() const;
const std::string& getSourceLayer() const;
+ void setSourceLayer(const std::string& sourceLayer);
void setFilter(const Filter&);
const Filter& getFilter() const;
@@ -28,22 +27,25 @@ public:
// Paint properties
PropertyValue<float> getCircleRadius() const;
- void setCircleRadius(PropertyValue<float>);
+ void setCircleRadius(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<Color> getCircleColor() const;
- void setCircleColor(PropertyValue<Color>);
+ void setCircleColor(PropertyValue<Color>, const optional<std::string>& klass = {});
PropertyValue<float> getCircleBlur() const;
- void setCircleBlur(PropertyValue<float>);
+ void setCircleBlur(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getCircleOpacity() const;
- void setCircleOpacity(PropertyValue<float>);
+ void setCircleOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<std::array<float, 2>> getCircleTranslate() const;
- void setCircleTranslate(PropertyValue<std::array<float, 2>>);
+ void setCircleTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
PropertyValue<TranslateAnchorType> getCircleTranslateAnchor() const;
- void setCircleTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setCircleTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
+
+ PropertyValue<CirclePitchScaleType> getCirclePitchScale() const;
+ void setCirclePitchScale(PropertyValue<CirclePitchScaleType>, const optional<std::string>& klass = {});
// Private implementation
diff --git a/include/mbgl/style/layers/fill_layer.hpp b/include/mbgl/style/layers/fill_layer.hpp
index a14bf4a390..e70d67f538 100644
--- a/include/mbgl/style/layers/fill_layer.hpp
+++ b/include/mbgl/style/layers/fill_layer.hpp
@@ -13,14 +13,13 @@ namespace style {
class FillLayer : public Layer {
public:
- FillLayer(const std::string& layerID);
+ FillLayer(const std::string& layerID, const std::string& sourceID);
~FillLayer() final;
// Source
-
- void setSource(const std::string& sourceID, const std::string& sourceLayer);
const std::string& getSourceID() const;
const std::string& getSourceLayer() const;
+ void setSourceLayer(const std::string& sourceLayer);
void setFilter(const Filter&);
const Filter& getFilter() const;
@@ -28,25 +27,25 @@ public:
// Paint properties
PropertyValue<bool> getFillAntialias() const;
- void setFillAntialias(PropertyValue<bool>);
+ void setFillAntialias(PropertyValue<bool>, const optional<std::string>& klass = {});
PropertyValue<float> getFillOpacity() const;
- void setFillOpacity(PropertyValue<float>);
+ void setFillOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<Color> getFillColor() const;
- void setFillColor(PropertyValue<Color>);
+ void setFillColor(PropertyValue<Color>, const optional<std::string>& klass = {});
PropertyValue<Color> getFillOutlineColor() const;
- void setFillOutlineColor(PropertyValue<Color>);
+ void setFillOutlineColor(PropertyValue<Color>, const optional<std::string>& klass = {});
PropertyValue<std::array<float, 2>> getFillTranslate() const;
- void setFillTranslate(PropertyValue<std::array<float, 2>>);
+ void setFillTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
PropertyValue<TranslateAnchorType> getFillTranslateAnchor() const;
- void setFillTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setFillTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
PropertyValue<std::string> getFillPattern() const;
- void setFillPattern(PropertyValue<std::string>);
+ void setFillPattern(PropertyValue<std::string>, const optional<std::string>& klass = {});
// Private implementation
diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs
new file mode 100644
index 0000000000..aaae30287c
--- /dev/null
+++ b/include/mbgl/style/layers/layer.hpp.ejs
@@ -0,0 +1,75 @@
+<%
+ const type = locals.type;
+ const layoutProperties = locals.layoutProperties;
+ const paintProperties = locals.paintProperties;
+-%>
+// This file is generated. Do not edit.
+
+#pragma once
+
+#include <mbgl/style/layer.hpp>
+#include <mbgl/style/filter.hpp>
+#include <mbgl/style/property_value.hpp>
+
+#include <mbgl/util/color.hpp>
+
+<% if (type === 'line' || type === 'symbol') { -%>
+#include <vector>
+
+<% } -%>
+namespace mbgl {
+namespace style {
+
+class <%- camelize(type) %>Layer : public Layer {
+public:
+<% if (type === 'background') { -%>
+ <%- camelize(type) %>Layer(const std::string& layerID);
+<% } else { -%>
+ <%- camelize(type) %>Layer(const std::string& layerID, const std::string& sourceID);
+<% } -%>
+ ~<%- camelize(type) %>Layer() final;
+
+<% if (type !== 'background') { -%>
+ // Source
+ const std::string& getSourceID() const;
+<% if (type !== 'raster') { -%>
+ const std::string& getSourceLayer() const;
+ void setSourceLayer(const std::string& sourceLayer);
+
+ void setFilter(const Filter&);
+ const Filter& getFilter() const;
+<% } -%>
+
+<% } -%>
+<% if (layoutProperties.length) { -%>
+ // Layout properties
+
+<% for (const property of layoutProperties) { -%>
+ PropertyValue<<%- propertyType(property) %>> get<%- camelize(property.name) %>() const;
+ void set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>>);
+
+<% } -%>
+<% } -%>
+ // Paint properties
+
+<% for (const property of paintProperties) { -%>
+ PropertyValue<<%- propertyType(property) %>> get<%- camelize(property.name) %>() const;
+ void set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>>, const optional<std::string>& klass = {});
+
+<% } -%>
+ // Private implementation
+
+ class Impl;
+ Impl* const impl;
+
+ <%- camelize(type) %>Layer(const Impl&);
+ <%- camelize(type) %>Layer(const <%- camelize(type) %>Layer&) = delete;
+};
+
+template <>
+inline bool Layer::is<<%- camelize(type) %>Layer>() const {
+ return type == Type::<%- camelize(type) %>;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp
index fb9e37811a..abcb425b96 100644
--- a/include/mbgl/style/layers/line_layer.hpp
+++ b/include/mbgl/style/layers/line_layer.hpp
@@ -15,14 +15,13 @@ namespace style {
class LineLayer : public Layer {
public:
- LineLayer(const std::string& layerID);
+ LineLayer(const std::string& layerID, const std::string& sourceID);
~LineLayer() final;
// Source
-
- void setSource(const std::string& sourceID, const std::string& sourceLayer);
const std::string& getSourceID() const;
const std::string& getSourceLayer() const;
+ void setSourceLayer(const std::string& sourceLayer);
void setFilter(const Filter&);
const Filter& getFilter() const;
@@ -44,34 +43,34 @@ public:
// Paint properties
PropertyValue<float> getLineOpacity() const;
- void setLineOpacity(PropertyValue<float>);
+ void setLineOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<Color> getLineColor() const;
- void setLineColor(PropertyValue<Color>);
+ void setLineColor(PropertyValue<Color>, const optional<std::string>& klass = {});
PropertyValue<std::array<float, 2>> getLineTranslate() const;
- void setLineTranslate(PropertyValue<std::array<float, 2>>);
+ void setLineTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
PropertyValue<TranslateAnchorType> getLineTranslateAnchor() const;
- void setLineTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setLineTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
PropertyValue<float> getLineWidth() const;
- void setLineWidth(PropertyValue<float>);
+ void setLineWidth(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getLineGapWidth() const;
- void setLineGapWidth(PropertyValue<float>);
+ void setLineGapWidth(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getLineOffset() const;
- void setLineOffset(PropertyValue<float>);
+ void setLineOffset(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getLineBlur() const;
- void setLineBlur(PropertyValue<float>);
+ void setLineBlur(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<std::vector<float>> getLineDasharray() const;
- void setLineDasharray(PropertyValue<std::vector<float>>);
+ void setLineDasharray(PropertyValue<std::vector<float>>, const optional<std::string>& klass = {});
PropertyValue<std::string> getLinePattern() const;
- void setLinePattern(PropertyValue<std::string>);
+ void setLinePattern(PropertyValue<std::string>, const optional<std::string>& klass = {});
// Private implementation
diff --git a/include/mbgl/style/layers/raster_layer.hpp b/include/mbgl/style/layers/raster_layer.hpp
index 6d0c7dd91c..dea0c26bf3 100644
--- a/include/mbgl/style/layers/raster_layer.hpp
+++ b/include/mbgl/style/layers/raster_layer.hpp
@@ -13,36 +13,34 @@ namespace style {
class RasterLayer : public Layer {
public:
- RasterLayer(const std::string& layerID);
+ RasterLayer(const std::string& layerID, const std::string& sourceID);
~RasterLayer() final;
// Source
-
- void setSource(const std::string& sourceID);
const std::string& getSourceID() const;
// Paint properties
PropertyValue<float> getRasterOpacity() const;
- void setRasterOpacity(PropertyValue<float>);
+ void setRasterOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getRasterHueRotate() const;
- void setRasterHueRotate(PropertyValue<float>);
+ void setRasterHueRotate(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getRasterBrightnessMin() const;
- void setRasterBrightnessMin(PropertyValue<float>);
+ void setRasterBrightnessMin(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getRasterBrightnessMax() const;
- void setRasterBrightnessMax(PropertyValue<float>);
+ void setRasterBrightnessMax(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getRasterSaturation() const;
- void setRasterSaturation(PropertyValue<float>);
+ void setRasterSaturation(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getRasterContrast() const;
- void setRasterContrast(PropertyValue<float>);
+ void setRasterContrast(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getRasterFadeDuration() const;
- void setRasterFadeDuration(PropertyValue<float>);
+ void setRasterFadeDuration(PropertyValue<float>, const optional<std::string>& klass = {});
// Private implementation
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index 3806310c95..77b63d9b91 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -15,14 +15,13 @@ namespace style {
class SymbolLayer : public Layer {
public:
- SymbolLayer(const std::string& layerID);
+ SymbolLayer(const std::string& layerID, const std::string& sourceID);
~SymbolLayer() final;
// Source
-
- void setSource(const std::string& sourceID, const std::string& sourceLayer);
const std::string& getSourceID() const;
const std::string& getSourceLayer() const;
+ void setSourceLayer(const std::string& sourceLayer);
void setFilter(const Filter&);
const Filter& getFilter() const;
@@ -47,12 +46,18 @@ public:
PropertyValue<bool> getIconOptional() const;
void setIconOptional(PropertyValue<bool>);
- PropertyValue<RotationAlignmentType> getIconRotationAlignment() const;
- void setIconRotationAlignment(PropertyValue<RotationAlignmentType>);
+ PropertyValue<AlignmentType> getIconRotationAlignment() const;
+ void setIconRotationAlignment(PropertyValue<AlignmentType>);
PropertyValue<float> getIconSize() const;
void setIconSize(PropertyValue<float>);
+ PropertyValue<IconTextFitType> getIconTextFit() const;
+ void setIconTextFit(PropertyValue<IconTextFitType>);
+
+ PropertyValue<std::array<float, 4>> getIconTextFitPadding() const;
+ void setIconTextFitPadding(PropertyValue<std::array<float, 4>>);
+
PropertyValue<std::string> getIconImage() const;
void setIconImage(PropertyValue<std::string>);
@@ -68,8 +73,11 @@ public:
PropertyValue<std::array<float, 2>> getIconOffset() const;
void setIconOffset(PropertyValue<std::array<float, 2>>);
- PropertyValue<RotationAlignmentType> getTextRotationAlignment() const;
- void setTextRotationAlignment(PropertyValue<RotationAlignmentType>);
+ PropertyValue<AlignmentType> getTextPitchAlignment() const;
+ void setTextPitchAlignment(PropertyValue<AlignmentType>);
+
+ PropertyValue<AlignmentType> getTextRotationAlignment() const;
+ void setTextRotationAlignment(PropertyValue<AlignmentType>);
PropertyValue<std::string> getTextField() const;
void setTextField(PropertyValue<std::string>);
@@ -125,46 +133,46 @@ public:
// Paint properties
PropertyValue<float> getIconOpacity() const;
- void setIconOpacity(PropertyValue<float>);
+ void setIconOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<Color> getIconColor() const;
- void setIconColor(PropertyValue<Color>);
+ void setIconColor(PropertyValue<Color>, const optional<std::string>& klass = {});
PropertyValue<Color> getIconHaloColor() const;
- void setIconHaloColor(PropertyValue<Color>);
+ void setIconHaloColor(PropertyValue<Color>, const optional<std::string>& klass = {});
PropertyValue<float> getIconHaloWidth() const;
- void setIconHaloWidth(PropertyValue<float>);
+ void setIconHaloWidth(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getIconHaloBlur() const;
- void setIconHaloBlur(PropertyValue<float>);
+ void setIconHaloBlur(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<std::array<float, 2>> getIconTranslate() const;
- void setIconTranslate(PropertyValue<std::array<float, 2>>);
+ void setIconTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
PropertyValue<TranslateAnchorType> getIconTranslateAnchor() const;
- void setIconTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setIconTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
PropertyValue<float> getTextOpacity() const;
- void setTextOpacity(PropertyValue<float>);
+ void setTextOpacity(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<Color> getTextColor() const;
- void setTextColor(PropertyValue<Color>);
+ void setTextColor(PropertyValue<Color>, const optional<std::string>& klass = {});
PropertyValue<Color> getTextHaloColor() const;
- void setTextHaloColor(PropertyValue<Color>);
+ void setTextHaloColor(PropertyValue<Color>, const optional<std::string>& klass = {});
PropertyValue<float> getTextHaloWidth() const;
- void setTextHaloWidth(PropertyValue<float>);
+ void setTextHaloWidth(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<float> getTextHaloBlur() const;
- void setTextHaloBlur(PropertyValue<float>);
+ void setTextHaloBlur(PropertyValue<float>, const optional<std::string>& klass = {});
PropertyValue<std::array<float, 2>> getTextTranslate() const;
- void setTextTranslate(PropertyValue<std::array<float, 2>>);
+ void setTextTranslate(PropertyValue<std::array<float, 2>>, const optional<std::string>& klass = {});
PropertyValue<TranslateAnchorType> getTextTranslateAnchor() const;
- void setTextTranslateAnchor(PropertyValue<TranslateAnchorType>);
+ void setTextTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {});
// Private implementation
diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp
new file mode 100644
index 0000000000..92341066b1
--- /dev/null
+++ b/include/mbgl/style/source.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/style/types.hpp>
+
+#include <memory>
+#include <string>
+
+namespace mbgl {
+namespace style {
+
+/**
+ * The runtime representation of a [source](https://www.mapbox.com/mapbox-gl-style-spec/#sources) from the Mapbox Style
+ * Specification.
+ *
+ * `Source` is an abstract base class; concrete derived classes are provided for each source type. `Source` contains
+ * functionality that is common to all layer types:
+ *
+ * * Runtime type information: type predicates and casting
+ * * Accessors for properties common to all source types: ID, etc.
+ * * Cloning and copying
+ *
+ * All other functionality lives in the derived classes. To instantiate a source, create an instance of the desired
+ * type, passing the ID:
+ *
+ * auto vectorSource = std::make_unique<VectorSource>("my-vector-source");
+ */
+class Source : public mbgl::util::noncopyable {
+public:
+ virtual ~Source();
+
+ // Check whether this source is of the given subtype.
+ template <class T>
+ bool is() const;
+
+ // Dynamically cast this source to the given subtype.
+ template <class T>
+ T* as() {
+ return is<T>() ? reinterpret_cast<T*>(this) : nullptr;
+ }
+
+ template <class T>
+ const T* as() const {
+ return is<T>() ? reinterpret_cast<const T*>(this) : nullptr;
+ }
+
+ const std::string& getID() const;
+
+ // Create a new source with the specified `id`. All other properties
+ // are copied from this source.
+ std::unique_ptr<Source> copy(const std::string& id) const;
+
+ // Private implementation
+ class Impl;
+ const std::unique_ptr<Impl> baseImpl;
+
+protected:
+ const SourceType type;
+ Source(SourceType, std::unique_ptr<Impl>);
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp
new file mode 100644
index 0000000000..37ddce1bcc
--- /dev/null
+++ b/include/mbgl/style/sources/geojson_source.hpp
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+#include <mbgl/util/geojson.hpp>
+
+#include <mapbox/geojson.hpp>
+
+namespace mapbox {
+
+namespace geojsonvt {
+class GeoJSONVT;
+} // namespace geojsonvt
+
+namespace supercluster {
+class Supercluster;
+} // namespace supercluster
+
+} // namespace mapbox
+
+namespace mbgl {
+namespace style {
+
+using GeoJSONVTPointer = std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>;
+using SuperclusterPointer = std::unique_ptr<mapbox::supercluster::Supercluster>;
+
+struct GeoJSONOptions {
+ // GeoJSON-VT options
+ uint8_t maxzoom = 18;
+ uint16_t buffer = 128;
+ double tolerance = 0.375;
+
+ // Supercluster options
+ bool cluster = false;
+ uint16_t clusterRadius = 50;
+ uint8_t clusterMaxZoom = 17;
+};
+
+class GeoJSONSource : public Source {
+public:
+ GeoJSONSource(const std::string& id, const GeoJSONOptions options_ = GeoJSONOptions());
+
+ void setURL(const std::string& url);
+ void setGeoJSON(const GeoJSON&);
+
+ std::string getURL();
+
+ // Private implementation
+
+ class Impl;
+ Impl* const impl;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/sources/raster_source.hpp b/include/mbgl/style/sources/raster_source.hpp
new file mode 100644
index 0000000000..2d1d648eec
--- /dev/null
+++ b/include/mbgl/style/sources/raster_source.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/variant.hpp>
+
+namespace mbgl {
+namespace style {
+
+class RasterSource : public Source {
+public:
+ RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize);
+
+ // Private implementation
+
+ class Impl;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/sources/vector_source.hpp b/include/mbgl/style/sources/vector_source.hpp
new file mode 100644
index 0000000000..3d53362734
--- /dev/null
+++ b/include/mbgl/style/sources/vector_source.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/variant.hpp>
+
+namespace mbgl {
+namespace style {
+
+class VectorSource : public Source {
+public:
+ VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset);
+
+ // Private implementation
+
+ class Impl;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/transition_options.hpp b/include/mbgl/style/transition_options.hpp
index 87faff21f8..d7a6633f0c 100644
--- a/include/mbgl/style/transition_options.hpp
+++ b/include/mbgl/style/transition_options.hpp
@@ -6,13 +6,9 @@
namespace mbgl {
namespace style {
-class TransitionOptions {
-public:
- TransitionOptions(const optional<Duration>& duration_ = {}, const optional<Duration>& delay_ = {})
- : duration(duration_), delay(delay_) {}
-
- optional<Duration> duration;
- optional<Duration> delay;
+struct TransitionOptions {
+ optional<Duration> duration = {};
+ optional<Duration> delay = {};
};
} // namespace style
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp
index 27b524a800..34e5642ec6 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include <mbgl/util/enum.hpp>
+#include <cstdint>
namespace mbgl {
@@ -13,14 +13,6 @@ enum class SourceType : uint8_t {
Annotations
};
-MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, {
- { SourceType::Vector, "vector" },
- { SourceType::Raster, "raster" },
- { SourceType::GeoJSON, "geojson" },
- { SourceType::Video, "video" },
- { SourceType::Annotations, "annotations" },
-});
-
namespace style {
enum class VisibilityType : bool {
@@ -53,14 +45,20 @@ enum class RotateAnchorType : bool {
Viewport,
};
+enum class CirclePitchScaleType : bool {
+ Map,
+ Viewport,
+};
+
enum class SymbolPlacementType : bool {
Point,
Line,
};
-enum class RotationAlignmentType : bool {
+enum class AlignmentType : uint8_t {
Map,
Viewport,
+ Undefined,
};
enum class TextJustifyType : uint8_t {
@@ -87,5 +85,12 @@ enum class TextTransformType : uint8_t {
Lowercase,
};
+enum class IconTextFitType : uint8_t {
+ None,
+ Both,
+ Width,
+ Height
+};
+
} // namespace style
} // namespace mbgl
diff --git a/include/mbgl/util/char_array_buffer.hpp b/include/mbgl/util/char_array_buffer.hpp
new file mode 100644
index 0000000000..177f005477
--- /dev/null
+++ b/include/mbgl/util/char_array_buffer.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <streambuf>
+
+namespace mbgl {
+namespace util {
+
+// ref https://artofcode.wordpress.com/2010/12/12/deriving-from-stdstreambuf/
+
+class CharArrayBuffer : public std::streambuf
+{
+public:
+ CharArrayBuffer(char const* data, std::size_t size)
+ : begin_(data), end_(data + size), current_(data) {}
+
+private:
+ int_type underflow() final {
+ if (current_ == end_) {
+ return traits_type::eof();
+ }
+ return traits_type::to_int_type(*current_);
+ }
+
+ int_type uflow() final {
+ if (current_ == end_) {
+ return traits_type::eof();
+ }
+ return traits_type::to_int_type(*current_++);
+ }
+
+ int_type pbackfail(int_type ch) final {
+ if (current_ == begin_ || (ch != traits_type::eof() && ch != current_[-1])) {
+ return traits_type::eof();
+ }
+ return traits_type::to_int_type(*--current_);
+ }
+
+ std::streamsize showmanyc() final {
+ return end_ - current_;
+ }
+
+ pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode) final {
+ if (dir == std::ios_base::beg) current_ = std::min(begin_ + off, end_);
+ else if (dir == std::ios_base::cur) current_ = std::min(current_ + off, end_);
+ else current_ = std::max(end_ - off, begin_); // dir == std::ios_base::end
+ return pos_type(off_type(current_ - begin_));
+ }
+
+ char const * const begin_;
+ char const * const end_;
+ char const * current_;
+};
+
+} // namespace util
+} // namespace mbgl
diff --git a/include/mbgl/util/color.hpp b/include/mbgl/util/color.hpp
index d7fe61c640..4be380fde3 100644
--- a/include/mbgl/util/color.hpp
+++ b/include/mbgl/util/color.hpp
@@ -1,10 +1,40 @@
#pragma once
-#include <array>
+#include <mbgl/util/optional.hpp>
+
+#include <string>
namespace mbgl {
// Stores a premultiplied color, with all four channels ranging from 0..1
-using Color = std::array<float, 4>;
+class Color {
+public:
+ float r = 0.0f;
+ float g = 0.0f;
+ float b = 0.0f;
+ float a = 0.0f;
+
+ static constexpr Color black() { return { 0.0f, 0.0f, 0.0f, 1.0f }; };
+ static constexpr Color white() { return { 1.0f, 1.0f, 1.0f, 1.0f }; };
+
+ static optional<Color> parse(const std::string&);
+};
+
+constexpr bool operator==(const Color& colorA, const Color& colorB) {
+ return colorA.r == colorB.r && colorA.g == colorB.g && colorA.b == colorB.b && colorA.a == colorB.a;
+}
+
+constexpr bool operator!=(const Color& colorA, const Color& colorB) {
+ return !(colorA == colorB);
+}
+
+constexpr Color operator*(const Color& color, float alpha) {
+ return {
+ color.r * alpha,
+ color.g * alpha,
+ color.b * alpha,
+ color.a * alpha
+ };
+}
} // namespace mbgl
diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp
index 621fc1820e..3c0b3eb93e 100644
--- a/include/mbgl/util/constants.hpp
+++ b/include/mbgl/util/constants.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/unitbezier.hpp>
#include <cmath>
#include <string>
@@ -17,7 +18,7 @@ constexpr float tileSize = 512;
* In practice, all features are converted to this extent before being added.
*
* Positions are stored as signed 16bit integers.
- * One bit is lost for signedness to support featuers extending past the left edge of the tile.
+ * 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 to support features extending past the extent on the right edge of the tile.
* This leaves us with 2^13 = 8192
@@ -35,11 +36,13 @@ constexpr double PITCH_MAX = M_PI / 3;
constexpr double MIN_ZOOM = 0.0;
constexpr double MAX_ZOOM = 25.5;
-constexpr uint64_t DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024;;
+constexpr uint64_t DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024;
constexpr Duration DEFAULT_FADE_DURATION = Milliseconds(300);
constexpr Seconds CLOCK_SKEW_RETRY_TIMEOUT { 30 };
+constexpr UnitBezier DEFAULT_TRANSITION_EASE = { 0, 0, 0.25, 1 };
+
} // namespace util
namespace debug {
diff --git a/include/mbgl/util/convert.hpp b/include/mbgl/util/convert.hpp
new file mode 100644
index 0000000000..c2b3d9950d
--- /dev/null
+++ b/include/mbgl/util/convert.hpp
@@ -0,0 +1,17 @@
+#include <array>
+#include <type_traits>
+#include <utility>
+
+namespace mbgl {
+namespace util {
+
+template<typename To, typename From, std::size_t Size,
+ typename = std::enable_if_t<std::is_convertible<From, To>::value>>
+constexpr std::array<To, Size> convert(const std::array<From, Size>&from) {
+ std::array<To, Size> to {};
+ std::copy(std::begin(from), std::end(from), std::begin(to));
+ return to;
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/include/mbgl/util/enum.hpp b/include/mbgl/util/enum.hpp
index 3fbf313aed..48ffda463e 100644
--- a/include/mbgl/util/enum.hpp
+++ b/include/mbgl/util/enum.hpp
@@ -1,52 +1,36 @@
#pragma once
-#include <iosfwd>
+#include <mbgl/util/optional.hpp>
+
+#include <algorithm>
+#include <cassert>
#include <string>
namespace mbgl {
-template <typename Type>
-struct EnumValue {
- const Type value;
- const char *name;
-};
-
-template <typename EnumName, const EnumValue<EnumName> *names, const size_t length>
-struct Enum {
- using Type = EnumName;
- Type value;
- static const constexpr size_t l = length;
-private:
- static constexpr inline bool compare(const char *a, const char *b) {
- return *a == *b && (*a == '\0' || compare(a + 1, b + 1));
- }
- static constexpr inline const char *lookup_type(Type e, EnumValue<Type> const * const list, size_t r) {
- return r == 0 ? "" : list->value == e ? list->name : lookup_type(e, list + 1, r - 1);
- }
- static constexpr inline Type lookup_name(const char *n, EnumValue<Type> const * const list, size_t r) {
- return r == 0 ? Type(-1) : compare(list->name, n) ? list->value : lookup_name(n, list + 1, r - 1);
- }
+template <typename T>
+class Enum {
public:
- inline constexpr Enum(const char *n) : value(lookup_name(n, names, length)) {}
- inline constexpr Enum(const std::string &n) : value(lookup_name(n.c_str(), names, length)) {}
- inline constexpr Enum(Type t) : value(t) {}
-
- inline void operator=(const char *n) { value = lookup_name(n, names, length); }
- inline void operator=(const std::string &n) { *this = n.c_str(); }
- inline void operator=(Type t) { value = t; }
+ using Value = std::pair<const T, const char *>;
- inline constexpr bool valid() const { return value != Type(-1); }
+ static const char * toString(T t) {
+ auto it = std::find_if(begin, end, [&] (const auto& v) { return t == v.first; });
+ assert(it != end); return it->second;
+ }
- inline constexpr const char *c_str() const { return lookup_type(value, names, length); }
- inline std::string str() const { return c_str(); }
+ static optional<T> toEnum(const std::string& s) {
+ auto it = std::find_if(begin, end, [&] (const auto& v) { return s == v.second; });
+ return it == end ? optional<T>() : it->first;
+ }
- inline constexpr operator Type() const { return value; }
+private:
+ static const Value* begin;
+ static const Value* end;
};
-#define MBGL_DEFINE_ENUM_CLASS(name, type, strings...) \
- const constexpr ::mbgl::EnumValue<type> type##_names[] = strings; \
- using name = ::mbgl::Enum<type, type##_names, sizeof(type##_names) / sizeof(::mbgl::EnumValue<type>)>; \
- inline std::ostream& operator<<(std::ostream& os, type t) { return os << name(t).str(); }
+#define MBGL_DEFINE_ENUM(type, strings...) \
+const constexpr Enum<type>::Value type##_names[] = strings; \
+template <> const Enum<type>::Value* Enum<type>::begin = std::begin(type##_names); \
+template <> const Enum<type>::Value* Enum<type>::end = std::end(type##_names)
} // namespace mbgl
-
diff --git a/include/mbgl/util/exception.hpp b/include/mbgl/util/exception.hpp
index b73a94fcd2..7c331636d4 100644
--- a/include/mbgl/util/exception.hpp
+++ b/include/mbgl/util/exception.hpp
@@ -6,23 +6,23 @@ namespace mbgl {
namespace util {
struct Exception : std::runtime_error {
- inline Exception(const char *msg) : std::runtime_error(msg) {}
- inline Exception(const std::string &msg) : std::runtime_error(msg) {}
+ Exception(const char *msg) : std::runtime_error(msg) {}
+ Exception(const std::string &msg) : std::runtime_error(msg) {}
};
struct SpriteImageException : Exception {
- inline SpriteImageException(const char *msg) : Exception(msg) {}
- inline SpriteImageException(const std::string &msg) : Exception(msg) {}
+ SpriteImageException(const char *msg) : Exception(msg) {}
+ SpriteImageException(const std::string &msg) : Exception(msg) {}
};
struct MisuseException : Exception {
- inline MisuseException(const char *msg) : Exception(msg) {}
- inline MisuseException(const std::string &msg) : Exception(msg) {}
+ MisuseException(const char *msg) : Exception(msg) {}
+ MisuseException(const std::string &msg) : Exception(msg) {}
};
struct ShaderException : Exception {
- inline ShaderException(const char *msg) : Exception(msg) {}
- inline ShaderException(const std::string &msg) : Exception(msg) {}
+ ShaderException(const char *msg) : Exception(msg) {}
+ ShaderException(const std::string &msg) : Exception(msg) {}
};
} // namespace util
diff --git a/include/mbgl/util/feature.hpp b/include/mbgl/util/feature.hpp
index 7747d34ee9..b72aa15ddd 100644
--- a/include/mbgl/util/feature.hpp
+++ b/include/mbgl/util/feature.hpp
@@ -7,13 +7,9 @@
namespace mbgl {
using Value = mapbox::geometry::value;
-
-class Feature : public mapbox::geometry::feature<double> {
-public:
- Feature(geometry_type&& geometry_)
- : mapbox::geometry::feature<double> { std::move(geometry_) } {}
-
- optional<uint64_t> id;
-};
+using NullValue = mapbox::geometry::null_value_t;
+using PropertyMap = mapbox::geometry::property_map;
+using FeatureIdentifier = mapbox::geometry::identifier;
+using Feature = mapbox::geometry::feature<double>;
} // namespace mbgl
diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp
index 7e15d5c2f1..9dca10eb84 100644
--- a/include/mbgl/util/geo.hpp
+++ b/include/mbgl/util/geo.hpp
@@ -57,18 +57,18 @@ public:
LatLng(const UnwrappedTileID& id);
};
-inline bool operator==(const LatLng& a, const LatLng& b) {
+constexpr bool operator==(const LatLng& a, const LatLng& b) {
return a.latitude == b.latitude && a.longitude == b.longitude;
}
-inline bool operator!=(const LatLng& a, const LatLng& b) {
+constexpr bool operator!=(const LatLng& a, const LatLng& b) {
return !(a == b);
}
class ProjectedMeters {
public:
- double northing = 0;
- double easting = 0;
+ double northing;
+ double easting;
ProjectedMeters(double n = 0, double e = 0)
: northing(n), easting(e) {}
@@ -78,6 +78,10 @@ public:
}
};
+constexpr bool operator==(const ProjectedMeters& a, const ProjectedMeters& b) {
+ return a.northing == b.northing && a.easting == b.easting;
+}
+
class LatLngBounds {
public:
// Return a bounds covering the entire (unwrapped) world.
@@ -157,18 +161,18 @@ private:
LatLng sw;
LatLng ne;
- LatLngBounds(const LatLng& sw_, const LatLng& ne_)
- : sw(sw_), ne(ne_) {}
+ LatLngBounds(LatLng sw_, LatLng ne_)
+ : sw(std::move(sw_)), ne(std::move(ne_)) {}
- friend bool operator==(const LatLngBounds&, const LatLngBounds&);
- friend bool operator!=(const LatLngBounds&, const LatLngBounds&);
+ friend constexpr bool operator==(const LatLngBounds&, const LatLngBounds&);
+ friend constexpr bool operator!=(const LatLngBounds&, const LatLngBounds&);
};
-inline bool operator==(const LatLngBounds& a, const LatLngBounds& b) {
+constexpr bool operator==(const LatLngBounds& a, const LatLngBounds& b) {
return a.sw == b.sw && a.ne == b.ne;
}
-inline bool operator!=(const LatLngBounds& a, const LatLngBounds& b) {
+constexpr bool operator!=(const LatLngBounds& a, const LatLngBounds& b) {
return !(a == b);
}
@@ -187,12 +191,12 @@ public:
double left = 0; ///< Number of pixels inset from the left edge.
double bottom = 0; ///< Number of pixels inset from the bottom edge.
double right = 0; ///< Number of pixels inset from the right edge.
-
+
EdgeInsets() {}
-
+
EdgeInsets(const double t, const double l, const double b, const double r)
: top(t), left(l), bottom(b), right(r) {}
-
+
explicit operator bool() const {
return !(std::isnan(top) || std::isnan(left) || std::isnan(bottom) || std::isnan(right))
&& (top || left || bottom || right);
@@ -210,7 +214,7 @@ public:
top + o.top, left + o.left, bottom + o.bottom, right + o.right,
};
}
-
+
ScreenCoordinate getCenter(uint16_t width, uint16_t height) const;
};
diff --git a/include/mbgl/util/geojson.hpp b/include/mbgl/util/geojson.hpp
new file mode 100644
index 0000000000..b4e789a3ac
--- /dev/null
+++ b/include/mbgl/util/geojson.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <mapbox/geojson.hpp>
+
+namespace mbgl {
+
+using GeoJSON = mapbox::geojson::geojson;
+using FeatureCollection = mapbox::geojson::feature_collection;
+
+} // namespace mbgl
diff --git a/include/mbgl/util/geometry.hpp b/include/mbgl/util/geometry.hpp
index 6b9c332bf2..6dc16bc514 100644
--- a/include/mbgl/util/geometry.hpp
+++ b/include/mbgl/util/geometry.hpp
@@ -5,6 +5,13 @@
namespace mbgl {
+enum class FeatureType : uint8_t {
+ Unknown = 0,
+ Point = 1,
+ LineString = 2,
+ Polygon = 3
+};
+
template <class T>
using Point = mapbox::geometry::point<T>;
@@ -34,4 +41,21 @@ Point<S> convertPoint(const Point<T>& p) {
return Point<S>(p.x, p.y);
}
+struct ToFeatureType {
+ template <class T>
+ FeatureType operator()(const Point<T> &) const { return FeatureType::Point; }
+ template <class T>
+ FeatureType operator()(const MultiPoint<T> &) const { return FeatureType::Point; }
+ template <class T>
+ FeatureType operator()(const LineString<T> &) const { return FeatureType::LineString; }
+ template <class T>
+ FeatureType operator()(const MultiLineString<T> &) const { return FeatureType::LineString; }
+ template <class T>
+ FeatureType operator()(const Polygon<T> &) const { return FeatureType::Polygon; }
+ template <class T>
+ FeatureType operator()(const MultiPolygon<T> &) const { return FeatureType::Polygon; }
+ template <class T>
+ FeatureType operator()(const mapbox::geometry::geometry_collection<T> &) const { return FeatureType::Unknown; }
+};
+
} // namespace mbgl
diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp
index 8e1c994657..eb45088580 100644
--- a/include/mbgl/util/projection.hpp
+++ b/include/mbgl/util/projection.hpp
@@ -11,30 +11,32 @@ namespace mbgl {
class Projection {
public:
- static inline double getMetersPerPixelAtLatitude(double lat, double zoom) {
- const double mapPixelWidthAtZoom = std::pow(2.0, zoom) * util::tileSize;
+ static double getMetersPerPixelAtLatitude(double lat, double zoom) {
+ const double constrainedZoom = util::clamp(zoom, util::MIN_ZOOM, util::MAX_ZOOM);
+ const double mapPixelWidthAtZoom = std::pow(2.0, constrainedZoom) * util::tileSize;
const double constrainedLatitude = util::clamp(lat, -util::LATITUDE_MAX, util::LATITUDE_MAX);
-
return std::cos(constrainedLatitude * util::DEG2RAD) * util::M2PI * util::EARTH_RADIUS_M / mapPixelWidthAtZoom;
}
- static inline ProjectedMeters projectedMetersForLatLng(const LatLng& latLng) {
+ static ProjectedMeters projectedMetersForLatLng(const LatLng& latLng) {
const double constrainedLatitude = util::clamp(latLng.latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX);
+ const double constrainedLongitude = util::clamp(latLng.longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX);
const double m = 1 - 1e-15;
const double f = util::clamp(std::sin(util::DEG2RAD * constrainedLatitude), -m, m);
- const double easting = util::EARTH_RADIUS_M * latLng.longitude * util::DEG2RAD;
+ const double easting = util::EARTH_RADIUS_M * constrainedLongitude * util::DEG2RAD;
const double northing = 0.5 * util::EARTH_RADIUS_M * std::log((1 + f) / (1 - f));
return ProjectedMeters(northing, easting);
}
- static inline LatLng latLngForProjectedMeters(const ProjectedMeters& projectedMeters) {
+ static LatLng latLngForProjectedMeters(const ProjectedMeters& projectedMeters) {
double latitude = (2 * std::atan(std::exp(projectedMeters.northing / util::EARTH_RADIUS_M)) - (M_PI / 2)) * util::RAD2DEG;
double longitude = projectedMeters.easting * util::RAD2DEG / util::EARTH_RADIUS_M;
latitude = util::clamp(latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX);
+ longitude = util::clamp(longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX);
return LatLng(latitude, longitude);
}
diff --git a/include/mbgl/util/range.hpp b/include/mbgl/util/range.hpp
new file mode 100644
index 0000000000..8da2dd45bb
--- /dev/null
+++ b/include/mbgl/util/range.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+namespace mbgl {
+
+template <class T>
+class Range {
+public:
+ Range(const T& min_, const T& max_)
+ : min(min_), max(max_) {}
+
+ T min;
+ T max;
+};
+
+template <class T>
+bool operator==(const Range<T>& a, const Range<T>& b) {
+ return a.min == b.min && a.max == b.max;
+}
+
+template <class T>
+bool operator!=(const Range<T>& a, const Range<T>& b) {
+ return !(a == b);
+}
+
+} // namespace mbgl
diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp
index 56965c97e6..25f07c0312 100644
--- a/include/mbgl/util/run_loop.hpp
+++ b/include/mbgl/util/run_loop.hpp
@@ -1,11 +1,11 @@
#pragma once
-#include <mbgl/util/atomic.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/work_task.hpp>
#include <mbgl/util/work_request.hpp>
+#include <atomic>
#include <functional>
#include <utility>
#include <queue>
@@ -59,7 +59,7 @@ public:
template <class Fn, class... Args>
std::unique_ptr<AsyncRequest>
invokeCancellable(Fn&& fn, Args&&... args) {
- auto flag = std::make_shared<util::Atomic<bool>>();
+ auto flag = std::make_shared<std::atomic<bool>>();
*flag = false;
auto tuple = std::make_tuple(std::move(args)...);
@@ -77,7 +77,7 @@ public:
template <class Fn, class Cb, class... Args>
std::unique_ptr<AsyncRequest>
invokeWithCallback(Fn&& fn, Cb&& callback, Args&&... args) {
- auto flag = std::make_shared<util::Atomic<bool>>();
+ auto flag = std::make_shared<std::atomic<bool>>();
*flag = false;
// Create a lambda L1 that invokes another lambda L2 on the current RunLoop R, that calls
@@ -114,7 +114,7 @@ private:
template <class F, class P>
class Invoker : public WorkTask {
public:
- Invoker(F&& f, P&& p, std::shared_ptr<util::Atomic<bool>> canceled_ = nullptr)
+ Invoker(F&& f, P&& p, std::shared_ptr<std::atomic<bool>> canceled_ = nullptr)
: canceled(std::move(canceled_)),
func(std::move(f)),
params(std::move(p)) {
@@ -148,7 +148,7 @@ private:
}
std::recursive_mutex mutex;
- std::shared_ptr<util::Atomic<bool>> canceled;
+ std::shared_ptr<std::atomic<bool>> canceled;
F func;
P params;
diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp
new file mode 100644
index 0000000000..8a7fbe9b73
--- /dev/null
+++ b/include/mbgl/util/tileset.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mbgl/util/range.hpp>
+
+#include <vector>
+#include <string>
+#include <cstdint>
+
+namespace mbgl {
+
+class Tileset {
+public:
+ std::vector<std::string> tiles;
+ Range<uint8_t> zoomRange { 0, 22 };
+ std::string attribution;
+
+ // TileJSON also includes center, zoom, and bounds, but they are not used by mbgl.
+};
+
+} // namespace mbgl
diff --git a/include/mbgl/util/traits.hpp b/include/mbgl/util/traits.hpp
index 7c9499897e..9d6f947cd2 100644
--- a/include/mbgl/util/traits.hpp
+++ b/include/mbgl/util/traits.hpp
@@ -5,8 +5,8 @@
namespace mbgl {
template<typename T>
-constexpr auto underlying_type(T t) -> typename std::underlying_type<T>::type {
- return static_cast<typename std::underlying_type<T>::type>(t);
+constexpr auto underlying_type(T t) -> typename std::underlying_type_t<T> {
+ return typename std::underlying_type_t<T>(t);
}
} // namespace mbgl
diff --git a/include/mbgl/util/unitbezier.hpp b/include/mbgl/util/unitbezier.hpp
index ce3e78f3cc..3a4994917b 100644
--- a/include/mbgl/util/unitbezier.hpp
+++ b/include/mbgl/util/unitbezier.hpp
@@ -31,32 +31,31 @@ namespace mbgl {
namespace util {
struct UnitBezier {
- UnitBezier(double p1x, double p1y, double p2x, double p2y) {
- // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
- cx = 3.0 * p1x;
- bx = 3.0 * (p2x - p1x) - cx;
- ax = 1.0 - cx - bx;
-
- cy = 3.0 * p1y;
- by = 3.0 * (p2y - p1y) - cy;
- ay = 1.0 - cy - by;
+ // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
+ constexpr UnitBezier(double p1x, double p1y, double p2x, double p2y)
+ : cx(3.0 * p1x)
+ , bx(3.0 * (p2x - p1x) - cx)
+ , ax(1.0 - cx - bx)
+ , cy(3.0 * p1y)
+ , by(3.0 * (p2y - p1y) - cy)
+ , ay(1.0 - cy - by) {
}
- double sampleCurveX(double t) {
+ double sampleCurveX(double t) const {
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
return ((ax * t + bx) * t + cx) * t;
}
- double sampleCurveY(double t) {
+ double sampleCurveY(double t) const {
return ((ay * t + by) * t + cy) * t;
}
- double sampleCurveDerivativeX(double t) {
+ double sampleCurveDerivativeX(double t) const {
return (3.0 * ax * t + 2.0 * bx) * t + cx;
}
// Given an x value, find a parametric value it came from.
- double solveCurveX(double x, double epsilon) {
+ double solveCurveX(double x, double epsilon) const {
double t0;
double t1;
double t2;
@@ -100,18 +99,18 @@ struct UnitBezier {
return t2;
}
- double solve(double x, double epsilon) {
+ double solve(double x, double epsilon) const {
return sampleCurveY(solveCurveX(x, epsilon));
}
private:
- double ax;
- double bx;
- double cx;
+ const double cx;
+ const double bx;
+ const double ax;
- double ay;
- double by;
- double cy;
+ const double cy;
+ const double by;
+ const double ay;
};
} // namespace util
diff --git a/mbgl.gypi b/mbgl.gypi
index d386f4920b..7f31d4619a 100644
--- a/mbgl.gypi
+++ b/mbgl.gypi
@@ -108,6 +108,8 @@
'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
'DEAD_CODE_STRIPPING': 'NO',
},
+ }, 'OS=="android"', {
+ 'cflags_cc': [ '-g', '-Os', '-fvisibility=hidden' ],
}, {
'cflags_cc': [ '-g', '-O3' ],
}],
@@ -125,26 +127,17 @@
},
'targets': [
{
- 'target_name': 'core',
- 'product_name': 'mbgl-core',
- 'type': 'static_library',
- 'standalone_static_library': 1,
- 'hard_dependency': 1,
+ 'target_name': 'headers',
+ 'type': 'none',
'sources': [
- '<!@(find <(DEPTH)/src -name "*.hpp")',
- '<!@(find <(DEPTH)/src -name "*.cpp")',
- '<!@(find <(DEPTH)/src -name "*.c")',
- '<!@(find <(DEPTH)/src -name "*.h")',
- '<!@(find <(DEPTH)/include -name "*.hpp")',
- '<!@(find <(DEPTH)/include -name "*.h")',
'<!@(find -H <(DEPTH)/node_modules/mapbox-gl-shaders -name "*.glsl")',
'<(SHARED_INTERMEDIATE_DIR)/include/mbgl/util/version.hpp',
],
'rules': [
{
- 'rule_name': 'Build Shaders',
+ 'rule_name': 'shaders',
'message': 'Building shader',
'extension': 'glsl',
'inputs': [ '<(DEPTH)/scripts/build-shaders.py' ],
@@ -156,17 +149,43 @@
'actions': [
{
- 'action_name': 'Build Version Header',
+ 'action_name': 'version',
+ 'message': 'Bulding version header',
'inputs': [ '<(DEPTH)/scripts/build-version.py', ],
'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/include/mbgl/util/version.hpp', ],
'action': [ '<@(_inputs)', '<(SHARED_INTERMEDIATE_DIR)' ],
- }
+ },
+ ],
+
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/include',
+ ],
+ },
+ },
+ {
+ 'target_name': 'core',
+ 'product_name': 'mbgl-core',
+ 'type': 'static_library',
+ 'standalone_static_library': 1,
+ 'hard_dependency': 1,
+
+ 'dependencies': [
+ 'headers',
+ ],
+
+ 'sources': [
+ '<!@(find <(DEPTH)/src -name "*.hpp")',
+ '<!@(find <(DEPTH)/src -name "*.cpp")',
+ '<!@(find <(DEPTH)/src -name "*.c")',
+ '<!@(find <(DEPTH)/src -name "*.h")',
+ '<!@(find <(DEPTH)/include -name "*.hpp")',
+ '<!@(find <(DEPTH)/include -name "*.h")',
],
'include_dirs': [
'include',
'src',
- '<(SHARED_INTERMEDIATE_DIR)/include',
],
'variables': {
@@ -175,7 +194,10 @@
'<@(protozero_cflags)',
'<@(boost_cflags)',
'<@(geometry_cflags)',
+ '<@(geojson_cflags)',
'<@(geojsonvt_cflags)',
+ '<@(supercluster_cflags)',
+ '<@(kdbush_cflags)',
'<@(rapidjson_cflags)',
'<@(variant_cflags)',
'<@(earcut_cflags)',
@@ -190,7 +212,7 @@
'<@(opengl_ldflags)',
],
'libraries': [
- '<@(geojsonvt_static_libs)',
+ '<@(geojson_static_libs)',
],
},
@@ -293,12 +315,14 @@
'cflags_cc': [
'<@(variant_cflags)',
'<@(geometry_cflags)',
+ '<@(geojson_cflags)',
'<@(unique_resource_cflags)',
],
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [
'<@(variant_cflags)',
'<@(geometry_cflags)',
+ '<@(geojson_cflags)',
'<@(unique_resource_cflags)',
],
},
diff --git a/package.json b/package.json
index 0b7f7e2d1b..5a96d0defd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mapbox-gl-native",
- "version": "3.2.1",
+ "version": "3.3.2",
"description": "Renders map tiles with Mapbox GL",
"keywords": [
"mapbox",
@@ -21,9 +21,9 @@
"csscolorparser": "^1.0.2",
"ejs": "^2.4.1",
"express": "^4.11.1",
- "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#8dfd9653e6659cb2837a4b98f3e8e1d559b29a09",
- "mapbox-gl-style-spec": "^8.5.1",
- "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#f45fd7aba98650c7f3bf778c9cbbfd3b548a4ee8",
+ "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#4d1f89514bf03536c8e682439df165c33a37122a",
+ "mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#83b1a3e5837d785af582efd5ed1a212f2df6a4ae",
+ "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#8e38705747dee1279a8503d05593543eb9af3ce1",
"node-gyp": "^3.3.1",
"request": "^2.72.0",
"tape": "^4.5.1"
@@ -35,6 +35,7 @@
"preinstall": "npm install node-pre-gyp",
"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"
},
"gypfile": true,
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md
index f73c5ba198..b5524132f6 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -2,13 +2,56 @@
Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
-## 4.0.1
+## 4.1.1 - July 19, 2016
+
+Mapbox Android 4.1.1 is a patch release and is recommended to upgrade to it as soon as possible.
+
+* Default Styles Caching Crash ([#5722](https://github.com/mapbox/mapbox-gl-native/issues/5722))
+
+## 4.1.0 - June 29, 2016
+
+Mapbox Android 4.1.0 builds off our ambitious 4.0.0 version with 3 major new features being released.
+
+* View Based Annotations ([#3276](https://github.com/mapbox/mapbox-gl-native/issues/3276))
+* UserLocationView Refactor ([#4396](https://github.com/mapbox/mapbox-gl-native/issues/4396))
+* MapboxAccountManager ([#5004](https://github.com/mapbox/mapbox-gl-native/issues/5004))
+
+## 4.1.0-beta.3 - June 20, 2016
+
+* New samples:
+ * [Location picker](https://github.com/mapbox/mapbox-gl-native/pull/5391)
+ * [Animate and rotate multiple markers](https://github.com/mapbox/mapbox-gl-native/issues/5299)
+ * [Scaling marker activity](https://github.com/mapbox/mapbox-gl-native/issues/5409)
+* Marker improvements:
+ * [Expose MarkerView alpha](https://github.com/mapbox/mapbox-gl-native/pull/5329)
+ * [Icon should be optional for MarkerView](https://github.com/mapbox/mapbox-gl-native/pull/5328)
+ * [Expose an API to enable selection/deselection of markers on a map tap](https://github.com/mapbox/mapbox-gl-native/pull/5312)
+ * [Bring selected MarkerView to the front](https://github.com/mapbox/mapbox-gl-native/pull/5294)
+* [Make gesture focal point configurable](https://github.com/mapbox/mapbox-gl-native/pull/5332)
+
+## 4.1.0-beta.2 - June 8, 2016
+
+* Dynamically Update InfoWindow ([#5237](https://github.com/mapbox/mapbox-gl-native/issues/5237))
+* armeabi ABI Work On armv7 Devices ([#3985](https://github.com/mapbox/mapbox-gl-native/issues/3985))
+* Remove Adapter Requirement For MarkerView ([#5214](https://github.com/mapbox/mapbox-gl-native/issues/5214))
+* Always Current Version Style URL Constants ([#5193](https://github.com/mapbox/mapbox-gl-native/issues/5193))
+* Random NullPointerException On Telemetry ([#5186](https://github.com/mapbox/mapbox-gl-native/issues/5186))
+
+## 4.1.0-beta.1 - May 26, 2016
+
+Mapbox Android 4.1.0 builds off our ambitious 4.0.0 version with 3 major new features being released. To help us produce the highest quality SDK possible we're releasing an official Beta release first so that everyone has time to explore it and help hardened it before the official 4.1.0 Final Release.
+
+* View Based Annotations ([#3276](https://github.com/mapbox/mapbox-gl-native/issues/3276))
+* UserLocationView Refactor ([#4396](https://github.com/mapbox/mapbox-gl-native/issues/4396))
+* MapboxAccountManager ([#5004](https://github.com/mapbox/mapbox-gl-native/issues/5004))
+
+## 4.0.1 - May 12, 2016
Mapbox Android 4.0.1 is a patch release to make this bug fix available sooner.
* MapboxMap.removeAnnotations() doesn't remove markers ([#4553](https://github.com/mapbox/mapbox-gl-native/issues/4553))
-## 4.0.0
+## 4.0.0 - March 30, 2016
Mapbox Android 4.0.0 contains the following 3 major new features.
@@ -16,14 +59,14 @@ Mapbox Android 4.0.0 contains the following 3 major new features.
* Offline Maps ([#3891](https://github.com/mapbox/mapbox-gl-native/issues/3891))
* Telemetry ([#2421](https://github.com/mapbox/mapbox-gl-native/issues/2421))
-## 4.0.0-rc.1
+## 4.0.0-rc.1 - March 25, 2016
* Default Value Bug Fix for MapboxMapOptions ([#4398](https://github.com/mapbox/mapbox-gl-native/issues/4398))
* NullPointerException When Scrolling ([#4424](https://github.com/mapbox/mapbox-gl-native/issues/4424))
* Platform Specific CHANGELOGS ([#4432](https://github.com/mapbox/mapbox-gl-native/issues/4432))
* Introduce LatLng.wrap() ([#4475](https://github.com/mapbox/mapbox-gl-native/issues/4475))
-## 4.0.0-beta.2
+## 4.0.0-beta.2 - March 21, 2016
* arm64 ABI Support ([#3128](https://github.com/mapbox/mapbox-gl-native/issues/3128))
* Unify Offline And Cache Databases ([#4362](https://github.com/mapbox/mapbox-gl-native/issues/4362))
@@ -32,7 +75,7 @@ Mapbox Android 4.0.0 contains the following 3 major new features.
* OnCameraChange Bug Fix ([#4327](https://github.com/mapbox/mapbox-gl-native/issues/4327))
* OnCameraChangeListener vs getCameraPosition ([#4326](https://github.com/mapbox/mapbox-gl-native/issues/4326))
-## 4.0.0-beta.1
+## 4.0.0-beta.1 - March 15, 2016
Mapbox Android 4.0.0 is the most ambitious Android release to date with 3 major new features being released. To help us produce the highest quality SDK possible we're releasing an official Beta release first so that everyone has time to explore it and help hardened it before the official 4.0.0 Final Release.
@@ -40,27 +83,27 @@ Mapbox Android 4.0.0 is the most ambitious Android release to date with 3 major
* Offline Maps ([#3891](https://github.com/mapbox/mapbox-gl-native/issues/3891))
* Telemetry ([#2421](https://github.com/mapbox/mapbox-gl-native/issues/2421))
-## 3.2.0
+## 3.2.0 - January 28, 2016
* Fixed crash caused by annotation image with non-integer width or height ([#3031](https://github.com/mapbox/mapbox-gl-native/issues/3031))
* Tracking Mode Reverses Bearing Fix ([#3664](https://github.com/mapbox/mapbox-gl-native/issues/3664))
* GPS Extra Rotation Fix ([#3661](https://github.com/mapbox/mapbox-gl-native/issues/3661))
* Added new methods for getting and setting the min and max zoom levels: `getMinZoom`, `setMinZoom`, `getMaxZoom`, `setMaxZoom`. ([#509](https://github.com/mapbox/mapbox-gl-native/issues/509))
-## 3.1.0
+## 3.1.0 - January 20, 2016
* Camera API Callback Improvements ([#3412](https://github.com/mapbox/mapbox-gl-native/issues/3412))
* Coordinate Deprecated For LatLng ([#3309](https://github.com/mapbox/mapbox-gl-native/issues/3309))
* Responsive User Dot Location Tracking ([#2049](https://github.com/mapbox/mapbox-gl-native/issues/2049))
-## 3.0.0
+## 3.0.0 - December 21, 2015
* Added Camera API ([#3244](https://github.com/mapbox/mapbox-gl-native/issues/3244))
* Custom Layer Support ([#3248](https://github.com/mapbox/mapbox-gl-native/issues/3348))
* Reverse Tilt Gesutre Detection ([#3245](https://github.com/mapbox/mapbox-gl-native/issues/3245))
* Decoupled Location Provider ([#2954](https://github.com/mapbox/mapbox-gl-native/issues/2954))
-## 2.3.0
+## 2.3.0 - December 4, 2015
* Added Tilt / Pitch Support ([#2805](https://github.com/mapbox/mapbox-gl-native/issues/2805))
* Keep InfoWindow Open When Panning ([#3121](https://github.com/mapbox/mapbox-gl-native/issues/3121))
@@ -68,7 +111,7 @@ Mapbox Android 4.0.0 is the most ambitious Android release to date with 3 major
* OkHttp Replace curl ([#2856](https://github.com/mapbox/mapbox-gl-native/issues/2856))
* GPS and Compass Customization Support ([#2858](https://github.com/mapbox/mapbox-gl-native/issues/2858))
-## 2.2.0
+## 2.2.0 - October 28, 2015
- New User Dot location graphics ([#2732](https://github.com/mapbox/mapbox-gl-native/issues/2732))
- Custom Drawable Markers ([#2744](https://github.com/mapbox/mapbox-gl-native/issues/2744))
@@ -78,7 +121,7 @@ Mapbox Android 4.0.0 is the most ambitious Android release to date with 3 major
- **RESOLVED** Black Screen On Ice Cream Sandwich and Jelly Bean devices ([#2802](https://github.com/mapbox/mapbox-gl-native/issues/2802))
-## 2.1.0
+## 2.1.0 - October 21, 2015
- Initial Android release.
diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties
index c2df63b0bb..6aab985298 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -1,5 +1,5 @@
GROUP=com.mapbox.mapboxsdk
-VERSION_NAME=4.1.0-SNAPSHOT
+VERSION_NAME=4.2.0-SNAPSHOT
POM_DESCRIPTION=Mapbox GL Android SDK
POM_URL=https://github.com/mapbox/mapbox-gl-native
@@ -17,6 +17,6 @@ ANDROID_BUILD_TARGET_SDK_VERSION=23
ANDROID_BUILD_TOOLS_VERSION=23.0.3
ANDROID_BUILD_SDK_VERSION=23
-POM_NAME=Mapbox GL Android SDK
+POM_NAME=Mapbox Android SDK
POM_ARTIFACT_ID=mapbox-android-sdk
POM_PACKAGING=aar
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
index 0307d462c8..c848e61e6c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
@@ -2,6 +2,7 @@
package="com.mapbox.mapboxsdk">
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
+ <uses-feature android:name="android.hardware.wifi" android:required="false" /> <!-- Implied by ACCESS_WIFI_STATE. -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapboxAccountManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapboxAccountManager.java
index 211590653a..8f2597c60a 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapboxAccountManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapboxAccountManager.java
@@ -2,8 +2,10 @@ package com.mapbox.mapboxsdk;
import android.content.Context;
import android.text.TextUtils;
+
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
+import com.mapbox.mapboxsdk.exceptions.MapboxAccountManagerNotStartedException;
import com.mapbox.mapboxsdk.telemetry.MapboxEventManager;
public class MapboxAccountManager {
@@ -16,8 +18,9 @@ public class MapboxAccountManager {
/**
* MapboxAccountManager should NOT be instantiated directly.
* Use @see MapboxAccountManager#getInstance() instead.
+ *
* @param applicationContext Context used to get ApplicationContext
- * @param accessToken Mapbox Access Token
+ * @param accessToken Mapbox Access Token
*/
private MapboxAccountManager(Context applicationContext, String accessToken) {
super();
@@ -29,7 +32,7 @@ public class MapboxAccountManager {
* Primary entry point to Mapbox for implementing developers.
* Must be configured in either Application.onCreate() or Launch Activity.onCreate()
*
- * @param context Context used to get Application Context
+ * @param context Context used to get Application Context
* @param accessToken Mapbox Access Token. You can get one on the Mapbox Web site.
* @return MapboxAccountManager instance for app
*/
@@ -49,11 +52,16 @@ public class MapboxAccountManager {
* @return MapboxAccountManager instance for app. May be NULL if not configured yet.
*/
public static MapboxAccountManager getInstance() {
+ if (mapboxAccountManager == null) {
+ throw new MapboxAccountManagerNotStartedException();
+ }
+
return mapboxAccountManager;
}
/**
* Access Token for this application
+ *
* @return Mapbox Access Token
*/
public String getAccessToken() {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java
index 36d56591c8..3c4868c84b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java
@@ -23,6 +23,7 @@ public abstract class Annotation implements Comparable<Annotation> {
*/
private long id = -1; // -1 unless added to a MapView
protected MapboxMap mapboxMap;
+ protected MapView mapView;
protected Annotation() {
}
@@ -68,6 +69,22 @@ public abstract class Annotation implements Comparable<Annotation> {
return mapboxMap;
}
+ /**
+ * Don not use this method. Used internally by the SDK.
+ */
+ public void setMapView(MapView mapView) {
+ this.mapView = mapView;
+ }
+
+ /**
+ * Gets the associated MapView
+ *
+ * @return The MapView
+ */
+ protected MapView getMapView() {
+ return mapView;
+ }
+
@Override
public int compareTo(@NonNull Annotation annotation) {
if (id < annotation.getId()) {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java
index fc2022f9e1..3adeb52ea7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java
@@ -39,6 +39,22 @@ public abstract class BaseMarkerOptions<U extends Marker, T extends BaseMarkerOp
return getThis();
}
+ public T setIcon(Icon icon) {
+ return icon(icon);
+ }
+
+ public T setPosition(LatLng position) {
+ return position(position);
+ }
+
+ public T setSnippet(String snippet) {
+ return snippet(snippet);
+ }
+
+ public T setTitle(String title) {
+ return title(title);
+ }
+
public abstract T getThis();
public abstract U getMarker();
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 0cd54fc0f0..a5c6397b6f 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
@@ -1,7 +1,7 @@
package com.mapbox.mapboxsdk.annotations;
import android.os.Parcelable;
-import android.support.annotation.AnimatorRes;
+import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.geometry.LatLng;
@@ -26,10 +26,10 @@ public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends Base
protected float anchorV = 1f;
protected float infoWindowAnchorU = 0.5f;
protected float infoWindowAnchorV = 0.0f;
- protected int selectAnimRes;
- protected int deselectAnimRes;
- protected int rotation;
+ protected float rotation;
protected boolean visible = true;
+ protected boolean selected;
+ protected float alpha = 1.0f;
/**
* Default constructor
@@ -99,7 +99,7 @@ public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends Base
* @param v the v-value
* @return the object for which the method was called
*/
- public T anchor(float u, float v) {
+ public T anchor(@FloatRange(from = 0.0, to = 1.0) float u, @FloatRange(from = 0.0, to = 1.0) float v) {
this.anchorU = u;
this.anchorV = v;
return getThis();
@@ -112,53 +112,42 @@ public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends Base
* @param v the v-values
* @return the object for which the method was called
*/
- public T infoWindowAnchor(float u, float v) {
+ public T infoWindowAnchor(@FloatRange(from = 0.0, to = 1.0) float u, @FloatRange(from = 0.0, to = 1.0) float v) {
this.infoWindowAnchorU = u;
this.infoWindowAnchorV = v;
return getThis();
}
/**
- * Set the animator resource to be used when an MarkerView is selected.
+ * Set the rotation of the MarkerView.
*
- * @param selectAnimRes the used animator resource
+ * @param rotation the rotation value
* @return the object for which the method was called
*/
- public T selectAnimatorResource(@AnimatorRes int selectAnimRes) {
- this.selectAnimRes = selectAnimRes;
+ public T rotation(float rotation) {
+ this.rotation = rotation;
return getThis();
}
/**
- * Set the animator resource to be used when an MarkerView is deselected.
+ * Set the visibility state of the MarkerView.
*
- * @param deselectAnimRes the used animator resource
+ * @param visible the visible state
* @return the object for which the method was called
*/
- public T deselectAnimatorResource(@AnimatorRes int deselectAnimRes) {
- this.deselectAnimRes = deselectAnimRes;
+ public T visible(boolean visible) {
+ this.visible = visible;
return getThis();
}
/**
- * Set the rotation of the MarkerView.
+ * Set the alpha of the MarkerView.
*
- * @param rotation the rotation value
+ * @param alpha the alpha value
* @return the object for which the method was called
*/
- public T rotation(int rotation) {
- this.rotation = rotation;
- return getThis();
- }
-
- /**
- * Set the visibility state of the MarkerView.
- *
- * @param visible the visible state
- * @return the object for which the method was calleds
- */
- public T visible(boolean visible) {
- this.visible = visible;
+ public T alpha(float alpha) {
+ this.alpha = alpha;
return getThis();
}
@@ -244,29 +233,11 @@ public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends Base
}
/**
- * Get the animator resource used for selecting the MarkerView.
- *
- * @return the animator resource
- */
- public int getSelectAnimRes() {
- return selectAnimRes;
- }
-
- /**
- * Get the animator resource used for deselecting the MarkerView.
- *
- * @return the animator resource
- */
- public int getDeselectAnimRes() {
- return deselectAnimRes;
- }
-
- /**
* Get the rotation of the MarkerView.
*
* @return the rotation value
*/
- public int getRotation() {
+ public float getRotation() {
return rotation;
}
@@ -280,6 +251,15 @@ public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends Base
}
/**
+ * Get the alpha of the MarkerView.
+ *
+ * @return the alpha value
+ */
+ public float getAlpha() {
+ return alpha;
+ }
+
+ /**
* Get the instance of the object for which this method was called.
*
* @return the object for which the this method was called
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 d7d41b98be..93c6deddc9 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
@@ -33,6 +33,7 @@ public final class IconFactory {
private Context mContext;
private static IconFactory sInstance;
private Icon mDefaultMarker;
+ private Icon mDefaultMarkerView;
private BitmapFactory.Options mOptions;
private int mNextId = 0;
@@ -121,6 +122,13 @@ public final class IconFactory {
return mDefaultMarker;
}
+ public Icon defaultMarkerView() {
+ if (mDefaultMarkerView == null) {
+ mDefaultMarkerView = fromResource(R.drawable.default_markerview);
+ }
+ return mDefaultMarkerView;
+ }
+
private Icon fromInputStream(@NonNull InputStream is) {
Bitmap bitmap = BitmapFactory.decodeStream(is, null, mOptions);
return fromBitmap(bitmap);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java
index 7452ab8fac..9af459e8a0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java
@@ -3,6 +3,7 @@ package com.mapbox.mapboxsdk.annotations;
import android.content.res.Resources;
import android.graphics.PointF;
import android.support.annotation.LayoutRes;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -222,9 +223,22 @@ public class InfoWindow {
}
mMapboxMap = new WeakReference<>(mapboxMap);
String title = overlayItem.getTitle();
- ((TextView) view.findViewById(R.id.infowindow_title)).setText(title);
+ TextView titleTextView = ((TextView) view.findViewById(R.id.infowindow_title));
+ if (!TextUtils.isEmpty(title)) {
+ titleTextView.setText(title);
+ titleTextView.setVisibility(View.VISIBLE);
+ } else {
+ titleTextView.setVisibility(View.GONE);
+ }
+
String snippet = overlayItem.getSnippet();
- ((TextView) view.findViewById(R.id.infowindow_description)).setText(snippet);
+ TextView snippetTextView = ((TextView) view.findViewById(R.id.infowindow_description));
+ if (!TextUtils.isEmpty(snippet)) {
+ snippetTextView.setText(snippet);
+ snippetTextView.setVisibility(View.VISIBLE);
+ } else {
+ snippetTextView.setVisibility(View.GONE);
+ }
}
InfoWindow setBoundMarker(Marker boundMarker) {
@@ -245,11 +259,20 @@ public class InfoWindow {
View view = mView.get();
if (mapboxMap != null && marker != null && view != null) {
mCoordinates = mapboxMap.getProjection().toScreenLocation(marker.getPosition());
- view.setX(mCoordinates.x + mViewWidthOffset - mMarkerWidthOffset);
+
+ if (view instanceof InfoWindowView) {
+ view.setX(mCoordinates.x + mViewWidthOffset - mMarkerWidthOffset);
+ } else {
+ view.setX(mCoordinates.x - (view.getMeasuredWidth() / 2) - mMarkerWidthOffset);
+ }
view.setY(mCoordinates.y + mMarkerHeightOffset);
}
}
+ public View getView() {
+ return mView != null ? mView.get() : null;
+ }
+
boolean isVisible() {
return mIsVisible;
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java
index 16b219684f..d24f020a18 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.annotations;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
-
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
@@ -91,8 +90,9 @@ public class Marker extends Annotation {
}
}
- void setSnippet(String snippet) {
+ public void setSnippet(String snippet) {
this.snippet = snippet;
+ refreshInfoWindowContent();
}
/**
@@ -112,8 +112,30 @@ public class Marker extends Annotation {
return icon;
}
- void setTitle(String title) {
+ public void setTitle(String title) {
this.title = title;
+ refreshInfoWindowContent();
+ }
+
+ @Nullable
+ public InfoWindow getInfoWindow() {
+ return infoWindow;
+ }
+
+ /**
+ * Update only for default Marker's InfoWindow content for Title and Snippet
+ */
+ private void refreshInfoWindowContent() {
+ if (isInfoWindowShown() && mapView != null && mapboxMap != null && mapboxMap.getInfoWindowAdapter() == null) {
+ InfoWindow infoWindow = getInfoWindow(mapView);
+ if (mapView.getContext() != null) {
+ infoWindow.adaptDefaultMarker(this, mapboxMap, mapView);
+ }
+ MapboxMap map = getMapboxMap();
+ if (map != null) {
+ map.updateMarker(this);
+ }
+ }
}
/**
@@ -121,6 +143,7 @@ public class Marker extends Annotation {
*/
public InfoWindow showInfoWindow(@NonNull MapboxMap mapboxMap, @NonNull MapView mapView) {
setMapboxMap(mapboxMap);
+ setMapView(mapView);
MapboxMap.InfoWindowAdapter infoWindowAdapter = getMapboxMap().getInfoWindowAdapter();
if (infoWindowAdapter != null) {
// end developer is using a custom InfoWindowAdapter
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java
index 7ca3687b0d..e0f3b86fc2 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java
@@ -4,6 +4,7 @@ import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
+import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException;
import com.mapbox.mapboxsdk.geometry.LatLng;
@@ -70,6 +71,10 @@ public final class MarkerOptions extends BaseMarkerOptions<Marker, MarkerOptions
* @return Marker The build marker
*/
public Marker getMarker() {
+ if (position == null) {
+ throw new InvalidMarkerPositionException();
+ }
+
marker.setPosition(position);
marker.setSnippet(snippet);
marker.setTitle(title);
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 3e51044643..a577550635 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
@@ -1,6 +1,18 @@
package com.mapbox.mapboxsdk.annotations;
+import android.animation.AnimatorSet;
+import android.graphics.Bitmap;
+import android.graphics.PointF;
+import android.support.annotation.FloatRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.utils.AnimatorUtils;
/**
* MarkerView is an annotation that shows an View at a geographical location.
@@ -19,8 +31,8 @@ public class MarkerView extends Marker {
private float anchorU;
private float anchorV;
- private float offsetX;
- private float offsetY;
+ private float offsetX = -1;
+ private float offsetY = -1;
private float infoWindowAnchorU;
private float infoWindowAnchorV;
@@ -28,13 +40,14 @@ public class MarkerView extends Marker {
private boolean flat;
private boolean visible = true;
- private int selectAnimRes;
- private int deselectAnimRes;
-
private float tiltValue;
private float rotation;
private float alpha = 1;
+ private Icon markerViewIcon;
+
+ private boolean selected;
+
/**
* Publicly hidden default constructor
*/
@@ -53,12 +66,11 @@ public class MarkerView extends Marker {
this.infoWindowAnchorU = baseMarkerViewOptions.getInfoWindowAnchorU();
this.infoWindowAnchorV = baseMarkerViewOptions.getInfoWindowAnchorV();
this.flat = baseMarkerViewOptions.isFlat();
- this.selectAnimRes = baseMarkerViewOptions.getSelectAnimRes();
- this.deselectAnimRes = baseMarkerViewOptions.getDeselectAnimRes();
this.infoWindowAnchorU = baseMarkerViewOptions.infoWindowAnchorU;
this.infoWindowAnchorV = baseMarkerViewOptions.infoWindowAnchorV;
this.anchorU = baseMarkerViewOptions.anchorU;
this.anchorV = baseMarkerViewOptions.anchorV;
+ this.selected = baseMarkerViewOptions.selected;
}
/**
@@ -71,9 +83,10 @@ public class MarkerView extends Marker {
* @param u u-coordinate of the anchor, as a ratio of the image width (in the range [0, 1])
* @param v v-coordinate of the anchor, as a ratio of the image height (in the range [0, 1])
*/
- public void setAnchor(float u, float v) {
+ public void setAnchor(@FloatRange(from = 0.0, to = 1.0) float u, @FloatRange(from = 0.0, to = 1.0) float v) {
this.anchorU = u;
this.anchorV = v;
+ setOffset(-1, -1);
}
/**
@@ -95,26 +108,16 @@ public class MarkerView extends Marker {
}
/**
- * Internal method to set the horizontal calculated offset.
+ * Internal method to set the calculated offset.
* <p>
* These are calculated based on the View bounds and the provided anchor.
* </p>
*
* @param x the x-value of the offset
- */
- void setOffsetX(float x) {
- offsetX = x;
- }
-
- /**
- * Internal method to set the vertical calculated offset.
- * <p>
- * These are calculated based on the View bounds and the provided anchor.
- * </p>
- *
* @param y the y-value of the offset
*/
- void setOffsetY(float y) {
+ void setOffset(float x, float y) {
+ offsetX = x;
offsetY = y;
}
@@ -149,7 +152,7 @@ public class MarkerView extends Marker {
* @param v v-coordinate of the info window anchor, as a ratio of the image height (in the range [0, 1])
* @see #setAnchor(float, float) for more details.
*/
- public void setInfoWindowAnchor(float u, float v) {
+ public void setInfoWindowAnchor(@FloatRange(from = 0.0, to = 1.0) float u, @FloatRange(from = 0.0, to = 1.0) float v) {
this.infoWindowAnchorU = u;
this.infoWindowAnchorV = v;
}
@@ -191,42 +194,6 @@ public class MarkerView extends Marker {
}
/**
- * Get the animator resource used to animate to the selected state of a MarkerView.
- *
- * @return the animator resource used
- */
- public int getSelectAnimRes() {
- return selectAnimRes;
- }
-
- /**
- * Set the animator resource used to animate to the deselected state of a MarkerView.
- *
- * @param selectAnimRes the animator resource used
- */
- public void setSelectAnimRes(int selectAnimRes) {
- this.selectAnimRes = selectAnimRes;
- }
-
- /**
- * Get the animator resource used to animate to the deslected state of a MarkerView.
- *
- * @return the animator resource used
- */
- public int getDeselectAnimRes() {
- return deselectAnimRes;
- }
-
- /**
- * Set the animator resource used to animate to the selected state of a MarkerView.
- *
- * @param deselectAnimRes the animator resource used
- */
- public void setDeselectAnimRes(int deselectAnimRes) {
- this.deselectAnimRes = deselectAnimRes;
- }
-
- /**
* Internal method to get the current tilted value of a MarkerView.
*
* @return the tilted value
@@ -240,7 +207,7 @@ public class MarkerView extends Marker {
*
* @param tiltValue the tilted value to set
*/
- void setTilt(float tiltValue) {
+ void setTilt(@FloatRange(from = 0.0, to = MapboxConstants.MAXIMUM_TILT) float tiltValue) {
this.tiltValue = tiltValue;
}
@@ -308,7 +275,7 @@ public class MarkerView extends Marker {
*
* @param alpha the alpha value to animate to
*/
- public void setAlpha(float alpha) {
+ public void setAlpha(@FloatRange(from = 0.0, to = 255.0) float alpha) {
this.alpha = alpha;
if (markerViewManager != null) {
markerViewManager.animateAlpha(this, alpha);
@@ -316,6 +283,53 @@ public class MarkerView extends Marker {
}
/**
+ * Set the icon of the MarkerView.
+ *
+ * @param icon the icon to be used as Marker image
+ */
+ @Override
+ public void setIcon(@Nullable Icon icon) {
+ if (icon != null) {
+ markerViewIcon = IconFactory.recreate("icon", icon.getBitmap());
+ }
+ Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ Icon transparentIcon = IconFactory.recreate("markerViewSettings", bitmap);
+ if (markerViewManager != null) {
+ markerViewManager.updateIcon(this);
+ }
+ super.setIcon(transparentIcon);
+ }
+
+ @Override
+ public void setPosition(LatLng position) {
+ super.setPosition(position);
+ if (markerViewManager != null) {
+ markerViewManager.update();
+ }
+ }
+
+ public boolean isSelected() {
+ return selected;
+ }
+
+ /**
+ * For internal use only, use {@link MapboxMap#selectMarker(Marker)} instead.
+ */
+ void setSelected(boolean selected) {
+ this.selected = selected;
+ }
+
+ /**
+ * Get the icon of the MarkerView.
+ *
+ * @return the icon use as Marker image
+ */
+ @Override
+ public Icon getIcon() {
+ return markerViewIcon;
+ }
+
+ /**
* Set the MapboxMap associated tot the MapView containing the MarkerView.
* <p>
* This method is used to instantiate the MarkerView and provide an instance of {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter}
@@ -326,6 +340,12 @@ public class MarkerView extends Marker {
@Override
public void setMapboxMap(MapboxMap mapboxMap) {
super.setMapboxMap(mapboxMap);
+
+ if(isFlat()) {
+ // initial tilt value if MapboxMap is started with a tilt attribute
+ tiltValue = (float) mapboxMap.getCameraPosition().tilt;
+ }
+
markerViewManager = mapboxMap.getMarkerViewManager();
}
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 d9fc9e62ae..d138ded963 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
@@ -1,12 +1,16 @@
package com.mapbox.mapboxsdk.annotations;
+import android.content.Context;
import android.graphics.PointF;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.util.Pools;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.Projection;
@@ -26,12 +30,13 @@ import java.util.Map;
*/
public class MarkerViewManager {
- private Map<MarkerView, View> mMarkerViewMap;
+ private Map<MarkerView, View> markerViewMap;
private MapboxMap mapboxMap;
private MapView mapView;
private List<MapboxMap.MarkerViewAdapter> markerViewAdapters;
- private long mViewMarkerBoundsUpdateTime;
+ private long viewMarkerBoundsUpdateTime;
private MapboxMap.OnMarkerViewClickListener onMarkerViewClickListener;
+ private ImageMarkerViewAdapter defaultMarkerViewAdapter;
/**
* Creates an instance of MarkerViewManager.
@@ -43,7 +48,9 @@ public class MarkerViewManager {
this.mapboxMap = mapboxMap;
this.markerViewAdapters = new ArrayList<>();
this.mapView = mapView;
- mMarkerViewMap = new HashMap<>();
+ this.markerViewMap = new HashMap<>();
+ this.defaultMarkerViewAdapter = new ImageMarkerViewAdapter(mapView.getContext());
+ this.markerViewAdapters.add(defaultMarkerViewAdapter);
}
/**
@@ -56,7 +63,7 @@ public class MarkerViewManager {
* @param rotation the rotation value
*/
public void animateRotation(@NonNull MarkerView marker, float rotation) {
- View convertView = mMarkerViewMap.get(marker);
+ View convertView = markerViewMap.get(marker);
if (convertView != null) {
AnimatorUtils.rotate(convertView, rotation);
}
@@ -72,7 +79,7 @@ public class MarkerViewManager {
* @param alpha the alpha value
*/
public void animateAlpha(@NonNull MarkerView marker, float alpha) {
- View convertView = mMarkerViewMap.get(marker);
+ View convertView = markerViewMap.get(marker);
if (convertView != null) {
AnimatorUtils.alpha(convertView, alpha);
}
@@ -88,7 +95,7 @@ public class MarkerViewManager {
* @param visible the flag indicating if MarkerView is visible
*/
public void animateVisible(@NonNull MarkerView marker, boolean visible) {
- View convertView = mMarkerViewMap.get(marker);
+ View convertView = markerViewMap.get(marker);
if (convertView != null) {
convertView.setVisibility(visible ? View.VISIBLE : View.GONE);
}
@@ -103,20 +110,24 @@ public class MarkerViewManager {
* </p>
*/
public void update() {
- View convertView;
- for (MarkerView marker : mMarkerViewMap.keySet()) {
- convertView = mMarkerViewMap.get(marker);
+ for (final MarkerView marker : markerViewMap.keySet()) {
+ final View convertView = markerViewMap.get(marker);
if (convertView != null) {
PointF point = mapboxMap.getProjection().toScreenLocation(marker.getPosition());
- int x = (int) (marker.getAnchorU() * convertView.getMeasuredWidth());
- int y = (int) (marker.getAnchorV() * convertView.getMeasuredHeight());
-
- marker.setOffsetX(x);
- marker.setOffsetY(y);
+ if (marker.getOffsetX() == -1) {
+ // ensure view is measured first
+ if (convertView.getMeasuredWidth() == 0) {
+ convertView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ }
+ int x = (int) (marker.getAnchorU() * convertView.getMeasuredWidth());
+ int y = (int) (marker.getAnchorV() * convertView.getMeasuredHeight());
+ marker.setOffset(x, y);
+ }
- convertView.setX(point.x - x);
- convertView.setY(point.y - y);
+ convertView.setX(point.x - marker.getOffsetX());
+ convertView.setY(point.y - marker.getOffsetY());
+ // animate visibility
if (marker.isVisible() && convertView.getVisibility() == View.GONE) {
convertView.animate().cancel();
convertView.setAlpha(0);
@@ -133,9 +144,9 @@ public class MarkerViewManager {
*/
public void setTilt(float tilt) {
View convertView;
- for (MarkerView markerView : mMarkerViewMap.keySet()) {
+ for (MarkerView markerView : markerViewMap.keySet()) {
if (markerView.isFlat()) {
- convertView = mMarkerViewMap.get(markerView);
+ convertView = markerViewMap.get(markerView);
if (convertView != null) {
markerView.setTilt(tilt);
convertView.setRotationX(tilt);
@@ -145,69 +156,167 @@ public class MarkerViewManager {
}
/**
+ *
+ */
+ public void updateIcon(@NonNull MarkerView markerView) {
+ View convertView = markerViewMap.get(markerView);
+ if (convertView != null && convertView instanceof ImageView) {
+ ((ImageView) convertView).setImageBitmap(markerView.getIcon().getBitmap());
+ }
+ }
+
+ /**
* Animate a MarkerView to a deselected state.
* <p>
- * The {@link MarkerView#getDeselectAnimRes()} will be called to get the related animation.
- * If non are provided, no animation will be started.
+ * The {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter#onDeselect(MarkerView, View)} will be called to execute an animation.
* </p>
*
* @param marker the MarkerView to deselect
*/
public void deselect(@NonNull MarkerView marker) {
- final View convertView = mMarkerViewMap.get(marker);
+ deselect(marker, true);
+ }
+
+ /**
+ * Animate a MarkerView to a deselected state.
+ * <p>
+ * The {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter#onDeselect(MarkerView, View)} will be called to execute an animation.
+ * </p>
+ *
+ * @param marker the MarkerView to deselect
+ * @param callbackToMap indicates if deselect marker must be called on MapboxMap
+ */
+ public void deselect(@NonNull MarkerView marker, boolean callbackToMap) {
+ final View convertView = markerViewMap.get(marker);
+ if (convertView != null) {
+ for (MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) {
+ if (adapter.getMarkerClass().equals(marker.getClass())) {
+ adapter.onDeselect(marker, convertView);
+ }
+ }
+ if (callbackToMap) {
+ mapboxMap.deselectMarker(marker);
+ }
+ marker.setSelected(false);
+ }
+ }
+
+ /**
+ * Animate a MarkerView to a selected state.
+ *
+ * @param marker the MarkerView object to select
+ */
+ public void select(@NonNull MarkerView marker) {
+ select(marker, true);
+ }
+
+ /**
+ * Animate a MarkerView to a selected state.
+ *
+ * @param marker the MarkerView object to select
+ * @param callbackToMap indicates if select marker must be called on MapboxMap
+ */
+ public void select(@NonNull MarkerView marker, boolean callbackToMap) {
+ final View convertView = markerViewMap.get(marker);
+ for (MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) {
+ if (adapter.getMarkerClass().equals(marker.getClass())) {
+ select(marker, convertView, adapter, callbackToMap);
+ }
+ }
+ }
+
+ /**
+ * Animate a MarkerView to a selected state.
+ * <p>
+ * The {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter#onSelect(MarkerView, View, boolean)} will be called to execute an animation.
+ * </p>
+ *
+ * @param marker the MarkerView object to select
+ * @param convertView the View presentation of the MarkerView
+ * @param adapter the adapter used to adapt the marker to the convertView
+ */
+ public void select(@NonNull MarkerView marker, View convertView, MapboxMap.MarkerViewAdapter adapter) {
+ select(marker, convertView, adapter, true);
+ }
+
+
+ /**
+ * Animate a MarkerView to a selected state.
+ * <p>
+ * The {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter#onSelect(MarkerView, View, boolean)} will be called to execute an animation.
+ * </p>
+ *
+ * @param marker the MarkerView object to select
+ * @param convertView the View presentation of the MarkerView
+ * @param adapter the adapter used to adapt the marker to the convertView
+ * @param callbackToMap indicates if select marker must be called on MapboxMap
+ */
+ public void select(@NonNull MarkerView marker, View convertView, MapboxMap.MarkerViewAdapter adapter, boolean callbackToMap) {
if (convertView != null) {
- int deselectAnimatorRes = marker.getDeselectAnimRes();
- if (deselectAnimatorRes != 0) {
- AnimatorUtils.animate(convertView, deselectAnimatorRes);
+ if (adapter.onSelect(marker, convertView, false)) {
+ if (callbackToMap) {
+ mapboxMap.selectMarker(marker);
+ }
}
+ marker.setSelected(true);
+ convertView.bringToFront();
}
}
/**
+ * Get view representation from a MarkerView.
+ * <p>
+ * If marker is not found in current viewport, null is returned.
+ * </p>
+ *
+ * @param marker the marker to get the view for
+ * @return the android SDK View object
+ */
+ @Nullable
+ public View getView(MarkerView marker) {
+ return markerViewMap.get(marker);
+ }
+
+ /**
* Remove a MarkerView from a map.
* <p>
* The {@link MarkerView} will be removed using an alpha animation and related {@link View}
- * will be released to the {@link android.support.v4.util.Pools.SimplePool} from the related
+ * will be released to the android.support.v4.util.Pools.SimplePool from the related
* {@link com.mapbox.mapboxsdk.maps.MapboxMap.MarkerViewAdapter}. It's possible to remove
* the {@link MarkerView} from the underlying collection if needed.
* </p>
*
- * @param marker the MarkerView to remove
- * @param removeFromMap flag indicating if a MarkerView will be removed from the collection.
+ * @param marker the MarkerView to remove
*/
- public void removeMarkerView(MarkerView marker, boolean removeFromMap) {
- final View viewHolder = mMarkerViewMap.get(marker);
+ public void removeMarkerView(MarkerView marker) {
+ final View viewHolder = markerViewMap.get(marker);
if (viewHolder != null && marker != null) {
for (final MapboxMap.MarkerViewAdapter<?> adapter : markerViewAdapters) {
- if (adapter.getMarkerClass() == marker.getClass()) {
-
- // get pool of Views associated to an adapter
- final Pools.SimplePool<View> viewPool = adapter.getViewReusePool();
-
- // cancel ongoing animations
- viewHolder.animate().cancel();
- viewHolder.setAlpha(1);
- AnimatorUtils.alpha(viewHolder, 0, new AnimatorUtils.OnAnimationEndListener() {
- @Override
- public void onAnimationEnd() {
- viewHolder.setVisibility(View.GONE);
- viewPool.release(viewHolder);
- }
- });
+ if (adapter.getMarkerClass().equals(marker.getClass())) {
+ if (adapter.prepareViewForReuse(marker, viewHolder)) {
+ // reset offset for reuse
+ marker.setOffset(-1, -1);
+ adapter.releaseView(viewHolder);
+ }
}
}
}
- if (removeFromMap) {
- mMarkerViewMap.remove(marker);
- }
+ markerViewMap.remove(marker);
}
/**
- * Add a MarkerViewAdapter.
+ * Add a MarkerViewAdapter to the MarkerViewManager.
+ * <p>
+ * The provided MarkerViewAdapter must use supply a generic subclass of MarkerView.
+ * </p>
*
* @param markerViewAdapter the MarkerViewAdapter to add
*/
- public void addMarkerViewAdapter(@Nullable MapboxMap.MarkerViewAdapter markerViewAdapter) {
+ public void addMarkerViewAdapter(MapboxMap.MarkerViewAdapter markerViewAdapter) {
+ if (markerViewAdapter.getMarkerClass().equals(MarkerView.class)) {
+ throw new RuntimeException("Providing a custom MarkerViewAdapter requires subclassing MarkerView");
+ }
+
if (!markerViewAdapters.contains(markerViewAdapter)) {
markerViewAdapters.add(markerViewAdapter);
invalidateViewMarkersInBounds();
@@ -223,7 +332,6 @@ public class MarkerViewManager {
return markerViewAdapters;
}
-
/**
* Register a callback to be invoked when this view is clicked.
*
@@ -243,11 +351,11 @@ public class MarkerViewManager {
public void scheduleViewMarkerInvalidation() {
if (!markerViewAdapters.isEmpty()) {
long currentTime = SystemClock.elapsedRealtime();
- if (currentTime < mViewMarkerBoundsUpdateTime) {
+ if (currentTime < viewMarkerBoundsUpdateTime) {
return;
}
invalidateViewMarkersInBounds();
- mViewMarkerBoundsUpdateTime = currentTime + 250;
+ viewMarkerBoundsUpdateTime = currentTime + 250;
}
}
@@ -264,26 +372,27 @@ public class MarkerViewManager {
View convertView;
// remove old markers
- Iterator<MarkerView> iterator = mMarkerViewMap.keySet().iterator();
+ Iterator<MarkerView> iterator = markerViewMap.keySet().iterator();
while (iterator.hasNext()) {
MarkerView m = iterator.next();
if (!markers.contains(m)) {
// remove marker
- convertView = mMarkerViewMap.get(m);
- int deselectAnimRes = m.getDeselectAnimRes();
- if (deselectAnimRes != 0) {
- AnimatorUtils.animate(convertView, deselectAnimRes, 0);
+ convertView = markerViewMap.get(m);
+ for (MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) {
+ if (adapter.getMarkerClass().equals(m.getClass())) {
+ adapter.prepareViewForReuse(m, convertView);
+ adapter.releaseView(convertView);
+ iterator.remove();
+ }
}
- removeMarkerView(m, false);
- iterator.remove();
}
}
// introduce new markers
for (final MarkerView marker : markers) {
- if (!mMarkerViewMap.containsKey(marker)) {
+ if (!markerViewMap.containsKey(marker)) {
for (final MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) {
- if (adapter.getMarkerClass() == marker.getClass()) {
+ if (adapter.getMarkerClass().equals(marker.getClass())) {
convertView = (View) adapter.getViewReusePool().acquire();
final View adaptedView = adapter.getView(marker, convertView, mapView);
if (adaptedView != null) {
@@ -298,18 +407,16 @@ public class MarkerViewManager {
adaptedView.setAlpha(marker.getAlpha());
// visible
- adaptedView.setVisibility(marker.isVisible() ? View.VISIBLE : View.GONE);
+ adaptedView.setVisibility(View.GONE);
if (mapboxMap.getSelectedMarkers().contains(marker)) {
// if a marker to be shown was selected
// replay that animation with duration 0
- int selectAnimRes = marker.getSelectAnimRes();
- if (selectAnimRes != 0) {
- AnimatorUtils.animate(convertView, selectAnimRes, 0);
+ if (adapter.onSelect(marker, adaptedView, true)) {
+ mapboxMap.selectMarker(marker);
}
}
- final int animSelectRes = marker.getSelectAnimRes();
adaptedView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
@@ -319,34 +426,93 @@ public class MarkerViewManager {
}
if (!clickHandled) {
- // InfoWindow offset
- int infoWindowOffsetX = (int) ((adaptedView.getWidth() * marker.getInfoWindowAnchorU()) - marker.getOffsetX());
- int infoWindowOffsetY = (int) ((adaptedView.getHeight() * marker.getInfoWindowAnchorV()) - marker.getOffsetY());
- marker.setTopOffsetPixels(infoWindowOffsetY);
- marker.setRightOffsetPixels(infoWindowOffsetX);
-
- if (animSelectRes != 0) {
- AnimatorUtils.animate(v, animSelectRes, new AnimatorUtils.OnAnimationEndListener() {
- @Override
- public void onAnimationEnd() {
- mapboxMap.selectMarker(marker);
- }
- });
- } else {
- mapboxMap.selectMarker(marker);
- }
+ ensureInfoWindowOffset(marker);
+ select(marker, v, adapter);
}
}
});
- mMarkerViewMap.put(marker, adaptedView);
+ markerViewMap.put(marker, adaptedView);
if (convertView == null) {
- mapView.addView(adaptedView);
+ adaptedView.setVisibility(View.GONE);
+ mapView.getMarkerViewContainer().addView(adaptedView);
}
}
}
}
}
}
+ // trigger update to make newly added ViewMarker visible,
+ // these would only be updated when the map is moved.
+ update();
+ }
+
+ //TODO: This whole method is a stopgap for: https://github.com/mapbox/mapbox-gl-native/issues/5384
+ public void ensureInfoWindowOffset(MarkerView marker) {
+ View view = null;
+ if (markerViewMap.containsKey(marker)) {
+ view = markerViewMap.get(marker);
+ } else {
+ for (final MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) {
+ if (adapter.getMarkerClass().equals(marker.getClass())) {
+ View convertView = (View) adapter.getViewReusePool().acquire();
+ view = adapter.getView(marker, convertView, mapView);
+ break;
+ }
+ }
+ }
+
+ if (view != null) {
+ //Ensure the marker's view is measured first
+ if (view.getMeasuredWidth() == 0) {
+ view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ }
+
+ // update position on map
+ if (marker.getOffsetX() == -1) {
+ int x = (int) (marker.getAnchorU() * view.getMeasuredWidth());
+ int y = (int) (marker.getAnchorV() * view.getMeasuredHeight());
+ marker.setOffset(x, y);
+ }
+
+ // InfoWindow offset
+ int infoWindowOffsetX = (int) ((view.getMeasuredWidth() * marker.getInfoWindowAnchorU()) - marker.getOffsetX());
+ int infoWindowOffsetY = (int) ((view.getMeasuredHeight() * marker.getInfoWindowAnchorV()) - marker.getOffsetY());
+ marker.setTopOffsetPixels(infoWindowOffsetY);
+ marker.setRightOffsetPixels(infoWindowOffsetX);
+ }
+ }
+
+ /**
+ * Default MarkerViewAdapter used for base class of MarkerView to adapt a MarkerView to an ImageView
+ */
+ public static class ImageMarkerViewAdapter extends MapboxMap.MarkerViewAdapter<MarkerView> {
+
+ private LayoutInflater inflater;
+
+ public ImageMarkerViewAdapter(Context context) {
+ super(context);
+ inflater = LayoutInflater.from(context);
+ }
+
+ @Nullable
+ @Override
+ public View getView(@NonNull MarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
+ ViewHolder viewHolder;
+ if (convertView == null) {
+ viewHolder = new ViewHolder();
+ convertView = inflater.inflate(R.layout.view_image_marker, parent, false);
+ viewHolder.imageView = (ImageView) convertView.findViewById(R.id.image);
+ convertView.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) convertView.getTag();
+ }
+ viewHolder.imageView.setImageBitmap(marker.getIcon().getBitmap());
+ return convertView;
+ }
+
+ private static class ViewHolder {
+ ImageView imageView;
+ }
}
}
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 0c9faed355..86ad873347 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
@@ -4,6 +4,7 @@ import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
+import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException;
import com.mapbox.mapboxsdk.geometry.LatLng;
/**
@@ -28,10 +29,9 @@ public class MarkerViewOptions extends BaseMarkerViewOptions<MarkerView, MarkerV
flat(in.readByte() != 0);
anchor(in.readFloat(), in.readFloat());
infoWindowAnchor(in.readFloat(), in.readFloat());
- selectAnimatorResource(in.readInt());
- deselectAnimatorResource(in.readInt());
- rotation(in.readInt());
+ rotation(in.readFloat());
visible(in.readByte() != 0);
+ alpha(in.readFloat());
if (in.readByte() != 0) {
// this means we have an icon
String iconId = in.readString();
@@ -61,10 +61,9 @@ public class MarkerViewOptions extends BaseMarkerViewOptions<MarkerView, MarkerV
out.writeFloat(getAnchorV());
out.writeFloat(getInfoWindowAnchorU());
out.writeFloat(getInfoWindowAnchorV());
- out.writeInt(getSelectAnimRes());
- out.writeInt(getDeselectAnimRes());
- out.writeInt(getRotation());
+ out.writeFloat(getRotation());
out.writeByte((byte) (isVisible() ? 1 : 0));
+ out.writeFloat(alpha);
Icon icon = getIcon();
out.writeByte((byte) (icon != null ? 1 : 0));
if (icon != null) {
@@ -75,6 +74,10 @@ public class MarkerViewOptions extends BaseMarkerViewOptions<MarkerView, MarkerV
@Override
public MarkerView getMarker() {
+ if (position == null) {
+ throw new InvalidMarkerPositionException();
+ }
+
marker.setPosition(position);
marker.setSnippet(snippet);
marker.setTitle(title);
@@ -82,10 +85,9 @@ public class MarkerViewOptions extends BaseMarkerViewOptions<MarkerView, MarkerV
marker.setFlat(flat);
marker.setAnchor(anchorU, anchorV);
marker.setInfoWindowAnchor(infoWindowAnchorU, infoWindowAnchorV);
- marker.setSelectAnimRes(selectAnimRes);
- marker.setDeselectAnimRes(deselectAnimRes);
marker.setRotation(rotation);
marker.setVisible(visible);
+ marker.setAlpha(alpha);
return marker;
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MultiPoint.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MultiPoint.java
index d2aaea1d17..a76238fdcb 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MultiPoint.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MultiPoint.java
@@ -34,19 +34,24 @@ public abstract class MultiPoint extends Annotation {
*
* @param points the points of the polyline
*/
- void setPoints(List<LatLng> points) {
+ public void setPoints(List<LatLng> points) {
this.points = new ArrayList<>(points);
+ update();
}
- void addPoint(LatLng point) {
+ public void addPoint(LatLng point) {
points.add(point);
+ update();
}
public float getAlpha() {
return alpha;
}
- void setAlpha(float alpha) {
+ public void setAlpha(float alpha) {
this.alpha = alpha;
+ update();
}
+
+ abstract void update();
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polygon.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polygon.java
index 4a07b16827..78e3a99e96 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polygon.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polygon.java
@@ -2,10 +2,7 @@ package com.mapbox.mapboxsdk.annotations;
import android.graphics.Color;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
/**
* Polygon is a geometry annotation that's a closed loop of coordinates.
@@ -19,19 +16,49 @@ public final class Polygon extends MultiPoint {
super();
}
+ /**
+ * Get the color of the fill region of the polygon.
+ *
+ * @return the color of the fill
+ */
public int getFillColor() {
return fillColor;
}
+ /**
+ * Get the color fo the stroke of the polygon.
+ *
+ * @return the color of the stroke
+ */
public int getStrokeColor() {
return strokeColor;
}
- void setFillColor(int color) {
+ /**
+ * Sets the color of the fill region of the polygon.
+ *
+ * @param color - the color in ARGB format
+ */
+ public void setFillColor(int color) {
fillColor = color;
+ update();
}
- void setStrokeColor(int color) {
+ /**
+ * Sets the color of the stroke of the polygon.
+ *
+ * @param color - the color in ARGB format
+ */
+ public void setStrokeColor(int color) {
strokeColor = color;
+ update();
+ }
+
+ @Override
+ void update() {
+ MapboxMap mapboxMap = getMapboxMap();
+ if (mapboxMap != null) {
+ mapboxMap.updatePolygon(this);
+ }
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polyline.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polyline.java
index cfaf0d21d9..4bf3242d57 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polyline.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polyline.java
@@ -2,6 +2,8 @@ package com.mapbox.mapboxsdk.annotations;
import android.graphics.Color;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+
/**
* Polyline is a geometry feature with an unclosed list of coordinates drawn as a line
*/
@@ -14,10 +16,20 @@ public final class Polyline extends MultiPoint {
super();
}
+ /**
+ * Returns the Polyline tint color.
+ *
+ * @return the tint color
+ */
public int getColor() {
return color;
}
+ /**
+ * Returns the Polyline width.
+ *
+ * @return the width
+ */
public float getWidth() {
return width;
}
@@ -27,8 +39,9 @@ public final class Polyline extends MultiPoint {
*
* @param color - the color in ARGB format
*/
- void setColor(int color) {
+ public void setColor(int color) {
this.color = color;
+ update();
}
/**
@@ -36,7 +49,16 @@ public final class Polyline extends MultiPoint {
*
* @param width in pixels
*/
- void setWidth(float width) {
+ public void setWidth(float width) {
this.width = width;
+ update();
+ }
+
+ @Override
+ void update() {
+ MapboxMap mapboxMap = getMapboxMap();
+ if (mapboxMap != null) {
+ mapboxMap.updatePolyline(this);
+ }
}
}
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 ece992ad54..3993615eb9 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
@@ -31,6 +31,16 @@ public class MapboxConstants {
public static final String KEY_META_DATA_STAGING_ACCESS_TOKEN = "com.mapbox.TestEventsAccessToken";
/**
+ * Key used to switch storage to external in AndroidManifest.xml
+ */
+ public final static String KEY_META_DATA_SET_STORAGE_EXTERNAL = "com.mapbox.SetStorageExternal";
+
+ /**
+ * Default value for KEY_META_DATA_SET_STORAGE_EXTERNAL (default is internal storage)
+ */
+ public final static boolean DEFAULT_SET_STORAGE_EXTERNAL = false;
+
+ /**
* Default animation time
*/
public static final int ANIMATION_DURATION = 300;
@@ -116,4 +126,6 @@ public class MapboxConstants {
public static final String MAPBOX_SHARED_PREFERENCES_FILE = "MapboxSharedPreferences";
public static final String MAPBOX_SHARED_PREFERENCE_KEY_VENDORID = "mapboxVendorId";
public static final String MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_ENABLED = "mapboxTelemetryEnabled";
+ public static final String MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_STAGING_URL = "mapboxTelemetryStagingUrl";
+ public static final String MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_STAGING_ACCESS_TOKEN = "mapboxTelemetryStagingAccessToken";
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java
index aa24d58656..51eb038052 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java
@@ -1,7 +1,9 @@
package com.mapbox.mapboxsdk.constants;
import android.support.annotation.StringDef;
+
import com.mapbox.mapboxsdk.maps.MapView;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -15,169 +17,33 @@ import java.lang.annotation.RetentionPolicy;
*/
public class Style {
- /**
- * Mapbox Streets: A complete basemap, perfect for incorporating your own data.
- */
- private static final String MAPBOX_STREETS_BASE = "mapbox://styles/mapbox/streets-v%d";
- /**
- * Outdoors: A general-purpose style tailored to outdoor activities.
- */
- private static final String OUTDOORS_BASE = "mapbox://styles/mapbox/outdoors-v%d";
- /**
- * Light: Subtle light backdrop for data visualizations.
- */
- private static final String LIGHT_BASE = "mapbox://styles/mapbox/light-v%d";
- /**
- * Dark: Subtle dark backdrop for data visualizations.
- */
- private static final String DARK_BASE = "mapbox://styles/mapbox/dark-v%d";
- /**
- * Satellite: A beautiful global satellite and aerial imagery layer.
- */
- private static final String SATELLITE_BASE = "mapbox://styles/mapbox/satellite-v%d";
- /**
- * Satellite Streets: Global satellite and aerial imagery with unobtrusive labels.
- */
- private static final String SATELLITE_STREETS_BASE = "mapbox://styles/mapbox/satellite-streets-v%d";
-
- /**
- * Satellite Streets: Global satellite and aerial imagery with unobtrusive labels (Version 8).
- */
- private static final String SATELLITE_STREETS_V8 = "mapbox://styles/mapbox/satellite-hybrid-v8";
-
- /**
- * Get versioned url of Mapbox streets style.
- * <p>
- * <ul>
- * <li>Current default version is 9.</li>
- * </ul
- * </p>
- * <p>
- * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
- * </p>
- *
- * @param version the version of the style.
- * @return uri to load style from
- */
- public static String getMapboxStreetsUrl(int version) {
- return String.format(MapboxConstants.MAPBOX_LOCALE, MAPBOX_STREETS_BASE, version);
- }
-
- /**
- * Get versioned url of Outdoors streets style.
- * <p>
- * <ul>
- * <li>Current version is 9.</li>
- * </ul>
- * </p>
- * <p>
- * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
- * </p>
- *
- * @param version the version of the style.
- * @return uri to load style from
- */
- public static String getOutdoorsStyleUrl(int version) {
- return String.format(MapboxConstants.MAPBOX_LOCALE, OUTDOORS_BASE, version);
- }
-
- /**
- * Get versioned url of Light style.
- * <p>
- * <ul>
- * <li>Current default version is 9.</li>
- * </ul>
- * </p>
- * <p>
- * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
- * </p>
- *
- * @param version the version of the style.
- * @return uri to load style from
- */
- public static String getLightStyleUrl(int version) {
- return String.format(MapboxConstants.MAPBOX_LOCALE, LIGHT_BASE, version);
- }
-
- /**
- * Get versioned url of Dark style.
- * <p>
- * <ul>
- * <li>Current default version is 9.</li>
- * </ul>
- * </p>
- * <p>
- * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
- * </p>
- *
- * @param version the version of the style.
- * @return uri to load style from
- */
- public static String getDarkStyleUrl(int version) {
- return String.format(MapboxConstants.MAPBOX_LOCALE, DARK_BASE, version);
- }
-
- /**
- * Get versioned url of Satellite style.
- * <p>
- * <ul>
- * <li>Current version is 9.</li>
- * </ul>
- * </p>
- * <p>
- * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
- * </p>
- *
- * @param version the version of the style.
- * @return uri to load style from
- */
- public static String getSatelliteStyleUrl(int version) {
- return String.format(MapboxConstants.MAPBOX_LOCALE, SATELLITE_BASE, version);
- }
/**
- * Get versioned url of Satellite streets style.
- * <p>
- * <ul>
- * <li>Current version is 9.</li>
- * </ul>
- * </p>
- * <p>
- * More information on the Mapbox styles API can be found on https://www.mapbox.com/api-documentation/#styles
- * </p>
- *
- * @param version the version of the style.
- * @return uri to load style from
+ * Indicates the parameter accepts one of the values from {@link Style}. Using one of these
+ * constants means your map style will always use the latest version and may change as we
+ * improve the style
*/
- public static String getSatelliteStreetsStyleUrl(int version) {
- if (version == 8) {
- return SATELLITE_STREETS_V8;
- }
- return String.format(MapboxConstants.MAPBOX_LOCALE, SATELLITE_STREETS_BASE, version);
- }
-
- /**
- * Indicates the parameter accepts one of the values from {@link Style}.
- *
- * @deprecated use dedicated versioned methods in {@link Style} instead.
- */
- @StringDef({MAPBOX_STREETS, EMERALD, LIGHT, DARK, SATELLITE, SATELLITE_STREETS})
+ @StringDef({MAPBOX_STREETS, OUTDOORS, EMERALD, LIGHT, DARK, SATELLITE, SATELLITE_STREETS})
@Retention(RetentionPolicy.SOURCE)
- @Deprecated
public @interface StyleUrl {
}
// IMPORTANT: If you change any of these you also need to edit them in strings.xml
/**
- * Mapbox Streets: A complete basemap, perfect for incorporating your own data.
- *
- * @deprecated use {@link #getMapboxStreetsUrl(int)} instead.
+ * Mapbox Streets: A complete basemap, perfect for incorporating your own data. Using this
+ * constant means your map style will always use the latest version and may change as we
+ * improve the style.
*/
- @Deprecated
public static final String MAPBOX_STREETS = "mapbox://styles/mapbox/streets-v9";
/**
+ * Outdoors: A general-purpose style tailored to outdoor activities. Using this constant means
+ * your map style will always use the latest version and may change as we improve the style.
+ */
+ public static final String OUTDOORS = "mapbox://styles/mapbox/outdoors-v9";
+
+ /**
* Emerald: A versatile style, with emphasis on road networks and public transit.
*
* @deprecated this style has been deprecated and will be removed in future versions.
@@ -186,34 +52,27 @@ public class Style {
public static final String EMERALD = "mapbox://styles/mapbox/emerald-v8";
/**
- * Light: Subtle light backdrop for data visualizations.
- *
- * @deprecated use {@link #getLightStyleUrl(int)} instead.
+ * Light: Subtle light backdrop for data visualizations. Using this constant means your map
+ * style will always use the latest version and may change as we improve the style.
*/
- @Deprecated
public static final String LIGHT = "mapbox://styles/mapbox/light-v9";
/**
- * Dark: Subtle dark backdrop for data visualizations.
- *
- * @deprecated use {@link #getDarkStyleUrl(int)} (int)} instead.
+ * Dark: Subtle dark backdrop for data visualizations. Using this constant means your map style
+ * will always use the latest version and may change as we improve the style.
*/
- @Deprecated
public static final String DARK = "mapbox://styles/mapbox/dark-v9";
/**
- * Satellite: A beautiful global satellite and aerial imagery layer.
- *
- * @deprecated use {@link #getSatelliteStyleUrl(int)} instead.
+ * Satellite: A beautiful global satellite and aerial imagery layer. Using this constant means
+ * your map style will always use the latest version and may change as we improve the style.
*/
- @Deprecated
public static final String SATELLITE = "mapbox://styles/mapbox/satellite-v9";
/**
- * Satellite Streets: Global satellite and aerial imagery with unobtrusive labels.
- *
- * @deprecated use {@link #getSatelliteStreetsStyleUrl(int)} (int)} instead.
+ * Satellite Streets: Global satellite and aerial imagery with unobtrusive labels. Using this
+ * constant means your map style will always use the latest version and may change as we
+ * improve the style.
*/
- @Deprecated
public static final String SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-streets-v9";
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/ConversionException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/ConversionException.java
new file mode 100644
index 0000000000..87656db2a9
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/ConversionException.java
@@ -0,0 +1,22 @@
+package com.mapbox.mapboxsdk.exceptions;
+
+/**
+ * Thrown on conversion errors
+ */
+public class ConversionException extends RuntimeException {
+
+ public ConversionException() {
+ }
+
+ public ConversionException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ public ConversionException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+
+ public ConversionException(Throwable throwable) {
+ super(throwable);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java
new file mode 100644
index 0000000000..8dc98118b1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java
@@ -0,0 +1,10 @@
+package com.mapbox.mapboxsdk.exceptions;
+
+public class InvalidMarkerPositionException extends RuntimeException {
+
+ public InvalidMarkerPositionException() {
+ super("Adding an invalid Marker to a Map. " +
+ "Missing the required position field. " +
+ "Provide a non null LatLng as position to the Marker.");
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxAccountManagerNotStartedException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxAccountManagerNotStartedException.java
new file mode 100644
index 0000000000..4954098f15
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxAccountManagerNotStartedException.java
@@ -0,0 +1,23 @@
+package com.mapbox.mapboxsdk.exceptions;
+
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.mapbox.mapboxsdk.MapboxAccountManager;
+import com.mapbox.mapboxsdk.maps.MapView;
+
+/**
+ * A MapboxAccountManagerNotStartedException is thrown by {@link com.mapbox.mapboxsdk.maps.MapView}
+ * when {@link MapboxAccountManager} is not started before {@link MapView#onCreate(Bundle)}.
+ *
+ * @see MapView#onCreate(Bundle)
+ * @see MapboxAccountManager#start(Context, String)
+ */
+public class MapboxAccountManagerNotStartedException extends RuntimeException {
+
+ public MapboxAccountManagerNotStartedException() {
+ super("\nMapboxAccountManager was not started correctly. Use MapboxAccountManager#start(Context, String) to initialise. " +
+ "\nMore information in this guide https://www.mapbox.com/help/first-steps-android-sdk/#access-tokens.");
+ }
+}
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 5251ab4d6d..2fd28202af 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
@@ -290,6 +290,13 @@ public class LatLngBounds implements Parcelable {
return LatLngBounds.fromLatLngs(mLatLngList);
}
+ public Builder includes(List<LatLng> latLngs){
+ for (LatLng point : latLngs) {
+ mLatLngList.add(point);
+ }
+ return this;
+ }
+
public Builder include(@NonNull LatLng latLng) {
mLatLngList.add(latLng);
return this;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/CustomLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/CustomLayer.java
deleted file mode 100644
index 30efd59a6e..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/CustomLayer.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.mapbox.mapboxsdk.layers;
-
-public class CustomLayer {
-
- public String mID;
- public long mContext;
- public long mInitializeFunction;
- public long mRenderFunction;
- public long mDeinitializeFunction;
-
- public CustomLayer(String id,
- long context,
- long initializeFunction,
- long renderFunction,
- long deinitializeFunction) {
- this.mID = id;
- this.mContext = context;
- this.mInitializeFunction = initializeFunction;
- this.mRenderFunction = renderFunction;
- this.mDeinitializeFunction = deinitializeFunction;
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/package-info.java
deleted file mode 100644
index 4c58308c47..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Do not use this package. Experimental feature.
- */
-package com.mapbox.mapboxsdk.layers;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java
index 08f18892d2..4fade484b4 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java
@@ -18,12 +18,10 @@ import java.util.concurrent.CopyOnWriteArrayList;
/**
* Manages locational updates. Contains methods to register and unregister location listeners.
- * <p>
* <ul>
* <li>You can register a {@link LocationListener} with {@link #addLocationListener(LocationListener)} to receive location updates.</li>
* <li> You can unregister a {@link LocationListener} with {@link #removeLocationListener(LocationListener)}.</li>
* </ul>
- * <p/>
* <p>
* Note: If registering a listener in your Activity.onResume() implementation, you should unregister it in Activity.onPause().
* (You won't receive location updates when paused, and this will cut down on unnecessary system overhead).
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 b34b947a2a..6b0c207a1a 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
@@ -13,11 +13,10 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
-import android.graphics.SurfaceTexture;
import android.graphics.drawable.ColorDrawable;
import android.location.Location;
import android.net.ConnectivityManager;
@@ -45,9 +44,11 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.Surface;
-import android.view.TextureView;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
@@ -77,7 +78,6 @@ import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.exceptions.IconBitmapChangedException;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.layers.CustomLayer;
import com.mapbox.mapboxsdk.location.LocationListener;
import com.mapbox.mapboxsdk.location.LocationServices;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
@@ -124,6 +124,7 @@ public class MapView extends FrameLayout {
private NativeMapView mNativeMapView;
private boolean mHasSurface = false;
+ private ViewGroup mMarkerViewContainer;
private CompassView mCompassView;
private ImageView mLogoView;
private ImageView mAttributionsView;
@@ -142,6 +143,7 @@ public class MapView extends FrameLayout {
private ShoveGestureDetector mShoveGestureDetector;
private boolean mTwoTap = false;
private boolean mZoomStarted = false;
+ private boolean mDragStarted = false;
private boolean mQuickZoom = false;
private boolean mScrollInProgress = false;
@@ -150,9 +152,13 @@ public class MapView extends FrameLayout {
private int mContentPaddingRight;
private int mContentPaddingBottom;
- private StyleInitializer mStyleInitializer;
+ private PointF mFocalPoint;
+
+ private String mStyleUrl;
+ private String mInitalStyle;
private List<OnMapReadyCallback> mOnMapReadyCallbackList;
+ private SnapshotRequest mSnapshotRequest;
@UiThread
public MapView(@NonNull Context context) {
@@ -184,7 +190,6 @@ public class MapView extends FrameLayout {
mOnMapChangedListener = new CopyOnWriteArrayList<>();
mMapboxMap = new MapboxMap(this);
mIcons = new ArrayList<>();
- mStyleInitializer = new StyleInitializer(context);
View view = LayoutInflater.from(context).inflate(R.layout.mapview_internal, this);
if (!isInEditMode()) {
@@ -192,8 +197,7 @@ public class MapView extends FrameLayout {
}
// Reference the TextureView
- TextureView textureView = (TextureView) view.findViewById(R.id.textureView);
- textureView.setSurfaceTextureListener(new SurfaceTextureListener());
+ SurfaceView surfaceView = (SurfaceView) view.findViewById(R.id.surfaceView);
// Check if we are in Android Studio UI editor to avoid error in layout preview
if (isInEditMode()) {
@@ -209,6 +213,8 @@ public class MapView extends FrameLayout {
setFocusableInTouchMode(true);
requestFocus();
+ surfaceView.getHolder().addCallback(new SurfaceCallback());
+
// Touch gesture detectors
mGestureDetector = new GestureDetectorCompat(context, new GestureListener());
mGestureDetector.setIsLongpressEnabled(true);
@@ -224,6 +230,8 @@ public class MapView extends FrameLayout {
// Connectivity
onConnectivityChanged(isConnected());
+ mMarkerViewContainer = (ViewGroup) view.findViewById(R.id.markerViewContainer);
+
mMyLocationView = (MyLocationView) view.findViewById(R.id.userLocationView);
mMyLocationView.setMapboxMap(mMapboxMap);
@@ -252,23 +260,19 @@ public class MapView extends FrameLayout {
CameraPosition position = options.getCamera();
if (position != null) {
mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(position));
+ mMyLocationView.setTilt(position.tilt);
}
- String accessToken = null;
- if (MapboxAccountManager.getInstance() != null) {
- accessToken = MapboxAccountManager.getInstance().getAccessToken();
- } else {
- accessToken = options.getAccessToken();
+ // access token
+ String accessToken = options.getAccessToken();
+ if (!TextUtils.isEmpty(accessToken)) {
+ mMapboxMap.setAccessToken(accessToken);
}
+ // style url
String style = options.getStyle();
- if (!TextUtils.isEmpty(accessToken)) {
- mMapboxMap.setAccessToken(accessToken);
- if (style != null) {
- setStyleUrl(style);
- }
- } else {
- mStyleInitializer.setStyle(style, true);
+ if (!TextUtils.isEmpty(style)) {
+ mInitalStyle = style;
}
// MyLocationView
@@ -356,8 +360,17 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
+ String accessToken = mMapboxMap.getAccessToken();
+ if (TextUtils.isEmpty(accessToken)) {
+ accessToken = MapboxAccountManager.getInstance().getAccessToken();
+ mMapboxMap.setAccessToken(accessToken);
+ } else {
+ // user provided access token through xml attributes, need to start MapboxAccountManager
+ MapboxAccountManager.start(getContext(), accessToken);
+ }
+
// Force a check for an access token
- MapboxAccountManager.validateAccessToken(getAccessToken());
+ MapboxAccountManager.validateAccessToken(accessToken);
if (savedInstanceState != null && savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
@@ -407,7 +420,6 @@ public class MapView extends FrameLayout {
// User location
try {
- //noinspection ResourceType
mMapboxMap.setMyLocationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED));
} catch (SecurityException ignore) {
// User did not accept location permissions
@@ -445,10 +457,20 @@ public class MapView extends FrameLayout {
callback.onMapReady(mMapboxMap);
iterator.remove();
}
+ mMapboxMap.getMarkerViewManager().scheduleViewMarkerInvalidation();
}
} else if (change == REGION_IS_CHANGING || change == REGION_DID_CHANGE || change == DID_FINISH_LOADING_MAP) {
mMapboxMap.getMarkerViewManager().scheduleViewMarkerInvalidation();
+
+ mCompassView.update(getDirection());
+ mMyLocationView.update();
+ mMapboxMap.getMarkerViewManager().update();
+
+ for (InfoWindow infoWindow : mMapboxMap.getInfoWindows()) {
+ infoWindow.update();
+ }
}
+
}
});
@@ -473,7 +495,7 @@ public class MapView extends FrameLayout {
outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, mMapboxMap.getCameraPosition());
outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, mMapboxMap.isDebugActive());
- outState.putString(MapboxConstants.STATE_STYLE_URL, mStyleInitializer.getStyle());
+ outState.putString(MapboxConstants.STATE_STYLE_URL, mStyleUrl);
outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED, mMapboxMap.isMyLocationEnabled());
// TrackingSettings
@@ -504,7 +526,7 @@ public class MapView extends FrameLayout {
// UiSettings - Logo
outState.putInt(MapboxConstants.STATE_LOGO_GRAVITY, uiSettings.getLogoGravity());
outState.putInt(MapboxConstants.STATE_LOGO_MARGIN_LEFT, uiSettings.getLogoMarginLeft());
- outState.putInt(MapboxConstants.STATE_LOGO_MARGIN_TOP, uiSettings.getCompassMarginTop());
+ outState.putInt(MapboxConstants.STATE_LOGO_MARGIN_TOP, uiSettings.getLogoMarginTop());
outState.putInt(MapboxConstants.STATE_LOGO_MARGIN_RIGHT, uiSettings.getLogoMarginRight());
outState.putInt(MapboxConstants.STATE_LOGO_MARGIN_BOTTOM, uiSettings.getLogoMarginBottom());
outState.putBoolean(MapboxConstants.STATE_LOGO_ENABLED, uiSettings.isLogoEnabled());
@@ -555,10 +577,23 @@ public class MapView extends FrameLayout {
mNativeMapView.update();
mMyLocationView.onResume();
- if (mStyleInitializer.isDefaultStyle()) {
+ if (mStyleUrl == null) {
+ // user supplied style through xml
// user has failed to supply a style url
- setStyleUrl(mStyleInitializer.getStyle());
+ setStyleUrl(mInitalStyle == null ? Style.MAPBOX_STREETS : mInitalStyle);
+ }
+ }
+
+ void setFocalPoint(PointF focalPoint) {
+ if (focalPoint == null) {
+ // resetting focal point,
+ UiSettings uiSettings = mMapboxMap.getUiSettings();
+ // need to validate if we need to reset focal point with user provided one
+ if (uiSettings.getFocalPoint() != null) {
+ focalPoint = uiSettings.getFocalPoint();
+ }
}
+ mFocalPoint = focalPoint;
}
/**
@@ -648,6 +683,7 @@ public class MapView extends FrameLayout {
if (mDestroyed) {
return;
}
+ mMyLocationView.setBearing(0);
mNativeMapView.cancelTransitions();
mNativeMapView.resetNorth();
}
@@ -672,11 +708,11 @@ public class MapView extends FrameLayout {
return mContentPaddingBottom;
}
- int getContentWidth(){
+ int getContentWidth() {
return getWidth() - mContentPaddingLeft - mContentPaddingRight;
}
- int getContentHeight(){
+ int getContentHeight() {
return getHeight() - mContentPaddingBottom - mContentPaddingTop;
}
@@ -783,7 +819,7 @@ public class MapView extends FrameLayout {
* <li>{@code asset://...}:
* reads the style from the APK {@code assets/} directory.
* This is used to load a style bundled with your app.</li>
- * <li>{@code null}: loads the default {@link Style#getMapboxStreetsUrl(int)} style.</li>
+ * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li>
* </ul>
* <p>
* This method is asynchronous and will return immediately before the style finishes loading.
@@ -799,7 +835,7 @@ public class MapView extends FrameLayout {
if (mDestroyed) {
return;
}
- mStyleInitializer.setStyle(url);
+ mStyleUrl = url;
mNativeMapView.setStyleUrl(url);
}
@@ -833,7 +869,7 @@ public class MapView extends FrameLayout {
@UiThread
@NonNull
public String getStyleUrl() {
- return mStyleInitializer.getStyle();
+ return mStyleUrl;
}
//
@@ -874,7 +910,7 @@ public class MapView extends FrameLayout {
* <p>
* DEPRECATED @see MapboxAccountManager#getAccessToken()
* </p>
- * <p/>
+ * <p>
* Returns the current Mapbox access token used to load map styles and tiles.
* </p>
*
@@ -991,12 +1027,53 @@ public class MapView extends FrameLayout {
if (updatedMarker.getId() == -1) {
Log.w(MapboxConstants.TAG, "marker has an id of -1, possibly was not added yet, doing nothing");
+ return;
+ }
+
+ if (!(updatedMarker instanceof MarkerView)) {
+ ensureIconLoaded(updatedMarker);
}
- ensureIconLoaded(updatedMarker);
mNativeMapView.updateMarker(updatedMarker);
}
+
+ void updatePolygon(Polygon polygon) {
+ if (mDestroyed) {
+ return;
+ }
+
+ if (polygon == null) {
+ Log.w(MapboxConstants.TAG, "polygon was null, doing nothing");
+ return;
+ }
+
+ if (polygon.getId() == -1) {
+ Log.w(MapboxConstants.TAG, "polygon has an id of -1, indicating the polygon was not added to the map yet.");
+ return;
+ }
+
+ mNativeMapView.updatePolygon(polygon);
+ }
+
+ void updatePolyline(Polyline polyline) {
+ if (mDestroyed) {
+ return;
+ }
+
+ if (polyline == null) {
+ Log.w(MapboxConstants.TAG, "polygon was null, doing nothing");
+ return;
+ }
+
+ if (polyline.getId() == -1) {
+ Log.w(MapboxConstants.TAG, "polygon has an id of -1, indicating the polygon was not added to the map yet.");
+ return;
+ }
+
+ mNativeMapView.updatePolyline(polyline);
+ }
+
private void ensureIconLoaded(Marker marker) {
Icon icon = marker.getIcon();
if (icon == null) {
@@ -1021,7 +1098,7 @@ public class MapView extends FrameLayout {
}
long addMarker(@NonNull Marker marker) {
- if(mDestroyed){
+ if (mDestroyed) {
return 0l;
}
return mNativeMapView.addMarker(marker);
@@ -1120,7 +1197,7 @@ public class MapView extends FrameLayout {
int count = annotationList.size();
for (int i = 0; i < count; i++) {
Annotation annotation = annotationList.get(i);
- if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) {
+ if (annotation instanceof MarkerView) {
annotations.add((MarkerView) annotation);
}
}
@@ -1128,6 +1205,13 @@ public class MapView extends FrameLayout {
return new ArrayList<>(annotations);
}
+ /**
+ * @return the ViewGroup containing the marker views
+ */
+ public ViewGroup getMarkerViewContainer() {
+ return mMarkerViewContainer;
+ }
+
int getTopOffsetPixelsForIcon(Icon icon) {
if (mDestroyed) {
@@ -1317,93 +1401,66 @@ public class MapView extends FrameLayout {
return mNativeMapView.getScale();
}
- // This class handles TextureView callbacks
- private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
+ private class SurfaceCallback implements SurfaceHolder.Callback {
private Surface mSurface;
- private View mViewHolder;
-
- private static final int VIEW_MARKERS_POOL_SIZE = 20;
-
- // 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) {
- mNativeMapView.createSurface(mSurface = new Surface(surface));
- mNativeMapView.resizeFramebuffer(width, height);
+ public void surfaceCreated(SurfaceHolder holder) {
+ mNativeMapView.createSurface(mSurface = holder.getSurface());
mHasSurface = true;
}
- // Called when the native surface texture has been destroyed
- // Must do all EGL/GL ES destruction here
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- mHasSurface = false;
-
- if (mNativeMapView != null) {
- mNativeMapView.destroySurface();
- }
- mSurface.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) {
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mDestroyed) {
return;
}
-
mNativeMapView.resizeFramebuffer(width, height);
}
- // Called when the SurfaceTexure frame is drawn to screen
- // Must sync with UI here
@Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- if (mDestroyed) {
- return;
- }
-
- mCompassView.update(getDirection());
- mMyLocationView.update();
- mMapboxMap.getMarkerViewManager().update();
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mHasSurface = false;
- for (InfoWindow infoWindow : mMapboxMap.getInfoWindows()) {
- infoWindow.update();
+ if (mNativeMapView != null) {
+ mNativeMapView.destroySurface();
}
+ mSurface.release();
}
}
- // Used by UserLocationView
- void update() {
- if (mDestroyed) {
- return;
- }
-
- mNativeMapView.update();
- }
-
CameraPosition invalidateCameraPosition() {
if (mDestroyed) {
return new CameraPosition.Builder().build();
}
- return new CameraPosition.Builder(mNativeMapView.getCameraValues()).build();
+ CameraPosition position = new CameraPosition.Builder(mNativeMapView.getCameraValues()).build();
+ mMyLocationView.setCameraPosition(position);
+ return position;
}
double getBearing() {
if (mDestroyed) {
return 0;
}
- return mNativeMapView.getBearing();
+
+ double direction = -mNativeMapView.getBearing();
+
+ while (direction > 360) {
+ direction -= 360;
+ }
+ while (direction < 0) {
+ direction += 360;
+ }
+
+ return direction;
}
void setBearing(float bearing) {
if (mDestroyed) {
return;
}
+ mMyLocationView.setBearing(bearing);
mNativeMapView.setBearing(bearing);
}
@@ -1411,9 +1468,18 @@ public class MapView extends FrameLayout {
if (mDestroyed) {
return;
}
+ mMyLocationView.setBearing(bearing);
mNativeMapView.setBearing(bearing, duration);
}
+ void setBearing(double bearing, float focalX, float focalY) {
+ if (mDestroyed) {
+ return;
+ }
+ mMyLocationView.setBearing(bearing);
+ mNativeMapView.setBearing(bearing, focalX, focalY);
+ }
+
//
// View events
//
@@ -1455,6 +1521,17 @@ public class MapView extends FrameLayout {
private void trackGestureEvent(@NonNull String gestureId, @NonNull float xCoordinate, @NonNull float yCoordinate) {
LatLng tapLatLng = fromScreenLocation(new PointF(xCoordinate, yCoordinate));
+ // NaN and Infinite checks to prevent JSON errors at send to server time
+ if (Double.isNaN(tapLatLng.getLatitude()) || Double.isNaN(tapLatLng.getLongitude())) {
+ Log.d(MapView.class.getSimpleName(), "trackGestureEvent() has a NaN lat or lon. Returning.");
+ return;
+ }
+
+ if (Double.isInfinite(tapLatLng.getLatitude()) || Double.isInfinite(tapLatLng.getLongitude())) {
+ Log.d(MapView.class.getSimpleName(), "trackGestureEvent() has an Infinite lat or lon. Returning.");
+ return;
+ }
+
Hashtable<String, Object> evt = new Hashtable<>();
evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_CLICK);
evt.put(MapboxEvent.ATTRIBUTE_CREATED, MapboxEventManager.generateCreateDate());
@@ -1476,6 +1553,17 @@ public class MapView extends FrameLayout {
private void trackGestureDragEndEvent(@NonNull float xCoordinate, @NonNull float yCoordinate) {
LatLng tapLatLng = fromScreenLocation(new PointF(xCoordinate, yCoordinate));
+ // NaN and Infinite checks to prevent JSON errors at send to server time
+ if (Double.isNaN(tapLatLng.getLatitude()) || Double.isNaN(tapLatLng.getLongitude())) {
+ Log.d(MapView.class.getSimpleName(), "trackGestureDragEndEvent() has a NaN lat or lon. Returning.");
+ return;
+ }
+
+ if (Double.isInfinite(tapLatLng.getLatitude()) || Double.isInfinite(tapLatLng.getLongitude())) {
+ Log.d(MapView.class.getSimpleName(), "trackGestureDragEndEvent() has an Infinite lat or lon. Returning.");
+ return;
+ }
+
Hashtable<String, Object> evt = new Hashtable<>();
evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_DRAGEND);
evt.put(MapboxEvent.ATTRIBUTE_CREATED, MapboxEventManager.generateCreateDate());
@@ -1533,8 +1621,12 @@ public class MapView extends FrameLayout {
|| mShoveGestureDetector.isInProgress();
if (mTwoTap && isTap && !inProgress) {
- PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event);
- zoom(false, focalPoint.x, focalPoint.y);
+ if (mFocalPoint != null) {
+ zoom(false, mFocalPoint.x / mScreenDensity, mFocalPoint.y / mScreenDensity);
+ } else {
+ PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event);
+ zoom(false, focalPoint.x, focalPoint.y);
+ }
mTwoTap = false;
return true;
}
@@ -1593,12 +1685,12 @@ public class MapView extends FrameLayout {
}
// Single finger double tap
- if (mMapboxMap.getTrackingSettings().isLocationTrackingDisabled()) {
+ if (mFocalPoint != null) {
+ // User provided focal point
+ zoom(true, mFocalPoint.x, mFocalPoint.y);
+ } else {
// Zoom in on gesture
zoom(true, e.getX(), e.getY());
- } else {
- // Zoom in on user location view
- zoom(true, mMyLocationView.getCenterX(), mMyLocationView.getCenterY());
}
break;
}
@@ -1673,8 +1765,10 @@ public class MapView extends FrameLayout {
}
}
} else {
- // deselect any selected marker
- mMapboxMap.deselectMarkers();
+ if (mMapboxMap.getUiSettings().isDeselectMarkersOnTap()) {
+ // deselect any selected marker
+ mMapboxMap.deselectMarkers();
+ }
// notify app of map click
MapboxMap.OnMapClickListener listener = mMapboxMap.getOnMapClickListener();
@@ -1742,10 +1836,15 @@ public class MapView extends FrameLayout {
return false;
}
+ if (mDragStarted) {
+ return false;
+ }
+
+ requestDisallowInterceptTouchEvent(true);
+
// reset tracking modes if gesture occurs
resetTrackingModesIfRequired();
-
// Cancel any animation
mNativeMapView.cancelTransitions();
@@ -1816,29 +1915,28 @@ public class MapView extends FrameLayout {
return false;
}
+ if (mDragStarted) {
+ return false;
+ }
+
// Cancel any animation
mNativeMapView.cancelTransitions();
// Gesture is a quickzoom if there aren't two fingers
mQuickZoom = !mTwoTap;
- TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
-
// Scale the map
- if (uiSettings.isScrollGesturesEnabled() && !mQuickZoom && trackingSettings.isLocationTrackingDisabled()) {
+ if (mFocalPoint != null) {
+ // arround user provided focal point
+ mNativeMapView.scaleBy(detector.getScaleFactor(), mFocalPoint.x / mScreenDensity, mFocalPoint.y / mScreenDensity);
+ } else if (mQuickZoom) {
+ // around center map
+ mNativeMapView.scaleBy(detector.getScaleFactor(), (getWidth() / 2) / mScreenDensity, (getHeight() / 2) / mScreenDensity);
+ } else {
// around gesture
mNativeMapView.scaleBy(detector.getScaleFactor(), detector.getFocusX() / mScreenDensity, detector.getFocusY() / mScreenDensity);
- } else {
- if (trackingSettings.isLocationTrackingDisabled()) {
- // around center map
- mNativeMapView.scaleBy(detector.getScaleFactor(), (getWidth() / 2) / mScreenDensity, (getHeight() / 2) / mScreenDensity);
- } else {
- // around user location view
- float x = mMyLocationView.getX() + mMyLocationView.getWidth() / 2;
- float y = mMyLocationView.getY() + mMyLocationView.getHeight() / 2;
- mNativeMapView.scaleBy(detector.getScaleFactor(), x / mScreenDensity, y / mScreenDensity);
- }
}
+
return true;
}
}
@@ -1881,10 +1979,14 @@ public class MapView extends FrameLayout {
return false;
}
+ if (mDragStarted) {
+ return false;
+ }
+
// If rotate is large enough ignore a tap
// Also is zoom already started, don't rotate
mTotalAngle += detector.getRotationDegreesDelta();
- if (!mZoomStarted && ((mTotalAngle > 10.0f) || (mTotalAngle < -10.0f))) {
+ if (!mZoomStarted && ((mTotalAngle > 20.0f) || (mTotalAngle < -20.0f))) {
mStarted = true;
}
@@ -1908,16 +2010,12 @@ public class MapView extends FrameLayout {
bearing += detector.getRotationDegreesDelta();
// Rotate the map
- if (mMapboxMap.getTrackingSettings().isLocationTrackingDisabled()) {
- // around gesture
- mNativeMapView.setBearing(bearing,
- detector.getFocusX() / mScreenDensity,
- detector.getFocusY() / mScreenDensity);
+ if (mFocalPoint != null) {
+ // User provided focal point
+ setBearing(bearing, mFocalPoint.x / mScreenDensity, mFocalPoint.y / mScreenDensity);
} else {
- // around center userlocation
- float x = mMyLocationView.getX() + mMyLocationView.getWidth() / 2;
- float y = mMyLocationView.getY() + mMyLocationView.getHeight() / 2;
- mNativeMapView.setBearing(bearing, x / mScreenDensity, y / mScreenDensity);
+ // around gesture
+ setBearing(bearing, detector.getFocusX() / mScreenDensity, detector.getFocusY() / mScreenDensity);
}
return true;
}
@@ -1950,6 +2048,7 @@ public class MapView extends FrameLayout {
mBeginTime = 0;
mTotalDelta = 0.0f;
mStarted = false;
+ mDragStarted = false;
}
@Override
@@ -1988,6 +2087,8 @@ public class MapView extends FrameLayout {
// Tilt the map
mMapboxMap.setTilt(pitch);
+ mDragStarted = true;
+
return true;
}
}
@@ -2409,6 +2510,13 @@ public class MapView extends FrameLayout {
mMapboxMap.setMyLocationEnabled(true);
}
mMyLocationView.setMyLocationTrackingMode(myLocationTrackingMode);
+
+ if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
+ setFocalPoint(new PointF(mMyLocationView.getCenterX(), mMyLocationView.getCenterY()));
+ } else {
+ setFocalPoint(null);
+ }
+
MapboxMap.OnMyLocationTrackingModeChangeListener listener = mMapboxMap.getOnMyLocationTrackingModeChangeListener();
if (listener != null) {
listener.onMyLocationTrackingModeChange(myLocationTrackingMode);
@@ -2514,45 +2622,19 @@ public class MapView extends FrameLayout {
return mMapboxMap.getUiSettings().getAttributionTintColor();
}
- //
- // Custom layer
- //
-
- @UiThread
- void addCustomLayer(CustomLayer customLayer, String before) {
- if (mDestroyed) {
- return;
- }
- mNativeMapView.addCustomLayer(customLayer, before);
- }
-
- @UiThread
- void removeCustomLayer(String id) {
- if (mDestroyed) {
- return;
- }
- mNativeMapView.removeCustomLayer(id);
- }
-
- @UiThread
- void invalidateCustomLayers() {
- if (mDestroyed) {
- return;
- }
- mNativeMapView.update();
- }
-
/**
* Sets a callback object which will be triggered when the {@link MapboxMap} instance is ready to be used.
*
* @param callback The callback object that will be triggered when the map is ready to be used.
*/
@UiThread
- public void getMapAsync(@NonNull final OnMapReadyCallback callback) {
- if (!mInitialLoad) {
+ public void getMapAsync(final OnMapReadyCallback callback) {
+ if (!mInitialLoad && callback != null) {
callback.onMapReady(mMapboxMap);
} else {
- mOnMapReadyCallbackList.add(callback);
+ if (callback != null) {
+ mOnMapReadyCallbackList.add(callback);
+ }
}
}
@@ -2568,20 +2650,55 @@ public class MapView extends FrameLayout {
return mMyLocationView;
}
+ NativeMapView getNativeMapView() {
+ return mNativeMapView;
+ }
+
+ //
+ // Snapshot API
+ //
+
@UiThread
void snapshot(@NonNull final MapboxMap.SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) {
- TextureView textureView = (TextureView) findViewById(R.id.textureView);
- final boolean canUseBitmap = bitmap != null && textureView.getWidth() == bitmap.getWidth() && textureView.getHeight() == bitmap.getHeight();
+ mSnapshotRequest = new SnapshotRequest(bitmap, callback);
+ mNativeMapView.scheduleTakeSnapshot();
+ mNativeMapView.render();
+ }
- setDrawingCacheEnabled(true);
- Bitmap content = Bitmap.createBitmap(getDrawingCache());
- setDrawingCacheEnabled(false);
+ // Called when the snapshot method was executed
+ // Called via JNI from NativeMapView
+ // Forward to any listeners
+ protected void onSnapshotReady(byte[] bytes) {
+ if (mSnapshotRequest != null && bytes != null) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inBitmap = mSnapshotRequest.getBitmap(); // the old Bitmap to be reused
+ options.inMutable = true;
+ options.inSampleSize = 1;
+ Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
+
+ MapboxMap.SnapshotReadyCallback callback = mSnapshotRequest.getCallback();
+ if (callback != null) {
+ callback.onSnapshotReady(bitmap);
+ }
+ }
+ }
+
+ private class SnapshotRequest {
+ private Bitmap bitmap;
+ private MapboxMap.SnapshotReadyCallback callback;
+
+ public SnapshotRequest(Bitmap bitmap, MapboxMap.SnapshotReadyCallback callback) {
+ this.bitmap = bitmap;
+ this.callback = callback;
+ }
+
+ public Bitmap getBitmap() {
+ return bitmap;
+ }
- Bitmap output = Bitmap.createBitmap(content.getWidth(), content.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
- canvas.drawBitmap(canUseBitmap ? textureView.getBitmap(bitmap) : textureView.getBitmap(), 0, 0, null);
- canvas.drawBitmap(content, new Matrix(), null);
- callback.onSnapshotReady(output);
+ public MapboxMap.SnapshotReadyCallback getCallback() {
+ return callback;
+ }
}
//
@@ -2700,41 +2817,6 @@ public class MapView extends FrameLayout {
}
/**
- * Class responsible for managing state of Style loading.
- */
- static class StyleInitializer {
-
- private String mStyle;
- private boolean mDefaultStyle;
-
- StyleInitializer(@NonNull Context context) {
- mStyle = Style.getMapboxStreetsUrl(context.getResources().getInteger(R.integer.style_version));
- mDefaultStyle = true;
- }
-
- void setStyle(@NonNull String style) {
- setStyle(style, false);
- }
-
- void setStyle(@NonNull String style, boolean defaultStyle) {
- if (style == null) {
- // don't override default style
- return;
- }
- mStyle = style;
- mDefaultStyle = defaultStyle;
- }
-
- public String getStyle() {
- return mStyle;
- }
-
- boolean isDefaultStyle() {
- return mDefaultStyle;
- }
- }
-
- /**
* Definition of a map change event.
*
* @see MapView.OnMapChangedListener#onMapChanged(int)
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 fbe6a31f6a..26f13ed353 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
@@ -38,9 +38,10 @@ import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.layers.CustomLayer;
-import com.mapbox.mapboxsdk.location.LocationListener;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
+import com.mapbox.mapboxsdk.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
+import com.mapbox.mapboxsdk.style.sources.Source;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
@@ -53,10 +54,11 @@ import java.util.concurrent.TimeUnit;
* you must obtain one from the getMapAsync() method on a MapFragment or MapView that you have
* added to your application.
* <p>
- * Note: Similar to a View object, a GoogleMap should only be read and modified from the main thread.
+ * Note: Similar to a View object, a MapboxMap should only be read and modified from the main thread.
* </p>
*/
public class MapboxMap {
+ private static final String TAG = MapboxMap.class.getSimpleName();
private MapView mMapView;
private UiSettings mUiSettings;
@@ -72,7 +74,6 @@ public class MapboxMap {
private List<InfoWindow> mInfoWindows;
private MapboxMap.InfoWindowAdapter mInfoWindowAdapter;
- private Bitmap mViewMarkerBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
private boolean mMyLocationEnabled;
private boolean mAllowConcurrentMultipleInfoWindows;
@@ -105,6 +106,54 @@ public class MapboxMap {
mMarkerViewManager = new MarkerViewManager(this, mapView);
}
+ // Style
+
+ @Nullable
+ @UiThread
+ public Layer getLayer(@NonNull String layerId) {
+ return getMapView().getNativeMapView().getLayer(layerId);
+ }
+
+ /**
+ * Tries to cast the Layer to T, returns null if it's another type
+ */
+ @Nullable
+ @UiThread
+ public <T extends Layer> T getLayerAs(@NonNull String layerId) {
+ try {
+ //noinspection unchecked
+ return (T) getMapView().getNativeMapView().getLayer(layerId);
+ } catch (ClassCastException e) {
+ Log.e(TAG, String.format("Layer: %s is a different type: %s", layerId, e.getMessage()));
+ return null;
+ }
+ }
+
+ @UiThread
+ public void addLayer(@NonNull Layer layer) {
+ addLayer(layer, null);
+ }
+
+ @UiThread
+ public void addLayer(@NonNull Layer layer, String before) {
+ getMapView().getNativeMapView().addLayer(layer, before);
+ }
+
+ @UiThread
+ public void removeLayer(@NonNull String layerId) throws NoSuchLayerException {
+ getMapView().getNativeMapView().removeLayer(layerId);
+ }
+
+ @UiThread
+ public void addSource(@NonNull Source source) {
+ getMapView().getNativeMapView().addSource(source);
+ }
+
+ @UiThread
+ public void removeSource(@NonNull String sourceId) {
+ getMapView().getNativeMapView().removeSource(sourceId);
+ }
+
//
// MinZoom
//
@@ -291,7 +340,7 @@ public class MapboxMap {
* it will return the current location of the camera in flight.
*
* @param update The change that should be applied to the camera.
- * @see {@link CameraUpdateFactory} for a set of updates.
+ * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
@UiThread
public final void easeCamera(CameraUpdate update) {
@@ -306,7 +355,7 @@ public class MapboxMap {
* @param update The change that should be applied to the camera.
* @param durationMs The duration of the animation in milliseconds. This must be strictly
* positive, otherwise an IllegalArgumentException will be thrown.
- * @see {@link CameraUpdateFactory} for a set of updates.
+ * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
@UiThread
public final void easeCamera(CameraUpdate update, int durationMs) {
@@ -327,7 +376,7 @@ public class MapboxMap {
* will be notified with onFinish(). If the animation stops due to interruption
* by a later camera movement or a user gesture, onCancel() will be called.
* Do not update or ease the camera from within onCancel().
- * @see {@link CameraUpdateFactory} for a set of camera updates.
+ * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
@UiThread
public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
@@ -368,7 +417,7 @@ public class MapboxMap {
* of the camera in flight.
*
* @param update The change that should be applied to the camera.
- * @see {@link CameraUpdateFactory} for a set of updates.
+ * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update) {
@@ -385,7 +434,7 @@ public class MapboxMap {
* @param callback The callback to invoke from the main thread when the animation stops. If the
* animation completes normally, onFinish() is called; otherwise, onCancel() is
* called. Do not update or animate the camera from within onCancel().
- * @see {@link CameraUpdateFactory} for a set of updates.
+ * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) {
@@ -401,7 +450,7 @@ public class MapboxMap {
* @param update The change that should be applied to the camera.
* @param durationMs The duration of the animation in milliseconds. This must be strictly
* positive, otherwise an IllegalArgumentException will be thrown.
- * @see {@link CameraUpdateFactory} for a set of updates.
+ * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update, int durationMs) {
@@ -424,7 +473,7 @@ public class MapboxMap {
* by a later camera movement or a user gesture, onCancel() will be called.
* Do not update or animate the camera from within onCancel(). If a callback
* isn't required, leave it as null.
- * @see {@link CameraUpdateFactory} for a set of updates.
+ * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
@UiThread
public final void animateCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
@@ -550,7 +599,7 @@ public class MapboxMap {
* <li>{@code asset://...}:
* reads the style from the APK {@code assets/} directory.
* This is used to load a style bundled with your app.</li>
- * <li>{@code null}: loads the default {@link Style#getMapboxStreetsUrl(int)} style.</li>
+ * <li>{@code null}: loads the default {@link Style#MAPBOX_STREETS} style.</li>
* </ul>
* <p>
* This method is asynchronous and will return immediately before the style finishes loading.
@@ -703,10 +752,11 @@ public class MapboxMap {
@NonNull
public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) {
MarkerView marker = prepareViewMarker(markerOptions);
- long id = mMapView.addMarker(marker);
marker.setMapboxMap(this);
+ long id = mMapView.addMarker(marker);
marker.setId(id);
mAnnotations.put(id, marker);
+ mMarkerViewManager.invalidateViewMarkersInBounds();
return marker;
}
@@ -777,6 +827,36 @@ public class MapboxMap {
}
/**
+ * Update a polygon on this map.
+ *
+ * @param polygon An updated polygon object.
+ */
+ @UiThread
+ public void updatePolygon(Polygon polygon) {
+ mMapView.updatePolygon(polygon);
+
+ int index = mAnnotations.indexOfKey(polygon.getId());
+ if (index > -1) {
+ mAnnotations.setValueAt(index, polygon);
+ }
+ }
+
+ /**
+ * Update a polyline on this map.
+ *
+ * @param polyline An updated polyline object.
+ */
+ @UiThread
+ public void updatePolyline(Polyline polyline) {
+ mMapView.updatePolyline(polyline);
+
+ int index = mAnnotations.indexOfKey(polyline.getId());
+ if (index > -1) {
+ mAnnotations.setValueAt(index, polyline);
+ }
+ }
+
+ /**
* Adds a polyline to this map.
*
* @param polylineOptions A polyline options object that defines how to render the polyline.
@@ -952,7 +1032,7 @@ public class MapboxMap {
Marker marker = (Marker) annotation;
marker.hideInfoWindow();
if (marker instanceof MarkerView) {
- mMarkerViewManager.removeMarkerView((MarkerView) marker, true);
+ mMarkerViewManager.removeMarkerView((MarkerView) marker);
}
}
long id = annotation.getId();
@@ -986,7 +1066,7 @@ public class MapboxMap {
Marker marker = (Marker) annotation;
marker.hideInfoWindow();
if (marker instanceof MarkerView) {
- mMarkerViewManager.removeMarkerView((MarkerView) marker, true);
+ mMarkerViewManager.removeMarkerView((MarkerView) marker);
}
}
ids[i] = annotationList.get(i).getId();
@@ -1012,7 +1092,7 @@ public class MapboxMap {
Marker marker = (Marker) annotation;
marker.hideInfoWindow();
if (marker instanceof MarkerView) {
- mMarkerViewManager.removeMarkerView((MarkerView) marker, true);
+ mMarkerViewManager.removeMarkerView((MarkerView) marker);
}
}
}
@@ -1143,6 +1223,11 @@ public class MapboxMap {
}
if (!handledDefaultClick) {
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.select((MarkerView) marker, false);
+ mMarkerViewManager.ensureInfoWindowOffset((MarkerView) marker);
+ }
+
if (isInfoWindowValidForMarker(marker) || getInfoWindowAdapter() != null) {
mInfoWindows.add(marker.showInfoWindow(this, mMapView));
}
@@ -1166,7 +1251,7 @@ public class MapboxMap {
}
if (marker instanceof MarkerView) {
- mMarkerViewManager.deselect((MarkerView) marker);
+ mMarkerViewManager.deselect((MarkerView) marker, false);
}
}
@@ -1187,6 +1272,10 @@ public class MapboxMap {
marker.hideInfoWindow();
}
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.deselect((MarkerView) marker, false);
+ }
+
mSelectedMarkers.remove(marker);
}
@@ -1209,7 +1298,11 @@ public class MapboxMap {
private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) {
MarkerView marker = markerViewOptions.getMarker();
- Icon icon = IconFactory.recreate("markerViewSettings", mViewMarkerBitmap);
+
+ Icon icon = markerViewOptions.getIcon();
+ if (icon == null) {
+ icon = IconFactory.getInstance(mMapView.getContext()).defaultMarkerView();
+ }
marker.setIcon(icon);
return marker;
}
@@ -1287,9 +1380,11 @@ public class MapboxMap {
//
/**
+ * <p>
* Sets the distance from the edges of the map view’s frame to the edges of the map
* view’s logical viewport.
- * <p/>
+ * </p>
+ * <p>
* When the value of this property is equal to {0,0,0,0}, viewport
* properties such as `centerCoordinate` assume a viewport that matches the map
* view’s frame. Otherwise, those properties are inset, excluding part of the
@@ -1533,10 +1628,8 @@ public class MapboxMap {
*
* @param listener The callback that's invoked when the user clicks on a marker.
* To unset the callback, use null.
- * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.location.LocationServices#addLocationListener(LocationListener)})}
*/
@UiThread
- @Deprecated
public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) {
mMapView.setOnMyLocationChangeListener(listener);
}
@@ -1573,34 +1666,6 @@ public class MapboxMap {
return mOnMyBearingTrackingModeChangeListener;
}
- //
- // Custom layer
- //
-
- /**
- * Do not use this method, experimental feature.
- */
- @UiThread
- public void addCustomLayer(CustomLayer customLayer, String before) {
- mMapView.addCustomLayer(customLayer, before);
- }
-
- /**
- * Do not use this method, experimental feature.
- */
- @UiThread
- public void removeCustomLayer(String id) {
- mMapView.removeCustomLayer(id);
- }
-
- /**
- * Do not use this method, experimental feature.
- */
- @UiThread
- public void invalidateCustomLayers() {
- mMapView.invalidateCustomLayers();
- }
-
MapView getMapView() {
return mMapView;
}
@@ -1621,7 +1686,7 @@ public class MapboxMap {
* Triggers an invalidation of the map view.
*/
public void invalidate() {
- mMapView.update();
+ mMapView.invalidate();
}
/**
@@ -1826,7 +1891,7 @@ public class MapboxMap {
public MarkerViewAdapter(Context context) {
this.context = context;
persistentClass = (Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
- mViewReusePool = new Pools.SimplePool<>(20);
+ mViewReusePool = new Pools.SimplePool<>(10000);
}
/**
@@ -1841,11 +1906,56 @@ public class MapboxMap {
public abstract View getView(@NonNull U marker, @NonNull View convertView, @NonNull ViewGroup parent);
/**
+ * Called when an MarkerView is removed from the MapView or the View object is going to be reused.
+ * <p>
+ * <p>
+ * This method should be used to reset an animated view back to it's original state for view reuse.
+ * </p>
+ * <p>
+ * Returning true indicates you want to the view reuse to be handled automatically.
+ * Returning false indicates you want to perform an animation and you are required calling {@link #releaseView(View)} yourself.
+ * </p>
+ *
+ * @param marker the model representing the MarkerView
+ * @param convertView the reusable view
+ * @return true if you want reuse to occur automatically, false if you want to manage this yourself.
+ */
+ public boolean prepareViewForReuse(@NonNull MarkerView marker, @NonNull View convertView) {
+ return true;
+ }
+
+ /**
+ * Called when a MarkerView is selected from the MapView.
+ * <p>
+ * Returning true from this method indicates you want to move the MarkerView to the selected state.
+ * Returning false indicates you want to animate the View first an manually select the MarkerView when appropriate.
+ * </p>
+ *
+ * @param marker the model representing the MarkerView
+ * @param convertView the reusable view
+ * @param reselectionFromRecycling indicates if the onSelect callback is the initial selection
+ * callback or that selection occurs due to recreation of selected marker
+ * @return true if you want to select the Marker immediately, false if you want to manage this yourself.
+ */
+ public boolean onSelect(@NonNull U marker, @NonNull View convertView, boolean reselectionFromRecycling) {
+ return true;
+ }
+
+ /**
+ * Called when a MarkerView is deselected from the MapView.
+ *
+ * @param marker the model representing the MarkerView
+ * @param convertView the reusable view
+ */
+ public void onDeselect(@NonNull U marker, @NonNull View convertView) {
+ }
+
+ /**
* Returns the generic type of the used MarkerView.
*
* @return the generic type
*/
- public Class<U> getMarkerClass() {
+ public final Class<U> getMarkerClass() {
return persistentClass;
}
@@ -1854,7 +1964,7 @@ public class MapboxMap {
*
* @return the pool associated to this adapter
*/
- public Pools.SimplePool<View> getViewReusePool() {
+ public final Pools.SimplePool<View> getViewReusePool() {
return mViewReusePool;
}
@@ -1863,9 +1973,19 @@ public class MapboxMap {
*
* @return the context used
*/
- public Context getContext() {
+ public final Context getContext() {
return context;
}
+
+ /**
+ * Release a View to the ViewPool.
+ *
+ * @param view the view to be released
+ */
+ public final void releaseView(View view) {
+ view.setVisibility(View.GONE);
+ mViewReusePool.release(view);
+ }
}
/**
@@ -1890,9 +2010,7 @@ public class MapboxMap {
* Interface definition for a callback to be invoked when the the My Location view changes location.
*
* @see MapboxMap#setOnMyLocationChangeListener(OnMyLocationChangeListener)
- * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.location.LocationListener}
*/
- @Deprecated
public interface OnMyLocationChangeListener {
/**
* Called when the location of the My Location view has changed
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 17593129e7..0fd6b1390d 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
@@ -13,10 +13,12 @@ import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.Gravity;
+
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.utils.ColorUtils;
+
import java.util.Arrays;
/**
@@ -176,17 +178,17 @@ public class MapboxMapOptions implements Parcelable {
mapboxMapOptions.myLocationBackgroundTintColor(typedArray.getColor(R.styleable.MapView_my_location_background_tint, Color.TRANSPARENT));
Drawable foregroundDrawable = typedArray.getDrawable(R.styleable.MapView_my_location_foreground);
- if(foregroundDrawable==null){
- foregroundDrawable = ContextCompat.getDrawable(context,R.drawable.ic_mylocationview_normal);
+ if (foregroundDrawable == null) {
+ foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.ic_mylocationview_normal);
}
Drawable foregroundBearingDrawable = typedArray.getDrawable(R.styleable.MapView_my_location_foreground_bearing);
- if(foregroundBearingDrawable==null){
- foregroundBearingDrawable = ContextCompat.getDrawable(context,R.drawable.ic_mylocationview_bearing);
+ if (foregroundBearingDrawable == null) {
+ foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.ic_mylocationview_bearing);
}
Drawable backgroundDrawable = typedArray.getDrawable(R.styleable.MapView_my_location_background);
- if(backgroundDrawable==null){
+ if (backgroundDrawable == null) {
backgroundDrawable = ContextCompat.getDrawable(context, R.drawable.ic_mylocationview_background);
}
@@ -223,6 +225,7 @@ public class MapboxMapOptions implements Parcelable {
*
* @param accessToken Token to be used to access the service
* @return This
+ * @deprecated As of release 4.1.0, replaced by {@link com.mapbox.mapboxsdk.MapboxAccountManager#start(Context, String)}
*/
@Deprecated
public MapboxMapOptions accessToken(String accessToken) {
@@ -451,19 +454,39 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * Set the foreground drawables of the MyLocationView.
*
- * @param myLocationForegroundDrawable
- * @param myLocationBearingDrawable
+ * @param myLocationForegroundDrawable the drawable to show as foreground without bearing
+ * @param myLocationBearingDrawable the drawable to show as foreground when bearing is disabled
* @return This
*/
- public MapboxMapOptions myLocationForegroundDrawables(Drawable myLocationForegroundDrawable, Drawable myLocationBearingDrawable ) {
+ public MapboxMapOptions myLocationForegroundDrawables(Drawable myLocationForegroundDrawable, Drawable myLocationBearingDrawable) {
this.myLocationForegroundDrawable = myLocationForegroundDrawable;
this.myLocationForegroundBearingDrawable = myLocationBearingDrawable;
return this;
}
/**
- * @param myLocationBackgroundDrawable
+ * Set the foreground drawable of the MyLocationView.
+ * <p>
+ * The same drawable will be used for both bearing as non bearing modes.
+ * </p>
+ *
+ * @param myLocationForegroundDrawable the drawable to show as foreground
+ * @return This
+ */
+ public MapboxMapOptions myLocationForegroundDrawable(Drawable myLocationForegroundDrawable) {
+ this.myLocationForegroundDrawable = myLocationForegroundDrawable;
+ return this;
+ }
+
+ /**
+ * Set the background drawable of MyLocationView.
+ * <p>
+ * Padding can be added to provide an offset to the background.
+ * </p>
+ *
+ * @param myLocationBackgroundDrawable the drawable to show as background
* @return This
*/
public MapboxMapOptions myLocationBackgroundDrawable(Drawable myLocationBackgroundDrawable) {
@@ -472,7 +495,12 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * @param myLocationForegroundTintColor
+ * Set the foreground tint color of MyLocationView.
+ * <p>
+ * The color will tint both the foreground and the bearing foreground drawable.
+ * </p>
+ *
+ * @param myLocationForegroundTintColor the color to tint the foreground drawable
* @return This
*/
public MapboxMapOptions myLocationForegroundTintColor(@ColorInt int myLocationForegroundTintColor) {
@@ -481,7 +509,9 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * @param myLocationBackgroundTintColor
+ * Set the background tint color of MyLocationView.
+ *
+ * @param myLocationBackgroundTintColor the color to tint the background
* @return This
*/
public MapboxMapOptions myLocationBackgroundTintColor(@ColorInt int myLocationBackgroundTintColor) {
@@ -490,7 +520,9 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * @param myLocationBackgroundPadding
+ * Set the MyLocationView padding.
+ *
+ * @param myLocationBackgroundPadding the color to tint the background
* @return This
*/
public MapboxMapOptions myLocationBackgroundPadding(int[] myLocationBackgroundPadding) {
@@ -499,7 +531,9 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * @param myLocationAccuracyTintColor
+ * Set the MyLocationView accuracy circle tint color.
+ *
+ * @param myLocationAccuracyTintColor the color to tint the accuracy circle
* @return This
*/
public MapboxMapOptions myLocationAccuracyTint(@ColorInt int myLocationAccuracyTintColor) {
@@ -508,7 +542,9 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * @param alpha
+ * Set the MyLocationView accuracy alpha value.
+ *
+ * @param alpha the alpha value
* @return This
*/
public MapboxMapOptions myLocationAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) {
@@ -691,6 +727,11 @@ public class MapboxMapOptions implements Parcelable {
return attributionMargins;
}
+ /**
+ * Get the current configured tint color for attribution for a map view.
+ *
+ * @return the tint color
+ */
@ColorInt
public int getAttributionTintColor() {
return attributionTintColor;
@@ -706,56 +747,72 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * @return
+ * Get the current configured MyLocationView foreground drawable.
+ *
+ * @return the drawable used as foreground
*/
public Drawable getMyLocationForegroundDrawable() {
return myLocationForegroundDrawable;
}
/**
- * @return
+ * Get the current configured MyLocationView foreground bearing drawable.
+ *
+ * @return the drawable used as foreground when bearing is enabled
*/
public Drawable getMyLocationForegroundBearingDrawable() {
return myLocationForegroundBearingDrawable;
}
/**
- * @return
+ * Get the current configured MyLocationView background drawable.
+ *
+ * @return the drawable used as background
*/
public Drawable getMyLocationBackgroundDrawable() {
return myLocationBackgroundDrawable;
}
/**
- * @return
+ * Get the current configured MyLocationView foreground tint color.
+ *
+ * @return the tint color
*/
public int getMyLocationForegroundTintColor() {
return myLocationForegroundTintColor;
}
/**
- * @return
+ * Get the current configured MyLocationView background tint color.
+ *
+ * @return the tint color
*/
public int getMyLocationBackgroundTintColor() {
return myLocationBackgroundTintColor;
}
/**
- * @return
+ * Get the current configured MyLocationView background padding.
+ *
+ * @return an array describing the padding in a LTRB manner
*/
public int[] getMyLocationBackgroundPadding() {
return myLocationBackgroundPadding;
}
/**
- * @return
+ * Get the current configured MyLocationView accuracy circle color tint value.
+ *
+ * @return the tint color
*/
public int getMyLocationAccuracyTintColor() {
return myLocationAccuracyTintColor;
}
/**
- * @return
+ * Get the current configured MyLocationView accuracy circle alpha value.
+ *
+ * @return the alpha value
*/
public int getMyLocationAccuracyAlpha() {
return myLocationAccuracyAlpha;
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 6c092ee0c8..929f515d8d 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
@@ -5,15 +5,21 @@ import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.view.Surface;
+import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
-import com.mapbox.mapboxsdk.layers.CustomLayer;
+import com.mapbox.mapboxsdk.offline.OfflineManager;
+import com.mapbox.mapboxsdk.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
+import com.mapbox.mapboxsdk.style.sources.Source;
import java.util.List;
@@ -50,7 +56,7 @@ final class NativeMapView {
public NativeMapView(MapView mapView) {
Context context = mapView.getContext();
- String dataPath = context.getFilesDir().getAbsolutePath();
+ String dataPath = OfflineManager.getDatabasePath(context);
// With the availability of offline, we're unifying the ambient (cache) and the offline
// databases to be in the same folder, outside cache, to avoid automatic deletion from
@@ -230,7 +236,7 @@ final class NativeMapView {
}
public void setLatLng(LatLng latLng, long duration) {
- nativeSetLatLng(mNativeMapViewPtr, latLng, duration);
+ nativeSetLatLng(mNativeMapViewPtr, latLng.getLatitude(), latLng.getLongitude(), duration);
}
public LatLng getLatLng() {
@@ -343,7 +349,7 @@ final class NativeMapView {
}
public long addMarker(Marker marker) {
- Marker[] markers = { marker };
+ Marker[] markers = {marker};
return nativeAddMarkers(mNativeMapViewPtr, markers)[0];
}
@@ -352,7 +358,7 @@ final class NativeMapView {
}
public long addPolyline(Polyline polyline) {
- Polyline[] polylines = { polyline };
+ Polyline[] polylines = {polyline};
return nativeAddPolylines(mNativeMapViewPtr, polylines)[0];
}
@@ -361,7 +367,7 @@ final class NativeMapView {
}
public long addPolygon(Polygon polygon) {
- Polygon[] polygons = { polygon };
+ Polygon[] polygons = {polygon};
return nativeAddPolygons(mNativeMapViewPtr, polygons)[0];
}
@@ -370,11 +376,25 @@ final class NativeMapView {
}
public void updateMarker(Marker marker) {
- nativeUpdateMarker(mNativeMapViewPtr, marker);
+ LatLng position = marker.getPosition();
+ Icon icon = marker.getIcon();
+ nativeUpdateMarker(mNativeMapViewPtr, marker.getId(), position.getLatitude(), position.getLongitude(), icon.getId());
+ }
+
+ public void updatePolygon(Polygon polygon) {
+ //TODO remove new id assignment, https://github.com/mapbox/mapbox-gl-native/issues/5844
+ long newId = nativeUpdatePolygon(mNativeMapViewPtr, polygon.getId(), polygon);
+ polygon.setId(newId);
+ }
+
+ public void updatePolyline(Polyline polyline) {
+ //TODO remove new id assignment, https://github.com/mapbox/mapbox-gl-native/issues/5844
+ long newId = nativeUpdatePolyline(mNativeMapViewPtr, polyline.getId(), polyline);
+ polyline.setId(newId);
}
public void removeAnnotation(long id) {
- long[] ids = { id };
+ long[] ids = {id};
removeAnnotations(ids);
}
@@ -423,19 +443,19 @@ final class NativeMapView {
}
public ProjectedMeters projectedMetersForLatLng(LatLng latLng) {
- return nativeProjectedMetersForLatLng(mNativeMapViewPtr, latLng);
+ return nativeProjectedMetersForLatLng(mNativeMapViewPtr, latLng.getLatitude(), latLng.getLongitude());
}
public LatLng latLngForProjectedMeters(ProjectedMeters projectedMeters) {
- return nativeLatLngForProjectedMeters(mNativeMapViewPtr, projectedMeters);
+ return nativeLatLngForProjectedMeters(mNativeMapViewPtr, projectedMeters.getNorthing(), projectedMeters.getEasting());
}
public PointF pixelForLatLng(LatLng latLng) {
- return nativePixelForLatLng(mNativeMapViewPtr, latLng);
+ return nativePixelForLatLng(mNativeMapViewPtr, latLng.getLatitude(), latLng.getLongitude());
}
public LatLng latLngForPixel(PointF pixel) {
- return nativeLatLngForPixel(mNativeMapViewPtr, pixel);
+ return nativeLatLngForPixel(mNativeMapViewPtr, pixel.x, pixel.y);
}
public double getTopOffsetPixelsForAnnotationSymbol(String symbolName) {
@@ -443,27 +463,46 @@ final class NativeMapView {
}
public void jumpTo(double angle, LatLng center, double pitch, double zoom) {
- nativeJumpTo(mNativeMapViewPtr, angle, center, pitch, zoom);
+ nativeJumpTo(mNativeMapViewPtr, angle, center.getLatitude(), center.getLongitude(), pitch, zoom);
}
public void easeTo(double angle, LatLng center, long duration, double pitch, double zoom, boolean easingInterpolator) {
- nativeEaseTo(mNativeMapViewPtr, angle, center, duration, pitch, zoom, easingInterpolator);
+ nativeEaseTo(mNativeMapViewPtr, angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom, easingInterpolator);
}
public void flyTo(double angle, LatLng center, long duration, double pitch, double zoom) {
- nativeFlyTo(mNativeMapViewPtr, angle, center, duration, pitch, zoom);
+ nativeFlyTo(mNativeMapViewPtr, angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom);
}
- public void addCustomLayer(CustomLayer customLayer, String before) {
- nativeAddCustomLayer(mNativeMapViewPtr, customLayer, before);
+ public double[] getCameraValues() {
+ return nativeGetCameraValues(mNativeMapViewPtr);
}
- public void removeCustomLayer(String id) {
- nativeRemoveCustomLayer(mNativeMapViewPtr, id);
+ // Runtime style Api
+
+ public Layer getLayer(String layerId) {
+ return nativeGetLayer(mNativeMapViewPtr, layerId);
}
- public double[] getCameraValues(){
- return nativeGetCameraValues(mNativeMapViewPtr);
+ public void addLayer(@NonNull Layer layer, @Nullable String before) {
+ nativeAddLayer(mNativeMapViewPtr, layer.getNativePtr(), before);
+ layer.invalidate();
+ }
+
+ public void removeLayer(@NonNull String layerId) throws NoSuchLayerException {
+ nativeRemoveLayer(mNativeMapViewPtr, layerId);
+ }
+
+ public void addSource(@NonNull Source source) {
+ nativeAddSource(mNativeMapViewPtr, source.getId(), source);
+ }
+
+ public void removeSource(@NonNull String sourceId) {
+ nativeRemoveSource(mNativeMapViewPtr, sourceId);
+ }
+
+ public void scheduleTakeSnapshot() {
+ nativeScheduleTakeSnapshot(mNativeMapViewPtr);
}
//
@@ -482,6 +521,10 @@ final class NativeMapView {
mMapView.onFpsChanged(fps);
}
+ protected void onSnapshotReady(byte[] bytes) {
+ mMapView.onSnapshotReady(bytes);
+ }
+
//
// JNI methods
//
@@ -539,7 +582,7 @@ final class NativeMapView {
private native void nativeMoveBy(long nativeMapViewPtr, double dx,
double dy, long duration);
- private native void nativeSetLatLng(long nativeMapViewPtr, LatLng latLng,
+ private native void nativeSetLatLng(long nativeMapViewPtr, double latitude, double longitude,
long duration);
private native LatLng nativeGetLatLng(long nativeMapViewPtr);
@@ -588,7 +631,7 @@ final class NativeMapView {
private native void nativeResetNorth(long nativeMapViewPtr);
- private native void nativeUpdateMarker(long nativeMapViewPtr, Marker marker);
+ private native void nativeUpdateMarker(long nativeMapViewPtr, long markerId, double lat, double lon, String iconId);
private native long[] nativeAddMarkers(long nativeMapViewPtr, Marker[] markers);
@@ -620,25 +663,37 @@ final class NativeMapView {
private native double nativeGetMetersPerPixelAtLatitude(long nativeMapViewPtr, double lat, double zoom);
- private native ProjectedMeters nativeProjectedMetersForLatLng(long nativeMapViewPtr, LatLng latLng);
+ private native ProjectedMeters nativeProjectedMetersForLatLng(long nativeMapViewPtr, double latitude, double longitude);
- private native LatLng nativeLatLngForProjectedMeters(long nativeMapViewPtr, ProjectedMeters projectedMeters);
+ private native LatLng nativeLatLngForProjectedMeters(long nativeMapViewPtr, double northing, double easting);
- private native PointF nativePixelForLatLng(long nativeMapViewPtr, LatLng latLng);
+ private native PointF nativePixelForLatLng(long nativeMapViewPtr, double lat, double lon);
- private native LatLng nativeLatLngForPixel(long nativeMapViewPtr, PointF pixel);
+ private native LatLng nativeLatLngForPixel(long nativeMapViewPtr, float x, float y);
private native double nativeGetTopOffsetPixelsForAnnotationSymbol(long nativeMapViewPtr, String symbolName);
- private native void nativeJumpTo(long nativeMapViewPtr, double angle, LatLng center, double pitch, double zoom);
+ private native void nativeJumpTo(long nativeMapViewPtr, double angle, double latitude, double longitude, double pitch, double zoom);
- private native void nativeEaseTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom, boolean easingInterpolator);
+ private native void nativeEaseTo(long nativeMapViewPtr, double angle, double latitude, double longitude, long duration, double pitch, double zoom, boolean easingInterpolator);
- private native void nativeFlyTo(long nativeMapViewPtr, double angle, LatLng center, long duration, double pitch, double zoom);
+ private native void nativeFlyTo(long nativeMapViewPtr, double angle, double latitude, double longitude, long duration, double pitch, double zoom);
- private native void nativeAddCustomLayer(long nativeMapViewPtr, CustomLayer customLayer, String before);
+ private native double[] nativeGetCameraValues(long mNativeMapViewPtr);
- private native void nativeRemoveCustomLayer(long nativeMapViewPtr, String id);
+ private native Layer nativeGetLayer(long nativeMapViewPtr, String layerId);
- private native double[] nativeGetCameraValues(long mNativeMapViewPtr);
+ private native void nativeAddLayer(long nativeMapViewPtr, long layerPtr, String before);
+
+ private native void nativeRemoveLayer(long nativeMapViewPtr, String layerId) throws NoSuchLayerException;
+
+ private native void nativeAddSource(long mNativeMapViewPtr, String id, Source source);
+
+ private native void nativeRemoveSource(long mNativeMapViewPtr, String sourceId);
+
+ private native long nativeUpdatePolygon(long nativeMapViewPtr, long polygonId, Polygon polygon);
+
+ private native long nativeUpdatePolyline(long nativeMapviewPtr, long polylineId, Polyline polyline);
+
+ private native void nativeScheduleTakeSnapshot(long nativeMapViewPtr);
}
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 d37c3a02ea..a41b2606a5 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
@@ -31,7 +31,7 @@ public class Projection {
* @param latitude The latitude for which to return the value.
* @return The distance measured in meters.
*/
- public double getMetersPerPixelAtLatitude(@FloatRange(from = -180, to = 180) double latitude) {
+ public double getMetersPerPixelAtLatitude(@FloatRange(from = -90, to = 90) double latitude) {
return mMapView.getMetersPerPixelAtLatitude(latitude);
}
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 30492bc421..f2a49f102b 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
@@ -101,7 +101,7 @@ public class TrackingSettings {
*
* @return True to indicate the tracking modes will be dismissed.
* @deprecated use @link #isAllDismissTrackingOnGestureinstead
- */
+ */
@Deprecated
public boolean isDismissTrackingOnGesture() {
return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture;
@@ -215,7 +215,7 @@ public class TrackingSettings {
private void validateGesturesForBearingTrackingMode() {
int myBearingTrackingMode = getMyBearingTrackingMode();
if (!dismissBearingTrackingOnGesture) {
- if (myBearingTrackingMode == MyBearingTracking.NONE) {
+ if (myBearingTrackingMode == MyBearingTracking.NONE || myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
uiSettings.setRotateGesturesEnabled(true);
} else {
uiSettings.setRotateGesturesEnabled(false);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
index 3cd9efb13e..4ce631fc3e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
@@ -1,7 +1,9 @@
package com.mapbox.mapboxsdk.maps;
+import android.graphics.PointF;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.view.Gravity;
import android.view.View;
@@ -33,6 +35,10 @@ public class UiSettings {
private boolean zoomControlsEnabled;
+ private boolean deselectMarkersOnTap = true;
+
+ private PointF focalPoint;
+
UiSettings(@NonNull MapView mapView) {
this.mapView = mapView;
this.compassSettings = new ViewSettings();
@@ -484,6 +490,26 @@ public class UiSettings {
}
/**
+ * Gets whether the markers are automatically deselected (and therefore, their infowindows
+ * closed) when a map tap is detected.
+
+ * @return If true, markers are deselected on a map tap.
+ */
+ public boolean isDeselectMarkersOnTap() {
+ return deselectMarkersOnTap;
+ }
+
+ /**
+ * Sets whether the markers are automatically deselected (and therefore, their infowindows
+ * closed) when a map tap is detected.
+ *
+ * @param deselectMarkersOnTap
+ */
+ public void setDeselectMarkersOnTap(boolean deselectMarkersOnTap) {
+ this.deselectMarkersOnTap = deselectMarkersOnTap;
+ }
+
+ /**
* <p>
* Changes whether the user may scroll around the map.
* </p>
@@ -542,6 +568,25 @@ public class UiSettings {
}
/**
+ * Sets the focal point used as center for a gesture
+ *
+ * @param focalPoint the focal point to be used.
+ */
+ public void setFocalPoint(@Nullable PointF focalPoint) {
+ this.focalPoint = focalPoint;
+ mapView.setFocalPoint(focalPoint);
+ }
+
+ /**
+ * Returns the gesture focal point
+ *
+ * @return The focal point
+ */
+ public PointF getFocalPoint() {
+ return focalPoint;
+ }
+
+ /**
* Returns the measured height of the MapView
*
* @return height in pixels
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 aed4e87c07..a37a717482 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,10 +1,11 @@
package com.mapbox.mapboxsdk.maps.widgets;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PorterDuff;
@@ -15,6 +16,8 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
+import android.os.Bundle;
+import android.os.Parcelable;
import android.os.SystemClock;
import android.support.annotation.ColorInt;
import android.support.annotation.FloatRange;
@@ -22,8 +25,8 @@ import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup;
-import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
@@ -33,7 +36,6 @@ import com.mapbox.mapboxsdk.location.LocationListener;
import com.mapbox.mapboxsdk.location.LocationServices;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.Projection;
-import com.mapbox.mapboxsdk.maps.UiSettings;
import java.lang.ref.WeakReference;
@@ -45,7 +47,6 @@ public class MyLocationView extends View {
private MyLocationBehavior myLocationBehavior;
private MapboxMap mapboxMap;
private Projection projection;
- private int maxSize;
private int[] contentPadding = new int[4];
private Location location;
@@ -57,12 +58,20 @@ public class MyLocationView extends View {
private float gpsDirection;
private float previousDirection;
- private float accuracy = 0;
- private Paint accuracyPaint = new Paint();
+ private float accuracy;
+ private Paint accuracyPaint;
private ValueAnimator locationChangeAnimator;
private ValueAnimator accuracyAnimator;
- private ObjectAnimator directionAnimator;
+ private ValueAnimator directionAnimator;
+
+ private ValueAnimator.AnimatorUpdateListener invalidateSelfOnUpdateListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ invalidate();
+ }
+ };
private Drawable foregroundDrawable;
private Drawable foregroundBearingDrawable;
@@ -79,6 +88,14 @@ public class MyLocationView extends View {
private int backgroundOffsetRight;
private int backgroundOffsetBottom;
+ private Matrix matrix;
+ private Camera camera;
+ private PointF screenLocation;
+
+ // camera vars
+ private float bearing;
+ private float tilt;
+
@MyLocationTracking.Mode
private int myLocationTrackingMode;
@@ -104,16 +121,44 @@ public class MyLocationView extends View {
}
private void init(Context context) {
+ if(isInEditMode()){
+ return;
+ }
+
setEnabled(false);
+
+ // setup LayoutParams
+ ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ setLayoutParams(lp);
+
+ matrix = new Matrix();
+ camera = new Camera();
+ camera.setLocation(0, 0, -1000);
+ accuracyPaint = new Paint();
+
myLocationBehavior = new MyLocationBehaviorFactory().getBehavioralModel(MyLocationTracking.TRACKING_NONE);
compassListener = new CompassListener(context);
- maxSize = (int) context.getResources().getDimension(R.dimen.my_locationview_size);
}
public final void setForegroundDrawables(Drawable defaultDrawable, Drawable bearingDrawable) {
- if (defaultDrawable == null || bearingDrawable == null) {
+ if (defaultDrawable == null) {
return;
}
+
+ if (bearingDrawable == null) {
+ // if user only provided one resource
+ // use same for bearing mode
+ bearingDrawable = defaultDrawable.getConstantState().newDrawable();
+ }
+
+ if (backgroundDrawable == null) {
+ // if the user didn't provide a background resource we will use the foreground resource instead,
+ // we need to create a new drawable to handle tinting correctly
+ backgroundDrawable = defaultDrawable.getConstantState().newDrawable();
+ }
+
if (defaultDrawable.getIntrinsicWidth() != bearingDrawable.getIntrinsicWidth() || defaultDrawable.getIntrinsicHeight() != bearingDrawable.getIntrinsicHeight()) {
throw new RuntimeException("The dimensions from location and bearing drawables should be match");
}
@@ -151,7 +196,6 @@ public class MyLocationView extends View {
backgroundOffsetTop = top;
backgroundOffsetRight = right;
backgroundOffsetBottom = bottom;
-
setShadowDrawableTint(backgroundTintColor);
invalidateBounds();
@@ -187,19 +231,18 @@ public class MyLocationView extends View {
int backgroundWidth = backgroundDrawable.getIntrinsicWidth();
int backgroundHeight = backgroundDrawable.getIntrinsicHeight();
-
- int foregroundWidth = foregroundDrawable.getIntrinsicWidth();
- int foregroundHeight = foregroundDrawable.getIntrinsicHeight();
-
int horizontalOffset = backgroundOffsetLeft - backgroundOffsetRight;
int verticalOffset = backgroundOffsetTop - backgroundOffsetBottom;
+ backgroundBounds = new Rect(-backgroundWidth / 2 + horizontalOffset, -backgroundHeight / 2 + verticalOffset, backgroundWidth / 2 + horizontalOffset, backgroundHeight / 2 + verticalOffset);
+ backgroundDrawable.setBounds(backgroundBounds);
- int accuracyWidth = 2 * maxSize;
-
- backgroundBounds = new Rect(accuracyWidth - (backgroundWidth / 2) + horizontalOffset, accuracyWidth + verticalOffset - (backgroundWidth / 2), accuracyWidth + (backgroundWidth / 2) + horizontalOffset, accuracyWidth + (backgroundHeight / 2) + verticalOffset);
- foregroundBounds = new Rect(accuracyWidth - (foregroundWidth / 2), accuracyWidth - (foregroundHeight / 2), accuracyWidth + (foregroundWidth / 2), accuracyWidth + (foregroundHeight / 2));
+ int foregroundWidth = foregroundDrawable.getIntrinsicWidth();
+ int foregroundHeight = foregroundDrawable.getIntrinsicHeight();
+ foregroundBounds = new Rect(-foregroundWidth / 2, -foregroundHeight / 2, foregroundWidth / 2, foregroundHeight / 2);
+ foregroundDrawable.setBounds(foregroundBounds);
+ foregroundBearingDrawable.setBounds(foregroundBounds);
- // invoke a new measure
+ // invoke a new draw
invalidate();
}
@@ -207,23 +250,51 @@ public class MyLocationView extends View {
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (location == null || foregroundBounds == null || backgroundBounds == null || accuracyAnimator == null) {
+ if (location == null || foregroundBounds == null || backgroundBounds == null || accuracyAnimator == null || screenLocation == null) {
// Not ready yet
return;
}
- // Draw circle
+ final PointF pointF = screenLocation;
+
float metersPerPixel = (float) projection.getMetersPerPixelAtLatitude(location.getLatitude());
- float accuracyPixels = (Float) accuracyAnimator.getAnimatedValue() / metersPerPixel;
+ float accuracyPixels = (Float) accuracyAnimator.getAnimatedValue() / metersPerPixel / 2;
float maxRadius = getWidth() / 2;
- canvas.drawCircle(foregroundBounds.centerX(), foregroundBounds.centerY(), accuracyPixels <= maxRadius ? accuracyPixels : maxRadius, accuracyPaint);
+ accuracyPixels = accuracyPixels <= maxRadius ? accuracyPixels : maxRadius;
+
+ // put matrix in origin
+ matrix.reset();
+
+ // apply tilt to camera
+ camera.save();
+ camera.rotate(tilt, 0, bearing);
+
+ // map camera matrix on our matrix
+ camera.getMatrix(matrix);
+
+ //
+ if (myBearingTrackingMode != MyBearingTracking.NONE && directionAnimator != null) {
+ matrix.preRotate((Float) directionAnimator.getAnimatedValue());
+ }
+
+ // put matrix at location of MyLocationView
+ matrix.postTranslate(pointF.x, pointF.y);
+
+ // concat our matrix on canvas
+ canvas.concat(matrix);
- // Draw shadow
+ // restore orientation from camera
+ camera.restore();
+
+ // draw circle
+ canvas.drawCircle(0, 0, accuracyPixels, accuracyPaint);
+
+ // draw shadow
if (backgroundDrawable != null) {
backgroundDrawable.draw(canvas);
}
- // Draw foreground
+ // draw foreground
if (myBearingTrackingMode == MyBearingTracking.NONE) {
if (foregroundDrawable != null) {
foregroundDrawable.draw(canvas);
@@ -233,31 +304,17 @@ public class MyLocationView extends View {
}
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- if (foregroundDrawable != null && foregroundBounds != null) {
- foregroundDrawable.setBounds(foregroundBounds);
- }
-
- if (foregroundBearingDrawable != null && foregroundBounds != null) {
- foregroundBearingDrawable.setBounds(foregroundBounds);
- }
-
- if (backgroundDrawable != null && backgroundBounds != null) {
- backgroundDrawable.setBounds(backgroundBounds);
- }
-
- setMeasuredDimension(4 * maxSize, 4 * maxSize);
+ public void setTilt(@FloatRange(from = 0, to = 60.0f) double tilt) {
+ this.tilt = (float) tilt;
}
- public void setTilt(@FloatRange(from = 0, to = 60.0f) double tilt) {
- setRotationX((float) tilt);
+ public void setBearing(double bearing) {
+ this.bearing = (float) bearing;
}
- void updateOnNextFrame() {
- mapboxMap.invalidate();
+ public void setCameraPosition(CameraPosition position) {
+ setTilt(position.tilt);
+ setBearing(position.bearing);
}
public void onPause() {
@@ -294,6 +351,24 @@ public class MyLocationView extends View {
toggleGps(enabled);
}
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable("superState", super.onSaveInstanceState());
+ bundle.putFloat("tilt", tilt);
+ return bundle;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state){
+ if (state instanceof Bundle){
+ Bundle bundle = (Bundle) state;
+ tilt = bundle.getFloat("tilt");
+ state = bundle.getParcelable("superState");
+ }
+ super.onRestoreInstanceState(state);
+ }
+
/**
* Enabled / Disable GPS location updates along with updating the UI
*
@@ -345,7 +420,7 @@ public class MyLocationView extends View {
compassListener.onPause();
if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
// always face north
- gpsDirection = 0;
+ gpsDirection = bearing;
setCompass(gpsDirection);
}
}
@@ -385,8 +460,9 @@ public class MyLocationView extends View {
}
previousDirection = newDir;
- directionAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, oldDir, newDir);
- directionAnimator.setDuration(1000);
+ directionAnimator = ValueAnimator.ofFloat(oldDir, newDir);
+ directionAnimator.setDuration(375);
+ directionAnimator.addUpdateListener(invalidateSelfOnUpdateListener);
directionAnimator.start();
}
@@ -469,29 +545,24 @@ public class MyLocationView extends View {
return;
}
- long currentTime = SystemClock.elapsedRealtime();
- if (currentTime < mCompassUpdateNextTimestamp) {
- return;
- }
-
int type = event.sensor.getType();
- float[] data;
if (type == Sensor.TYPE_ACCELEROMETER) {
- data = mGData;
+ System.arraycopy(event.values, 0, mGData, 0, 3);
} else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
- data = mMData;
+ System.arraycopy(event.values, 0, mMData, 0, 3);
} else {
// we should not be here.
return;
}
- for (int i = 0; i < 3; i++) {
- data[i] = event.values[i];
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime < mCompassUpdateNextTimestamp) {
+ return;
}
SensorManager.getRotationMatrix(mR, mI, mGData, mMData);
SensorManager.getOrientation(mR, mOrientation);
- setCompass((int) (mOrientation[0] * 180.0f / Math.PI));
+ setCompass(mCurrentDegree = (int) (mOrientation[0] * 180.0f / Math.PI));
mCompassUpdateNextTimestamp = currentTime + UPDATE_RATE_MS;
}
@@ -530,7 +601,7 @@ public class MyLocationView extends View {
double latitude = fromLat + (toLat - fromLat) * frac;
double longitude = fromLng + (toLng - fromLng) * frac;
behavior.updateLatLng(latitude, longitude);
- updateOnNextFrame();
+ update();
}
}
@@ -607,7 +678,7 @@ public class MyLocationView extends View {
if (location.hasBearing()) {
builder.bearing(location.getBearing());
}
- gpsDirection = 0;
+ gpsDirection = location.getBearing();
setCompass(gpsDirection);
} else if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
if (!compassListener.isPaused()) {
@@ -626,9 +697,9 @@ public class MyLocationView extends View {
@Override
void invalidate() {
int[] mapPadding = mapboxMap.getPadding();
- UiSettings uiSettings = mapboxMap.getUiSettings();
- setX((uiSettings.getWidth() - getWidth() + mapPadding[0] - mapPadding[2]) / 2 + (contentPadding[0] - contentPadding[2]) / 2);
- setY((uiSettings.getHeight() - getHeight() - mapPadding[3] + mapPadding[1]) / 2 + (contentPadding[1] - contentPadding[3]) / 2);
+ float x = (getWidth() + mapPadding[0] - mapPadding[2]) / 2 + (contentPadding[0] - contentPadding[2]) / 2;
+ float y = (getHeight() - mapPadding[3] + mapPadding[1]) / 2 + (contentPadding[1] - contentPadding[3]) / 2;
+ screenLocation = new PointF(x, y);
MyLocationView.this.invalidate();
}
}
@@ -648,8 +719,8 @@ public class MyLocationView extends View {
latLng = new LatLng(location);
// update LatLng direction
- if (location.hasBearing()) {
- gpsDirection = clamp(location.getBearing() - (float) mapboxMap.getCameraPosition().bearing);
+ if (myBearingTrackingMode == MyBearingTracking.GPS && location.hasBearing()) {
+ gpsDirection = location.getBearing();
setCompass(gpsDirection);
}
@@ -659,7 +730,7 @@ public class MyLocationView extends View {
// calculate updateLatLng time + add some extra offset to improve animation
long previousUpdateTimeStamp = locationUpdateTimestamp;
locationUpdateTimestamp = SystemClock.elapsedRealtime();
- long locationUpdateDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1);
+ long locationUpdateDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.3);
// calculate interpolated entity
interpolatedLocation = new LatLng((latLng.getLatitude() + previousLocation.getLatitude()) / 2, (latLng.getLongitude() + previousLocation.getLongitude()) / 2);
@@ -681,24 +752,9 @@ public class MyLocationView extends View {
latLng = interpolatedLocation;
}
- private float clamp(float direction) {
- float diff = previousDirection - direction;
- if (diff > 180.0f) {
- direction += 360.0f;
- } else if (diff < -180.0f) {
- direction -= 360.f;
- }
- previousDirection = direction;
- return direction;
- }
-
@Override
void invalidate() {
- PointF screenLocation = projection.toScreenLocation(latLng);
- if (screenLocation != null) {
- setX((screenLocation.x - getWidth() / 2));
- setY((screenLocation.y - getHeight() / 2));
- }
+ screenLocation = projection.toScreenLocation(latLng);
MyLocationView.this.invalidate();
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
index 9ae96ebf7b..80bd1b3bef 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
@@ -6,6 +6,9 @@ import android.support.annotation.IntRange;
import com.mapbox.mapboxsdk.maps.MapView;
+/**
+ * Settings to configure the visual appearance of the MyLocationView.
+ */
public class MyLocationViewSettings {
private MapView mapView;
@@ -52,43 +55,101 @@ public class MyLocationViewSettings {
private int[] padding = new int[4];
+ /**
+ * Creates an instance of MyLocationViewSettings
+ *
+ * @param mapView the MapView that hosts the MyLocationView
+ * @param myLocationView the MyLocationView to apply the settings to
+ * @see MyLocationView
+ */
public MyLocationViewSettings(MapView mapView, MyLocationView myLocationView) {
this.mapView = mapView;
this.myLocationView = myLocationView;
}
+ /**
+ * Returns if the MyLocationView is enabled
+ *
+ * @return true if MyLocationView is enabled,
+ */
public boolean isEnabled() {
return enabled;
}
+ /**
+ * Set the enabled state of MyLocationView
+ *
+ * @param enabled true shows the MyLocationView on the map
+ */
public void setEnabled(boolean enabled) {
this.enabled = enabled;
myLocationView.setEnabled(enabled);
}
+ /**
+ * Set the foreground drawable of the MyLocationView
+ * <p>
+ * The foreground drawable is the image visible on screen
+ * </p>
+ *
+ * @param foregroundDrawable the drawable to show as foreground without bearing
+ * @param foregroundBearingDrawable the drawable to show as foreground when bearing is enabled
+ */
public void setForegroundDrawable(Drawable foregroundDrawable, Drawable foregroundBearingDrawable) {
this.foregroundDrawable = foregroundDrawable;
this.foregroundBearingDrawable = foregroundBearingDrawable;
myLocationView.setForegroundDrawables(foregroundDrawable, foregroundBearingDrawable);
}
+ /**
+ * Get the foreground drawable when bearing is disabled.
+ *
+ * @return the drawable used as foreground
+ */
public Drawable getForegroundDrawable() {
return foregroundDrawable;
}
+ /**
+ * Get the foreground drawable when bearing is enabled.
+ *
+ * @return the bearing drawable used as foreground
+ */
public Drawable getForegroundBearingDrawable() {
return foregroundBearingDrawable;
}
+ /**
+ * Set the foreground tint color.
+ * <p>
+ * The color will tint both the foreground and the bearing foreground drawable.
+ * </p>
+ *
+ * @param foregroundTintColor the color to tint the foreground drawable
+ */
public void setForegroundTintColor(@ColorInt int foregroundTintColor) {
this.foregroundTintColor = foregroundTintColor;
myLocationView.setForegroundDrawableTint(foregroundTintColor);
}
+ /**
+ * Get the foreground tint color.
+ *
+ * @return the foreground tint color
+ */
public int getForegroundTintColor() {
return foregroundTintColor;
}
+ /**
+ * Set the background drawable of MyLocationView
+ * <p>
+ * Padding can be added to provide an offset to the background
+ * </p>
+ *
+ * @param backgroundDrawable the drawable to show as background
+ * @param padding the padding added to the background
+ */
public void setBackgroundDrawable(Drawable backgroundDrawable, int[] padding) {
this.backgroundDrawable = backgroundDrawable;
this.backgroundOffset = padding;
@@ -99,46 +160,99 @@ public class MyLocationViewSettings {
}
}
+ /**
+ * Get the background drawable of MyLocationView.
+ *
+ * @return the drawable used as background
+ */
public Drawable getBackgroundDrawable() {
return backgroundDrawable;
}
+ /**
+ * Set the background tint color.
+ *
+ * @param backgroundTintColor the color to tint the background
+ */
public void setBackgroundTintColor(@ColorInt int backgroundTintColor) {
this.backgroundTintColor = backgroundTintColor;
myLocationView.setShadowDrawableTint(backgroundTintColor);
}
+ /**
+ * Get the background tint color.
+ *
+ * @return the background tint color
+ */
public int getBackgroundTintColor() {
return backgroundTintColor;
}
+ /**
+ * Get the background offset.
+ *
+ * @return the background offset
+ */
public int[] getBackgroundOffset() {
return backgroundOffset;
}
+ /**
+ * Set the MyLocationView padding.
+ *
+ * @param left the padding left of MyLocationView
+ * @param top the padding top of MyLocationView
+ * @param right the padding right of MyLocationView
+ * @param bottom the padding bottom of MyLocaionView
+ */
public void setPadding(int left, int top, int right, int bottom) {
padding = new int[]{left, top, right, bottom};
myLocationView.setContentPadding(padding);
mapView.invalidateContentPadding();
}
+ /**
+ * Get the MyLocationView padding.
+ *
+ * @return an array describing the padding in a LTRB manner
+ */
public int[] getPadding() {
return padding;
}
+ /**
+ * Get the alpha value of the accuracy circle of MyLocationView
+ *
+ * @return the alpha value
+ */
public int getAccuracyAlpha() {
return accuracyAlpha;
}
- public void setAccuracyAlpha(@IntRange(from = 0, to = 255) int arruracyAlpha) {
- this.accuracyAlpha = arruracyAlpha;
- myLocationView.setAccuracyAlpha(arruracyAlpha);
+ /**
+ * Set the alpha value of the accuracy circle of MyLocationView
+ *
+ * @param accuracyAlpha the alpha value to set
+ */
+ public void setAccuracyAlpha(@IntRange(from = 0, to = 255) int accuracyAlpha) {
+ this.accuracyAlpha = accuracyAlpha;
+ myLocationView.setAccuracyAlpha(accuracyAlpha);
}
+ /**
+ * Get the accuracy tint color of MyLocationView.
+ *
+ * @return the tint color used for accuracy
+ */
public int getAccuracyTintColor() {
return accuracyTintColor;
}
+ /**
+ * Set the accuracy tint color of MyLocationView.
+ *
+ * @param accuracyTintColor the accuracy tint color
+ */
public void setAccuracyTintColor(@ColorInt int accuracyTintColor) {
this.accuracyTintColor = accuracyTintColor;
myLocationView.setAccuracyTint(accuracyTintColor);
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 e81c366dba..ba6434e06d 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,11 +1,17 @@
package com.mapbox.mapboxsdk.offline;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.util.Log;
+
import com.mapbox.mapboxsdk.MapboxAccountManager;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+
import java.io.File;
/**
@@ -88,7 +94,7 @@ public class OfflineManager {
private OfflineManager(Context context) {
// Get a pointer to the DefaultFileSource instance
- String assetRoot = context.getFilesDir().getAbsolutePath();
+ String assetRoot = getDatabasePath(context);
String cachePath = assetRoot + File.separator + DATABASE_NAME;
mDefaultFileSourcePtr = createDefaultFileSource(cachePath, assetRoot, DEFAULT_MAX_CACHE_SIZE);
@@ -100,6 +106,61 @@ public class OfflineManager {
deleteAmbientDatabase(context);
}
+ public static String getDatabasePath(Context context) {
+ // Default value
+ boolean setStorageExternal = MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL;
+
+ try {
+ // Try getting a custom value from the app Manifest
+ ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
+ context.getPackageName(), PackageManager.GET_META_DATA);
+ setStorageExternal = appInfo.metaData.getBoolean(
+ MapboxConstants.KEY_META_DATA_SET_STORAGE_EXTERNAL,
+ MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Failed to read the package metadata: " + e.getMessage());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to read the storage key: " + e.getMessage());
+ }
+
+ String databasePath = null;
+ if (setStorageExternal && isExternalStorageReadable()) {
+ try {
+ // Try getting the external storage path
+ databasePath = context.getExternalFilesDir(null).getAbsolutePath();
+ } catch (NullPointerException e) {
+ Log.e(LOG_TAG, "Failed to obtain the external storage path: " + e.getMessage());
+ }
+ }
+
+ if (databasePath == null) {
+ // Default to internal storage
+ databasePath = context.getFilesDir().getAbsolutePath();
+ }
+
+ return databasePath;
+ }
+
+ /**
+ * Checks if external storage is available to at least read. In order for this to work, make
+ * sure you include <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ * (or WRITE_EXTERNAL_STORAGE) for API level < 18 in your app Manifest.
+ *
+ * Code from https://developer.android.com/guide/topics/data/data-storage.html#filesExternal
+ */
+ public static boolean isExternalStorageReadable() {
+ String state = Environment.getExternalStorageState();
+ if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
+ return true;
+ }
+
+ Log.w(LOG_TAG, "External storage was requested but it isn't readable. For API level < 18"
+ + " make sure you've requested READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE"
+ + " permissions in your app Manifest (defaulting to internal storage).");
+
+ return false;
+ }
+
private void deleteAmbientDatabase(final Context context) {
// Delete the file in a separate thread to avoid affecting the UI
new Thread(new Runnable() {
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 7f066e74d1..3756c93df2 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
@@ -140,6 +140,34 @@ public class OfflineRegion {
public static final int STATE_INACTIVE = 0;
public static final int STATE_ACTIVE = 1;
+ // Keep track of the region state
+ private int state = STATE_INACTIVE;
+
+ private boolean deliverInactiveMessages = false;
+
+ /**
+ * Gets whether or not the `OfflineRegionObserver` will continue to deliver messages even if
+ * the region state has been set as STATE_INACTIVE.
+ */
+ public boolean isDeliveringInactiveMessages() {
+ return deliverInactiveMessages;
+ }
+
+ /**
+ * When set true, the `OfflineRegionObserver` will continue to deliver messages even if
+ * the region state has been set as STATE_INACTIVE (operations happen asynchronously). If set
+ * false, the client won't be notified of further messages.
+ */
+ public void setDeliverInactiveMessages(boolean deliverInactiveMessages) {
+ this.deliverInactiveMessages = deliverInactiveMessages;
+ }
+
+ private boolean deliverMessages() {
+ if (state == STATE_ACTIVE) return true;
+ if (isDeliveringInactiveMessages()) return true;
+ return false;
+ }
+
/*
* Constructor
*/
@@ -180,32 +208,38 @@ public class OfflineRegion {
setOfflineRegionObserver(new OfflineRegionObserver() {
@Override
public void onStatusChanged(final OfflineRegionStatus status) {
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- observer.onStatusChanged(status);
- }
- });
+ if (deliverMessages()) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ observer.onStatusChanged(status);
+ }
+ });
+ }
}
@Override
public void onError(final OfflineRegionError error) {
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- observer.onError(error);
- }
- });
+ if (deliverMessages()) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ observer.onError(error);
+ }
+ });
+ }
}
@Override
public void mapboxTileCountLimitExceeded(final long limit) {
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- observer.mapboxTileCountLimitExceeded(limit);
- }
- });
+ if (deliverMessages()) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ observer.mapboxTileCountLimitExceeded(limit);
+ }
+ });
+ }
}
});
}
@@ -214,6 +248,7 @@ public class OfflineRegion {
* Pause or resume downloading of regional resources.
*/
public void setDownloadState(@DownloadState int state) {
+ this.state = state;
setOfflineRegionDownloadState(state);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
new file mode 100644
index 0000000000..fae68c518e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
@@ -0,0 +1,73 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import com.mapbox.mapboxsdk.exceptions.ConversionException;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.*;
+
+/**
+ * Background Layer
+ */
+public class BackgroundLayer extends Layer {
+
+ public BackgroundLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public BackgroundLayer(String layerId) {
+ initialize(layerId);
+ }
+
+ protected native void initialize(String layerId);
+
+
+ public BackgroundLayer withProperties(@NonNull Property<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getBackgroundColor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetBackgroundColor());
+ }
+ /**
+ * The color with which the background will be drawn.
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int getBackgroundColorAsInt() {
+ checkValidity();
+ PropertyValue<String> value = getBackgroundColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("background-color was set as a Function");
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getBackgroundPattern() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetBackgroundPattern());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getBackgroundOpacity() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetBackgroundOpacity());
+ }
+
+ private native Object nativeGetBackgroundColor();
+
+ private native Object nativeGetBackgroundPattern();
+
+ private native Object nativeGetBackgroundOpacity();
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
new file mode 100644
index 0000000000..ce0e9fd5bd
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
@@ -0,0 +1,135 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import com.mapbox.mapboxsdk.exceptions.ConversionException;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.*;
+
+/**
+ * Circle Layer
+ */
+public class CircleLayer extends Layer {
+
+ public CircleLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public CircleLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ checkValidity();
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public CircleLayer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+
+ public void setFilter(Filter.Statement filter) {
+ checkValidity();
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ checkValidity();
+ nativeSetFilter(filter);
+ }
+
+ public CircleLayer withFilter(Object[] filter) {
+ setFilter(filter);
+ return this;
+ }
+
+ public CircleLayer withFilter(Filter.Statement filter) {
+ setFilter(filter);
+ return this;
+ }
+
+
+ public CircleLayer withProperties(@NonNull Property<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getCircleRadius() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetCircleRadius());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getCircleColor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetCircleColor());
+ }
+ /**
+ * The color of the circle.
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int getCircleColorAsInt() {
+ checkValidity();
+ PropertyValue<String> value = getCircleColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("circle-color was set as a Function");
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getCircleBlur() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetCircleBlur());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getCircleOpacity() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetCircleOpacity());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getCircleTranslate() {
+ checkValidity();
+ return (PropertyValue<Float[]>) new PropertyValue(nativeGetCircleTranslate());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getCircleTranslateAnchor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetCircleTranslateAnchor());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getCirclePitchScale() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetCirclePitchScale());
+ }
+
+ private native Object nativeGetCircleRadius();
+
+ private native Object nativeGetCircleColor();
+
+ private native Object nativeGetCircleBlur();
+
+ private native Object nativeGetCircleOpacity();
+
+ private native Object nativeGetCircleTranslate();
+
+ private native Object nativeGetCircleTranslateAnchor();
+
+ private native Object nativeGetCirclePitchScale();
+
+}
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
new file mode 100644
index 0000000000..f25d46dba9
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java
@@ -0,0 +1,30 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * Custom layer.
+ * <p/>
+ * Experimental feature. Do not use.
+ */
+public class CustomLayer extends Layer {
+
+ public CustomLayer(String id,
+ long context,
+ long initializeFunction,
+ long renderFunction,
+ long deinitializeFunction) {
+ initialize(id, initializeFunction, renderFunction, deinitializeFunction, context);
+ }
+
+ public CustomLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public void invalidate() {
+ nativeUpdate();
+ }
+
+ protected native void initialize(String id, long initializeFunction, long renderFunction, long deinitializeFunction, long context);
+
+ protected native void nativeUpdate();
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
new file mode 100644
index 0000000000..d188129e2a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
@@ -0,0 +1,150 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import com.mapbox.mapboxsdk.exceptions.ConversionException;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.*;
+
+/**
+ * Fill Layer
+ */
+public class FillLayer extends Layer {
+
+ public FillLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public FillLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ checkValidity();
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public FillLayer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+
+ public void setFilter(Filter.Statement filter) {
+ checkValidity();
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ checkValidity();
+ nativeSetFilter(filter);
+ }
+
+ public FillLayer withFilter(Object[] filter) {
+ setFilter(filter);
+ return this;
+ }
+
+ public FillLayer withFilter(Filter.Statement filter) {
+ setFilter(filter);
+ return this;
+ }
+
+
+ public FillLayer withProperties(@NonNull Property<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getFillAntialias() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetFillAntialias());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getFillOpacity() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetFillOpacity());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getFillColor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetFillColor());
+ }
+ /**
+ * The color of the filled part of this layer. This color can be specified as rgba with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used.
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int getFillColorAsInt() {
+ checkValidity();
+ PropertyValue<String> value = getFillColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("fill-color was set as a Function");
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getFillOutlineColor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetFillOutlineColor());
+ }
+ /**
+ * The outline color of the fill. Matches the value of `fill-color` if unspecified.
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int getFillOutlineColorAsInt() {
+ checkValidity();
+ PropertyValue<String> value = getFillOutlineColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("fill-outline-color was set as a Function");
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getFillTranslate() {
+ checkValidity();
+ return (PropertyValue<Float[]>) new PropertyValue(nativeGetFillTranslate());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getFillTranslateAnchor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetFillTranslateAnchor());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getFillPattern() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetFillPattern());
+ }
+
+ private native Object nativeGetFillAntialias();
+
+ private native Object nativeGetFillOpacity();
+
+ private native Object nativeGetFillColor();
+
+ private native Object nativeGetFillOutlineColor();
+
+ private native Object nativeGetFillTranslate();
+
+ private native Object nativeGetFillTranslateAnchor();
+
+ private native Object nativeGetFillPattern();
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java
new file mode 100644
index 0000000000..04da4da0cb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java
@@ -0,0 +1,115 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Utility to build filter expressions more easily:
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-filter">Style spec</a>
+ */
+public class Filter {
+
+ public abstract static class Statement {
+ protected final String operator;
+
+ public Statement(String operator) {
+ this.operator = operator;
+ }
+
+ public abstract Object[] toArray();
+ }
+
+ public static class SimpleStatement extends Statement {
+ private final String key;
+ private final Object[] values;
+
+ public SimpleStatement(String operator, String key, Object... values) {
+ super(operator);
+ this.key = key;
+ this.values = values;
+ }
+
+
+ @Override
+ public Object[] toArray() {
+ ArrayList<Object> array = new ArrayList<>(2 + values.length);
+ array.add(operator);
+ array.add(key);
+ array.addAll(Arrays.asList(values));
+ return array.toArray();
+ }
+ }
+
+ public static class CompoundStatement extends Statement {
+ private final Statement[] statements;
+
+ public CompoundStatement(String operator, Statement... statements) {
+ super(operator);
+ this.statements = statements;
+ }
+
+ @Override
+ public Object[] toArray() {
+ ArrayList<Object> array = new ArrayList<>(1 + statements.length);
+ array.add(operator);
+ for (Statement statement : statements) {
+ array.add(statement.toArray());
+ }
+ return array.toArray();
+ }
+ }
+
+ public static Statement all(Statement... statements) {
+ return new CompoundStatement("all", statements);
+ }
+
+ public static Statement any(Statement... statements) {
+ return new CompoundStatement("any", statements);
+ }
+
+ public static Statement none(Statement... statements) {
+ return new CompoundStatement("none", statements);
+ }
+
+ public static Statement has(String key) {
+ return new SimpleStatement("has", key);
+ }
+
+ public static Statement notHas(String key) {
+ return new SimpleStatement("!has", key);
+ }
+
+ public static Statement eq(String key, Object value) {
+ return new SimpleStatement("==", key, value);
+ }
+
+ public static Statement neq(String key, Object value) {
+ return new SimpleStatement("!=", key, value);
+ }
+
+ public static Statement gt(String key, Object value) {
+ return new SimpleStatement(">", key, value);
+ }
+
+ public static Statement gte(String key, Object value) {
+ return new SimpleStatement(">=", key, value);
+ }
+
+ public static Statement lt(String key, Object value) {
+ return new SimpleStatement("<", key, value);
+ }
+
+ public static Statement lte(String key, Object value) {
+ return new SimpleStatement("<=", key, value);
+ }
+
+ public static Statement in(String key, Object... values) {
+ return new SimpleStatement("in", key, values);
+ }
+
+ public static Statement notIn(String key, Object... values) {
+ return new SimpleStatement("!in", key, values);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Function.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Function.java
new file mode 100644
index 0000000000..c776f9ff23
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Function.java
@@ -0,0 +1,85 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.FloatRange;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.Size;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Representation of <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-function">Function</a> in the Mapbox style specification
+ *
+ * @param <T> the target property's value type. Make sure it matches.
+ */
+public class Function<T> {
+
+ public static class Stop<I, O> {
+ public final I in;
+ public final O out;
+
+ Stop(I in, O out) {
+ this.in = in;
+ this.out = out;
+ }
+
+ Object[] toValueObject() {
+ return new Object[]{in, out};
+ }
+ }
+
+ @SafeVarargs
+ public static <T> Function<T> zoom(@NonNull @Size(min = 1) Stop<Float, T>... stops) {
+ return new Function<T>(stops);
+ }
+
+ @SafeVarargs
+ public static <T> Function<T> zoom(
+ @FloatRange(from = 0, to = 1, fromInclusive = false, toInclusive = false) float base,
+ @NonNull @Size(min = 1) Stop<Float, T>... stops) {
+ return new Function<T>(stops)
+ .withBase(base);
+ }
+
+ public static <T> Stop<Float, T> stop(float in, Property<T> output) {
+ return new Stop<>(in, output.value);
+ }
+
+ private final Stop<Float, T>[] stops;
+ private Float base;
+
+ Function(@NonNull @Size(min = 1) Stop<Float, T>[] stops) {
+ this.stops = stops;
+ }
+
+ Function<T> withBase(float base) {
+ this.base = base;
+ return this;
+ }
+
+ @Nullable
+ public Float getBase() {
+ return base;
+ }
+
+ public Stop<Float, T>[] getStops() {
+ return stops;
+ }
+
+ Map<String, Object> toValueObject() {
+ Object[] stopsValue = new Object[stops.length];
+
+ for (int i = 0; i < stopsValue.length; i++) {
+ Stop stop = stops[i];
+ stopsValue[i] = stop.toValueObject();
+ }
+
+ Map<String, Object> value = new HashMap<>();
+ if (base != null) {
+ value.put("base", base);
+ }
+ value.put("stops", stopsValue);
+ return value;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
new file mode 100644
index 0000000000..387cedbd6c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
@@ -0,0 +1,112 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.NonNull;
+
+/**
+ * Base class for the different Layer types
+ */
+public abstract class Layer {
+
+ private long nativePtr;
+ private boolean invalidated;
+
+ public Layer(long nativePtr) {
+ this.nativePtr = nativePtr;
+ }
+
+ public Layer() {
+ }
+
+ public void setProperties(@NonNull Property<?>... properties) {
+ checkValidity();
+
+ if (properties.length == 0) {
+ return;
+ }
+
+ boolean updateClasses = false;
+ for (Property<?> property : properties) {
+ if (property instanceof PaintProperty) {
+ updateClasses = true;
+ nativeSetPaintProperty(property.name, convertValue(property.value));
+ } else {
+ nativeSetLayoutProperty(property.name, convertValue(property.value));
+ }
+ }
+
+ nativeUpdateStyle(updateClasses);
+ }
+
+ public String getId() {
+ checkValidity();
+ return nativeGetId();
+ }
+
+ public PropertyValue<String> getVisibility() {
+ checkValidity();
+ return new PropertyValue<>(nativeGetVisibility());
+ }
+
+ public float getMinZoom() {
+ checkValidity();
+ return nativeGetMinZoom();
+ }
+
+ public float getMaxZoom() {
+ checkValidity();
+ return nativeGetMaxZoom();
+ }
+
+ public void setMinZoom(float zoom) {
+ checkValidity();
+ nativeSetMinZoom(zoom);
+ }
+
+ public void setMaxZoom(float zoom) {
+ checkValidity();
+ nativeSetMaxZoom(zoom);
+ }
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+ protected native String nativeGetId();
+
+ protected native Object nativeGetVisibility();
+
+ protected native void nativeSetLayoutProperty(String name, Object value);
+
+ protected native void nativeSetPaintProperty(String name, Object value);
+
+ protected native void nativeSetFilter(Object[] filter);
+
+ protected native void nativeSetSourceLayer(String sourceLayer);
+
+ protected native void nativeUpdateStyle(boolean updateClasses);
+
+ protected native float nativeGetMinZoom();
+
+ protected native float nativeGetMaxZoom();
+
+ protected native void nativeSetMinZoom(float zoom);
+
+ protected native void nativeSetMaxZoom(float zoom);
+
+ public long getNativePtr() {
+ return nativePtr;
+ }
+
+ private Object convertValue(Object value) {
+ return value != null && value instanceof Function ? ((Function) value).toValueObject() : value;
+ }
+
+ protected void checkValidity() {
+ if (invalidated) {
+ throw new RuntimeException("Layer has been invalidated. Request a new reference after adding");
+ }
+ }
+
+ public void invalidate() {
+ this.invalidated = true;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LayoutProperty.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LayoutProperty.java
new file mode 100644
index 0000000000..553554270e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LayoutProperty.java
@@ -0,0 +1,9 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+class LayoutProperty<T> extends Property<T> {
+
+ LayoutProperty(String name, T value) {
+ super(name, value);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
new file mode 100644
index 0000000000..ab5dd0815e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
@@ -0,0 +1,191 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import com.mapbox.mapboxsdk.exceptions.ConversionException;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.*;
+
+/**
+ * Line Layer
+ */
+public class LineLayer extends Layer {
+
+ public LineLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public LineLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ checkValidity();
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public LineLayer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+
+ public void setFilter(Filter.Statement filter) {
+ checkValidity();
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ checkValidity();
+ nativeSetFilter(filter);
+ }
+
+ public LineLayer withFilter(Object[] filter) {
+ setFilter(filter);
+ return this;
+ }
+
+ public LineLayer withFilter(Filter.Statement filter) {
+ setFilter(filter);
+ return this;
+ }
+
+
+ public LineLayer withProperties(@NonNull Property<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getLineCap() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetLineCap());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getLineJoin() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetLineJoin());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getLineMiterLimit() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetLineMiterLimit());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getLineRoundLimit() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetLineRoundLimit());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getLineOpacity() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetLineOpacity());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getLineColor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetLineColor());
+ }
+ /**
+ * The color with which the line will be drawn.
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int getLineColorAsInt() {
+ checkValidity();
+ PropertyValue<String> value = getLineColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("line-color was set as a Function");
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getLineTranslate() {
+ checkValidity();
+ return (PropertyValue<Float[]>) new PropertyValue(nativeGetLineTranslate());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getLineTranslateAnchor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetLineTranslateAnchor());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getLineWidth() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetLineWidth());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getLineGapWidth() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetLineGapWidth());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getLineOffset() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetLineOffset());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getLineBlur() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetLineBlur());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getLineDasharray() {
+ checkValidity();
+ return (PropertyValue<Float[]>) new PropertyValue(nativeGetLineDasharray());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getLinePattern() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetLinePattern());
+ }
+
+ private native Object nativeGetLineCap();
+
+ private native Object nativeGetLineJoin();
+
+ private native Object nativeGetLineMiterLimit();
+
+ private native Object nativeGetLineRoundLimit();
+
+ private native Object nativeGetLineOpacity();
+
+ private native Object nativeGetLineColor();
+
+ private native Object nativeGetLineTranslate();
+
+ private native Object nativeGetLineTranslateAnchor();
+
+ private native Object nativeGetLineWidth();
+
+ private native Object nativeGetLineGapWidth();
+
+ private native Object nativeGetLineOffset();
+
+ private native Object nativeGetLineBlur();
+
+ private native Object nativeGetLineDasharray();
+
+ private native Object nativeGetLinePattern();
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/NoSuchLayerException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/NoSuchLayerException.java
new file mode 100644
index 0000000000..d6ef4f8824
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/NoSuchLayerException.java
@@ -0,0 +1,11 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * No such layer.
+ */
+public class NoSuchLayerException extends Exception {
+
+ public NoSuchLayerException(String message) {
+ super(message);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PaintProperty.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PaintProperty.java
new file mode 100644
index 0000000000..6f1a9a0a16
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PaintProperty.java
@@ -0,0 +1,9 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+class PaintProperty<T> extends Property<T> {
+
+ PaintProperty(String name, T value) {
+ super(name, value);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
new file mode 100644
index 0000000000..a31f1adb54
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
@@ -0,0 +1,237 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Paint/Layout properties for Layer
+ */
+public abstract class Property<T> {
+
+ //visibility
+ public static final String VISIBLE = "visible";
+ public static final String NONE = "none";
+
+ @StringDef({
+ VISIBLE,
+ NONE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VISIBILITY {}
+
+ //line-cap
+ public static final String LINE_CAP_BUTT = "butt";
+ public static final String LINE_CAP_ROUND = "round";
+ public static final String LINE_CAP_SQUARE = "square";
+
+ @StringDef({
+ LINE_CAP_BUTT,
+ LINE_CAP_ROUND,
+ LINE_CAP_SQUARE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LINE_CAP {}
+
+ //line-join
+ public static final String LINE_JOIN_BEVEL = "bevel";
+ public static final String LINE_JOIN_ROUND = "round";
+ public static final String LINE_JOIN_MITER = "miter";
+
+ @StringDef({
+ LINE_JOIN_BEVEL,
+ LINE_JOIN_ROUND,
+ LINE_JOIN_MITER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LINE_JOIN {}
+
+ //symbol-placement
+ public static final String SYMBOL_PLACEMENT_POINT = "point";
+ public static final String SYMBOL_PLACEMENT_LINE = "line";
+
+ @StringDef({
+ SYMBOL_PLACEMENT_POINT,
+ SYMBOL_PLACEMENT_LINE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SYMBOL_PLACEMENT {}
+
+ //icon-rotation-alignment
+ public static final String ICON_ROTATION_ALIGNMENT_MAP = "map";
+ public static final String ICON_ROTATION_ALIGNMENT_VIEWPORT = "viewport";
+
+ @StringDef({
+ ICON_ROTATION_ALIGNMENT_MAP,
+ ICON_ROTATION_ALIGNMENT_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ICON_ROTATION_ALIGNMENT {}
+
+ //icon-text-fit
+ public static final String ICON_TEXT_FIT_NONE = "none";
+ public static final String ICON_TEXT_FIT_BOTH = "both";
+ public static final String ICON_TEXT_FIT_WIDTH = "width";
+ public static final String ICON_TEXT_FIT_HEIGHT = "height";
+
+ @StringDef({
+ ICON_TEXT_FIT_NONE,
+ ICON_TEXT_FIT_BOTH,
+ ICON_TEXT_FIT_WIDTH,
+ ICON_TEXT_FIT_HEIGHT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ICON_TEXT_FIT {}
+
+ //text-pitch-alignment
+ public static final String TEXT_PITCH_ALIGNMENT_MAP = "map";
+ public static final String TEXT_PITCH_ALIGNMENT_VIEWPORT = "viewport";
+
+ @StringDef({
+ TEXT_PITCH_ALIGNMENT_MAP,
+ TEXT_PITCH_ALIGNMENT_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_PITCH_ALIGNMENT {}
+
+ //text-rotation-alignment
+ public static final String TEXT_ROTATION_ALIGNMENT_MAP = "map";
+ public static final String TEXT_ROTATION_ALIGNMENT_VIEWPORT = "viewport";
+
+ @StringDef({
+ TEXT_ROTATION_ALIGNMENT_MAP,
+ TEXT_ROTATION_ALIGNMENT_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_ROTATION_ALIGNMENT {}
+
+ //text-justify
+ public static final String TEXT_JUSTIFY_LEFT = "left";
+ public static final String TEXT_JUSTIFY_CENTER = "center";
+ public static final String TEXT_JUSTIFY_RIGHT = "right";
+
+ @StringDef({
+ TEXT_JUSTIFY_LEFT,
+ TEXT_JUSTIFY_CENTER,
+ TEXT_JUSTIFY_RIGHT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_JUSTIFY {}
+
+ //text-anchor
+ public static final String TEXT_ANCHOR_CENTER = "center";
+ public static final String TEXT_ANCHOR_LEFT = "left";
+ public static final String TEXT_ANCHOR_RIGHT = "right";
+ public static final String TEXT_ANCHOR_TOP = "top";
+ public static final String TEXT_ANCHOR_BOTTOM = "bottom";
+ public static final String TEXT_ANCHOR_TOP_LEFT = "top-left";
+ public static final String TEXT_ANCHOR_TOP_RIGHT = "top-right";
+ public static final String TEXT_ANCHOR_BOTTOM_LEFT = "bottom-left";
+ public static final String TEXT_ANCHOR_BOTTOM_RIGHT = "bottom-right";
+
+ @StringDef({
+ TEXT_ANCHOR_CENTER,
+ TEXT_ANCHOR_LEFT,
+ TEXT_ANCHOR_RIGHT,
+ TEXT_ANCHOR_TOP,
+ TEXT_ANCHOR_BOTTOM,
+ TEXT_ANCHOR_TOP_LEFT,
+ TEXT_ANCHOR_TOP_RIGHT,
+ TEXT_ANCHOR_BOTTOM_LEFT,
+ TEXT_ANCHOR_BOTTOM_RIGHT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_ANCHOR {}
+
+ //text-transform
+ public static final String TEXT_TRANSFORM_NONE = "none";
+ public static final String TEXT_TRANSFORM_UPPERCASE = "uppercase";
+ public static final String TEXT_TRANSFORM_LOWERCASE = "lowercase";
+
+ @StringDef({
+ TEXT_TRANSFORM_NONE,
+ TEXT_TRANSFORM_UPPERCASE,
+ TEXT_TRANSFORM_LOWERCASE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_TRANSFORM {}
+
+ //fill-translate-anchor
+ public static final String FILL_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String FILL_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ FILL_TRANSLATE_ANCHOR_MAP,
+ FILL_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FILL_TRANSLATE_ANCHOR {}
+
+ //line-translate-anchor
+ public static final String LINE_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String LINE_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ LINE_TRANSLATE_ANCHOR_MAP,
+ LINE_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LINE_TRANSLATE_ANCHOR {}
+
+ //icon-translate-anchor
+ public static final String ICON_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String ICON_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ ICON_TRANSLATE_ANCHOR_MAP,
+ ICON_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ICON_TRANSLATE_ANCHOR {}
+
+ //text-translate-anchor
+ public static final String TEXT_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String TEXT_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ TEXT_TRANSLATE_ANCHOR_MAP,
+ TEXT_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_TRANSLATE_ANCHOR {}
+
+ //circle-translate-anchor
+ public static final String CIRCLE_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String CIRCLE_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ CIRCLE_TRANSLATE_ANCHOR_MAP,
+ CIRCLE_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CIRCLE_TRANSLATE_ANCHOR {}
+
+ //circle-pitch-scale
+ public static final String CIRCLE_PITCH_SCALE_MAP = "map";
+ public static final String CIRCLE_PITCH_SCALE_VIEWPORT = "viewport";
+
+ @StringDef({
+ CIRCLE_PITCH_SCALE_MAP,
+ CIRCLE_PITCH_SCALE_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CIRCLE_PITCH_SCALE {}
+
+
+ //Class definition
+ public final String name;
+ public final T value;
+
+ /* package */ Property(String name, T value) {
+ this.name = name;
+ this.value = value;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
new file mode 100644
index 0000000000..88587dbb5b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
@@ -0,0 +1,1300 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.ColorInt;
+
+/**
+ * Constructs paint/layout properties for Layers
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers>Layer style documentation</a>
+ */
+public class PropertyFactory {
+
+ /**
+ * Set visibility
+ */
+ public static Property<String> visibility(@Property.VISIBILITY String value) {
+ return new LayoutProperty<>("visibility", value);
+ }
+
+ /**
+ * Set visibility
+ */
+ public static Property<Function<String>> visibility(Function<String> function) {
+ return new LayoutProperty<>("visibility", function);
+ }
+
+ /**
+ * Whether or not the fill should be antialiased.
+ */
+ public static Property<Boolean> fillAntialias(Boolean value) {
+ return new PaintProperty<>("fill-antialias", value);
+ }
+
+ /**
+ * Whether or not the fill should be antialiased.
+ */
+ public static Property<Function<Boolean>> fillAntialias(Function<Boolean> function) {
+ return new PaintProperty<>("fill-antialias", function);
+ }
+
+ /**
+ * The opacity of the entire fill layer. In contrast to the fill-color, this value will also affect the 1px stroke around the fill, if the stroke is used.
+ */
+ public static Property<Float> fillOpacity(Float value) {
+ return new PaintProperty<>("fill-opacity", value);
+ }
+
+ /**
+ * The opacity of the entire fill layer. In contrast to the fill-color, this value will also affect the 1px stroke around the fill, if the stroke is used.
+ */
+ public static Property<Function<Float>> fillOpacity(Function<Float> function) {
+ return new PaintProperty<>("fill-opacity", function);
+ }
+
+ /**
+ * The color of the filled part of this layer. This color can be specified as rgba with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used.
+ */
+ public static Property<String> fillColor(@ColorInt int value) {
+ return new PaintProperty<>("fill-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The color of the filled part of this layer. This color can be specified as rgba with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used.
+ */
+ public static Property<String> fillColor(String value) {
+ return new PaintProperty<>("fill-color", value);
+ }
+
+ /**
+ * The color of the filled part of this layer. This color can be specified as rgba with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used.
+ */
+ public static Property<Function<String>> fillColor(Function<String> function) {
+ return new PaintProperty<>("fill-color", function);
+ }
+
+ /**
+ * The outline color of the fill. Matches the value of `fill-color` if unspecified.
+ */
+ public static Property<String> fillOutlineColor(@ColorInt int value) {
+ return new PaintProperty<>("fill-outline-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The outline color of the fill. Matches the value of `fill-color` if unspecified.
+ */
+ public static Property<String> fillOutlineColor(String value) {
+ return new PaintProperty<>("fill-outline-color", value);
+ }
+
+ /**
+ * The outline color of the fill. Matches the value of `fill-color` if unspecified.
+ */
+ public static Property<Function<String>> fillOutlineColor(Function<String> function) {
+ return new PaintProperty<>("fill-outline-color", function);
+ }
+
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ */
+ public static Property<Float[]> fillTranslate(Float[] value) {
+ return new PaintProperty<>("fill-translate", value);
+ }
+
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ */
+ public static Property<Function<Float[]>> fillTranslate(Function<Float[]> function) {
+ return new PaintProperty<>("fill-translate", function);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen)
+ */
+ public static Property<String> fillTranslateAnchor(@Property.FILL_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("fill-translate-anchor", value);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen)
+ */
+ public static Property<Function<String>> fillTranslateAnchor(Function<String> function) {
+ return new PaintProperty<>("fill-translate-anchor", function);
+ }
+
+ /**
+ * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ */
+ public static Property<String> fillPattern(String value) {
+ return new PaintProperty<>("fill-pattern", value);
+ }
+
+ /**
+ * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ */
+ public static Property<Function<String>> fillPattern(Function<String> function) {
+ return new PaintProperty<>("fill-pattern", function);
+ }
+
+ /**
+ * The opacity at which the line will be drawn.
+ */
+ public static Property<Float> lineOpacity(Float value) {
+ return new PaintProperty<>("line-opacity", value);
+ }
+
+ /**
+ * The opacity at which the line will be drawn.
+ */
+ public static Property<Function<Float>> lineOpacity(Function<Float> function) {
+ return new PaintProperty<>("line-opacity", function);
+ }
+
+ /**
+ * The color with which the line will be drawn.
+ */
+ public static Property<String> lineColor(@ColorInt int value) {
+ return new PaintProperty<>("line-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The color with which the line will be drawn.
+ */
+ public static Property<String> lineColor(String value) {
+ return new PaintProperty<>("line-color", value);
+ }
+
+ /**
+ * The color with which the line will be drawn.
+ */
+ public static Property<Function<String>> lineColor(Function<String> function) {
+ return new PaintProperty<>("line-color", function);
+ }
+
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ */
+ public static Property<Float[]> lineTranslate(Float[] value) {
+ return new PaintProperty<>("line-translate", value);
+ }
+
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ */
+ public static Property<Function<Float[]>> lineTranslate(Function<Float[]> function) {
+ return new PaintProperty<>("line-translate", function);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen)
+ */
+ public static Property<String> lineTranslateAnchor(@Property.LINE_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("line-translate-anchor", value);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen)
+ */
+ public static Property<Function<String>> lineTranslateAnchor(Function<String> function) {
+ return new PaintProperty<>("line-translate-anchor", function);
+ }
+
+ /**
+ * Stroke thickness.
+ */
+ public static Property<Float> lineWidth(Float value) {
+ return new PaintProperty<>("line-width", value);
+ }
+
+ /**
+ * Stroke thickness.
+ */
+ public static Property<Function<Float>> lineWidth(Function<Float> function) {
+ return new PaintProperty<>("line-width", function);
+ }
+
+ /**
+ * Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap.
+ */
+ public static Property<Float> lineGapWidth(Float value) {
+ return new PaintProperty<>("line-gap-width", value);
+ }
+
+ /**
+ * Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap.
+ */
+ public static Property<Function<Float>> lineGapWidth(Function<Float> function) {
+ return new PaintProperty<>("line-gap-width", function);
+ }
+
+ /**
+ * The line's offset perpendicular to its direction. Values may be positive or negative, where positive indicates "rightwards" (if you were moving in the direction of the line) and negative indicates "leftwards."
+ */
+ public static Property<Float> lineOffset(Float value) {
+ return new PaintProperty<>("line-offset", value);
+ }
+
+ /**
+ * The line's offset perpendicular to its direction. Values may be positive or negative, where positive indicates "rightwards" (if you were moving in the direction of the line) and negative indicates "leftwards."
+ */
+ public static Property<Function<Float>> lineOffset(Function<Float> function) {
+ return new PaintProperty<>("line-offset", function);
+ }
+
+ /**
+ * Blur applied to the line, in pixels.
+ */
+ public static Property<Float> lineBlur(Float value) {
+ return new PaintProperty<>("line-blur", value);
+ }
+
+ /**
+ * Blur applied to the line, in pixels.
+ */
+ public static Property<Function<Float>> lineBlur(Function<Float> function) {
+ return new PaintProperty<>("line-blur", function);
+ }
+
+ /**
+ * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to pixels, multiply the length by the current line width.
+ */
+ public static Property<Float[]> lineDasharray(Float[] value) {
+ return new PaintProperty<>("line-dasharray", value);
+ }
+
+ /**
+ * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to pixels, multiply the length by the current line width.
+ */
+ public static Property<Function<Float[]>> lineDasharray(Function<Float[]> function) {
+ return new PaintProperty<>("line-dasharray", function);
+ }
+
+ /**
+ * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512).
+ */
+ public static Property<String> linePattern(String value) {
+ return new PaintProperty<>("line-pattern", value);
+ }
+
+ /**
+ * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512).
+ */
+ public static Property<Function<String>> linePattern(Function<String> function) {
+ return new PaintProperty<>("line-pattern", function);
+ }
+
+ /**
+ * The opacity at which the icon will be drawn.
+ */
+ public static Property<Float> iconOpacity(Float value) {
+ return new PaintProperty<>("icon-opacity", value);
+ }
+
+ /**
+ * The opacity at which the icon will be drawn.
+ */
+ public static Property<Function<Float>> iconOpacity(Function<Float> function) {
+ return new PaintProperty<>("icon-opacity", function);
+ }
+
+ /**
+ * The color of the icon. This can only be used with sdf icons.
+ */
+ public static Property<String> iconColor(@ColorInt int value) {
+ return new PaintProperty<>("icon-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The color of the icon. This can only be used with sdf icons.
+ */
+ public static Property<String> iconColor(String value) {
+ return new PaintProperty<>("icon-color", value);
+ }
+
+ /**
+ * The color of the icon. This can only be used with sdf icons.
+ */
+ public static Property<Function<String>> iconColor(Function<String> function) {
+ return new PaintProperty<>("icon-color", function);
+ }
+
+ /**
+ * The color of the icon's halo. Icon halos can only be used with sdf icons.
+ */
+ public static Property<String> iconHaloColor(@ColorInt int value) {
+ return new PaintProperty<>("icon-halo-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The color of the icon's halo. Icon halos can only be used with sdf icons.
+ */
+ public static Property<String> iconHaloColor(String value) {
+ return new PaintProperty<>("icon-halo-color", value);
+ }
+
+ /**
+ * The color of the icon's halo. Icon halos can only be used with sdf icons.
+ */
+ public static Property<Function<String>> iconHaloColor(Function<String> function) {
+ return new PaintProperty<>("icon-halo-color", function);
+ }
+
+ /**
+ * Distance of halo to the icon outline.
+ */
+ public static Property<Float> iconHaloWidth(Float value) {
+ return new PaintProperty<>("icon-halo-width", value);
+ }
+
+ /**
+ * Distance of halo to the icon outline.
+ */
+ public static Property<Function<Float>> iconHaloWidth(Function<Float> function) {
+ return new PaintProperty<>("icon-halo-width", function);
+ }
+
+ /**
+ * Fade out the halo towards the outside.
+ */
+ public static Property<Float> iconHaloBlur(Float value) {
+ return new PaintProperty<>("icon-halo-blur", value);
+ }
+
+ /**
+ * Fade out the halo towards the outside.
+ */
+ public static Property<Function<Float>> iconHaloBlur(Function<Float> function) {
+ return new PaintProperty<>("icon-halo-blur", function);
+ }
+
+ /**
+ * Distance that the icon's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
+ */
+ public static Property<Float[]> iconTranslate(Float[] value) {
+ return new PaintProperty<>("icon-translate", value);
+ }
+
+ /**
+ * Distance that the icon's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
+ */
+ public static Property<Function<Float[]>> iconTranslate(Function<Float[]> function) {
+ return new PaintProperty<>("icon-translate", function);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen).
+ */
+ public static Property<String> iconTranslateAnchor(@Property.ICON_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("icon-translate-anchor", value);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen).
+ */
+ public static Property<Function<String>> iconTranslateAnchor(Function<String> function) {
+ return new PaintProperty<>("icon-translate-anchor", function);
+ }
+
+ /**
+ * The opacity at which the text will be drawn.
+ */
+ public static Property<Float> textOpacity(Float value) {
+ return new PaintProperty<>("text-opacity", value);
+ }
+
+ /**
+ * The opacity at which the text will be drawn.
+ */
+ public static Property<Function<Float>> textOpacity(Function<Float> function) {
+ return new PaintProperty<>("text-opacity", function);
+ }
+
+ /**
+ * The color with which the text will be drawn.
+ */
+ public static Property<String> textColor(@ColorInt int value) {
+ return new PaintProperty<>("text-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The color with which the text will be drawn.
+ */
+ public static Property<String> textColor(String value) {
+ return new PaintProperty<>("text-color", value);
+ }
+
+ /**
+ * The color with which the text will be drawn.
+ */
+ public static Property<Function<String>> textColor(Function<String> function) {
+ return new PaintProperty<>("text-color", function);
+ }
+
+ /**
+ * The color of the text's halo, which helps it stand out from backgrounds.
+ */
+ public static Property<String> textHaloColor(@ColorInt int value) {
+ return new PaintProperty<>("text-halo-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The color of the text's halo, which helps it stand out from backgrounds.
+ */
+ public static Property<String> textHaloColor(String value) {
+ return new PaintProperty<>("text-halo-color", value);
+ }
+
+ /**
+ * The color of the text's halo, which helps it stand out from backgrounds.
+ */
+ public static Property<Function<String>> textHaloColor(Function<String> function) {
+ return new PaintProperty<>("text-halo-color", function);
+ }
+
+ /**
+ * Distance of halo to the font outline. Max text halo width is 1/4 of the font-size.
+ */
+ public static Property<Float> textHaloWidth(Float value) {
+ return new PaintProperty<>("text-halo-width", value);
+ }
+
+ /**
+ * Distance of halo to the font outline. Max text halo width is 1/4 of the font-size.
+ */
+ public static Property<Function<Float>> textHaloWidth(Function<Float> function) {
+ return new PaintProperty<>("text-halo-width", function);
+ }
+
+ /**
+ * The halo's fadeout distance towards the outside.
+ */
+ public static Property<Float> textHaloBlur(Float value) {
+ return new PaintProperty<>("text-halo-blur", value);
+ }
+
+ /**
+ * The halo's fadeout distance towards the outside.
+ */
+ public static Property<Function<Float>> textHaloBlur(Function<Float> function) {
+ return new PaintProperty<>("text-halo-blur", function);
+ }
+
+ /**
+ * Distance that the text's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
+ */
+ public static Property<Float[]> textTranslate(Float[] value) {
+ return new PaintProperty<>("text-translate", value);
+ }
+
+ /**
+ * Distance that the text's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
+ */
+ public static Property<Function<Float[]>> textTranslate(Function<Float[]> function) {
+ return new PaintProperty<>("text-translate", function);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen).
+ */
+ public static Property<String> textTranslateAnchor(@Property.TEXT_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("text-translate-anchor", value);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen).
+ */
+ public static Property<Function<String>> textTranslateAnchor(Function<String> function) {
+ return new PaintProperty<>("text-translate-anchor", function);
+ }
+
+ /**
+ * Circle radius.
+ */
+ public static Property<Float> circleRadius(Float value) {
+ return new PaintProperty<>("circle-radius", value);
+ }
+
+ /**
+ * Circle radius.
+ */
+ public static Property<Function<Float>> circleRadius(Function<Float> function) {
+ return new PaintProperty<>("circle-radius", function);
+ }
+
+ /**
+ * The color of the circle.
+ */
+ public static Property<String> circleColor(@ColorInt int value) {
+ return new PaintProperty<>("circle-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The color of the circle.
+ */
+ public static Property<String> circleColor(String value) {
+ return new PaintProperty<>("circle-color", value);
+ }
+
+ /**
+ * The color of the circle.
+ */
+ public static Property<Function<String>> circleColor(Function<String> function) {
+ return new PaintProperty<>("circle-color", function);
+ }
+
+ /**
+ * Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity.
+ */
+ public static Property<Float> circleBlur(Float value) {
+ return new PaintProperty<>("circle-blur", value);
+ }
+
+ /**
+ * Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity.
+ */
+ public static Property<Function<Float>> circleBlur(Function<Float> function) {
+ return new PaintProperty<>("circle-blur", function);
+ }
+
+ /**
+ * The opacity at which the circle will be drawn.
+ */
+ public static Property<Float> circleOpacity(Float value) {
+ return new PaintProperty<>("circle-opacity", value);
+ }
+
+ /**
+ * The opacity at which the circle will be drawn.
+ */
+ public static Property<Function<Float>> circleOpacity(Function<Float> function) {
+ return new PaintProperty<>("circle-opacity", function);
+ }
+
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ */
+ public static Property<Float[]> circleTranslate(Float[] value) {
+ return new PaintProperty<>("circle-translate", value);
+ }
+
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ */
+ public static Property<Function<Float[]>> circleTranslate(Function<Float[]> function) {
+ return new PaintProperty<>("circle-translate", function);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen)
+ */
+ public static Property<String> circleTranslateAnchor(@Property.CIRCLE_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("circle-translate-anchor", value);
+ }
+
+ /**
+ * Control whether the translation is relative to the map (north) or viewport (screen)
+ */
+ public static Property<Function<String>> circleTranslateAnchor(Function<String> function) {
+ return new PaintProperty<>("circle-translate-anchor", function);
+ }
+
+ /**
+ * Controls the scaling behavior of the circle when the map is pitched. The value `map` scales circles according to their apparent distance to the camera. The value `viewport` results in no pitch-related scaling.
+ */
+ public static Property<String> circlePitchScale(@Property.CIRCLE_PITCH_SCALE String value) {
+ return new PaintProperty<>("circle-pitch-scale", value);
+ }
+
+ /**
+ * Controls the scaling behavior of the circle when the map is pitched. The value `map` scales circles according to their apparent distance to the camera. The value `viewport` results in no pitch-related scaling.
+ */
+ public static Property<Function<String>> circlePitchScale(Function<String> function) {
+ return new PaintProperty<>("circle-pitch-scale", function);
+ }
+
+ /**
+ * The opacity at which the image will be drawn.
+ */
+ public static Property<Float> rasterOpacity(Float value) {
+ return new PaintProperty<>("raster-opacity", value);
+ }
+
+ /**
+ * The opacity at which the image will be drawn.
+ */
+ public static Property<Function<Float>> rasterOpacity(Function<Float> function) {
+ return new PaintProperty<>("raster-opacity", function);
+ }
+
+ /**
+ * Rotates hues around the color wheel.
+ */
+ public static Property<Float> rasterHueRotate(Float value) {
+ return new PaintProperty<>("raster-hue-rotate", value);
+ }
+
+ /**
+ * Rotates hues around the color wheel.
+ */
+ public static Property<Function<Float>> rasterHueRotate(Function<Float> function) {
+ return new PaintProperty<>("raster-hue-rotate", function);
+ }
+
+ /**
+ * Increase or reduce the brightness of the image. The value is the minimum brightness.
+ */
+ public static Property<Float> rasterBrightnessMin(Float value) {
+ return new PaintProperty<>("raster-brightness-min", value);
+ }
+
+ /**
+ * Increase or reduce the brightness of the image. The value is the minimum brightness.
+ */
+ public static Property<Function<Float>> rasterBrightnessMin(Function<Float> function) {
+ return new PaintProperty<>("raster-brightness-min", function);
+ }
+
+ /**
+ * Increase or reduce the brightness of the image. The value is the maximum brightness.
+ */
+ public static Property<Float> rasterBrightnessMax(Float value) {
+ return new PaintProperty<>("raster-brightness-max", value);
+ }
+
+ /**
+ * Increase or reduce the brightness of the image. The value is the maximum brightness.
+ */
+ public static Property<Function<Float>> rasterBrightnessMax(Function<Float> function) {
+ return new PaintProperty<>("raster-brightness-max", function);
+ }
+
+ /**
+ * Increase or reduce the saturation of the image.
+ */
+ public static Property<Float> rasterSaturation(Float value) {
+ return new PaintProperty<>("raster-saturation", value);
+ }
+
+ /**
+ * Increase or reduce the saturation of the image.
+ */
+ public static Property<Function<Float>> rasterSaturation(Function<Float> function) {
+ return new PaintProperty<>("raster-saturation", function);
+ }
+
+ /**
+ * Increase or reduce the contrast of the image.
+ */
+ public static Property<Float> rasterContrast(Float value) {
+ return new PaintProperty<>("raster-contrast", value);
+ }
+
+ /**
+ * Increase or reduce the contrast of the image.
+ */
+ public static Property<Function<Float>> rasterContrast(Function<Float> function) {
+ return new PaintProperty<>("raster-contrast", function);
+ }
+
+ /**
+ * Fade duration when a new tile is added.
+ */
+ public static Property<Float> rasterFadeDuration(Float value) {
+ return new PaintProperty<>("raster-fade-duration", value);
+ }
+
+ /**
+ * Fade duration when a new tile is added.
+ */
+ public static Property<Function<Float>> rasterFadeDuration(Function<Float> function) {
+ return new PaintProperty<>("raster-fade-duration", function);
+ }
+
+ /**
+ * The color with which the background will be drawn.
+ */
+ public static Property<String> backgroundColor(@ColorInt int value) {
+ return new PaintProperty<>("background-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The color with which the background will be drawn.
+ */
+ public static Property<String> backgroundColor(String value) {
+ return new PaintProperty<>("background-color", value);
+ }
+
+ /**
+ * The color with which the background will be drawn.
+ */
+ public static Property<Function<String>> backgroundColor(Function<String> function) {
+ return new PaintProperty<>("background-color", function);
+ }
+
+ /**
+ * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ */
+ public static Property<String> backgroundPattern(String value) {
+ return new PaintProperty<>("background-pattern", value);
+ }
+
+ /**
+ * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ */
+ public static Property<Function<String>> backgroundPattern(Function<String> function) {
+ return new PaintProperty<>("background-pattern", function);
+ }
+
+ /**
+ * The opacity at which the background will be drawn.
+ */
+ public static Property<Float> backgroundOpacity(Float value) {
+ return new PaintProperty<>("background-opacity", value);
+ }
+
+ /**
+ * The opacity at which the background will be drawn.
+ */
+ public static Property<Function<Float>> backgroundOpacity(Function<Float> function) {
+ return new PaintProperty<>("background-opacity", function);
+ }
+
+ /**
+ * The display of line endings.
+ */
+ public static Property<String> lineCap(@Property.LINE_CAP String value) {
+ return new LayoutProperty<>("line-cap", value);
+ }
+
+ /**
+ * The display of line endings.
+ */
+ public static Property<Function<String>> lineCap(Function<String> function) {
+ return new LayoutProperty<>("line-cap", function);
+ }
+
+ /**
+ * The display of lines when joining.
+ */
+ public static Property<String> lineJoin(@Property.LINE_JOIN String value) {
+ return new LayoutProperty<>("line-join", value);
+ }
+
+ /**
+ * The display of lines when joining.
+ */
+ public static Property<Function<String>> lineJoin(Function<String> function) {
+ return new LayoutProperty<>("line-join", function);
+ }
+
+ /**
+ * Used to automatically convert miter joins to bevel joins for sharp angles.
+ */
+ public static Property<Float> lineMiterLimit(Float value) {
+ return new LayoutProperty<>("line-miter-limit", value);
+ }
+
+ /**
+ * Used to automatically convert miter joins to bevel joins for sharp angles.
+ */
+ public static Property<Function<Float>> lineMiterLimit(Function<Float> function) {
+ return new LayoutProperty<>("line-miter-limit", function);
+ }
+
+ /**
+ * Used to automatically convert round joins to miter joins for shallow angles.
+ */
+ public static Property<Float> lineRoundLimit(Float value) {
+ return new LayoutProperty<>("line-round-limit", value);
+ }
+
+ /**
+ * Used to automatically convert round joins to miter joins for shallow angles.
+ */
+ public static Property<Function<Float>> lineRoundLimit(Function<Float> function) {
+ return new LayoutProperty<>("line-round-limit", function);
+ }
+
+ /**
+ * Label placement relative to its geometry. `line` can only be used on LineStrings and Polygons.
+ */
+ public static Property<String> symbolPlacement(@Property.SYMBOL_PLACEMENT String value) {
+ return new LayoutProperty<>("symbol-placement", value);
+ }
+
+ /**
+ * Label placement relative to its geometry. `line` can only be used on LineStrings and Polygons.
+ */
+ public static Property<Function<String>> symbolPlacement(Function<String> function) {
+ return new LayoutProperty<>("symbol-placement", function);
+ }
+
+ /**
+ * Distance between two symbol anchors.
+ */
+ public static Property<Float> symbolSpacing(Float value) {
+ return new LayoutProperty<>("symbol-spacing", value);
+ }
+
+ /**
+ * Distance between two symbol anchors.
+ */
+ public static Property<Function<Float>> symbolSpacing(Function<Float> function) {
+ return new LayoutProperty<>("symbol-spacing", function);
+ }
+
+ /**
+ * If true, the symbols will not cross tile edges to avoid mutual collisions. Recommended in layers that don't have enough padding in the vector tile to prevent collisions, or if it is a point symbol layer placed after a line symbol layer.
+ */
+ public static Property<Boolean> symbolAvoidEdges(Boolean value) {
+ return new LayoutProperty<>("symbol-avoid-edges", value);
+ }
+
+ /**
+ * If true, the symbols will not cross tile edges to avoid mutual collisions. Recommended in layers that don't have enough padding in the vector tile to prevent collisions, or if it is a point symbol layer placed after a line symbol layer.
+ */
+ public static Property<Function<Boolean>> symbolAvoidEdges(Function<Boolean> function) {
+ return new LayoutProperty<>("symbol-avoid-edges", function);
+ }
+
+ /**
+ * If true, the icon will be visible even if it collides with other previously drawn symbols.
+ */
+ public static Property<Boolean> iconAllowOverlap(Boolean value) {
+ return new LayoutProperty<>("icon-allow-overlap", value);
+ }
+
+ /**
+ * If true, the icon will be visible even if it collides with other previously drawn symbols.
+ */
+ public static Property<Function<Boolean>> iconAllowOverlap(Function<Boolean> function) {
+ return new LayoutProperty<>("icon-allow-overlap", function);
+ }
+
+ /**
+ * If true, other symbols can be visible even if they collide with the icon.
+ */
+ public static Property<Boolean> iconIgnorePlacement(Boolean value) {
+ return new LayoutProperty<>("icon-ignore-placement", value);
+ }
+
+ /**
+ * If true, other symbols can be visible even if they collide with the icon.
+ */
+ public static Property<Function<Boolean>> iconIgnorePlacement(Function<Boolean> function) {
+ return new LayoutProperty<>("icon-ignore-placement", function);
+ }
+
+ /**
+ * If true, text will display without their corresponding icons when the icon collides with other symbols and the text does not.
+ */
+ public static Property<Boolean> iconOptional(Boolean value) {
+ return new LayoutProperty<>("icon-optional", value);
+ }
+
+ /**
+ * If true, text will display without their corresponding icons when the icon collides with other symbols and the text does not.
+ */
+ public static Property<Function<Boolean>> iconOptional(Function<Boolean> function) {
+ return new LayoutProperty<>("icon-optional", function);
+ }
+
+ /**
+ * Orientation of icon when map is rotated.
+ */
+ public static Property<String> iconRotationAlignment(@Property.ICON_ROTATION_ALIGNMENT String value) {
+ return new LayoutProperty<>("icon-rotation-alignment", value);
+ }
+
+ /**
+ * Orientation of icon when map is rotated.
+ */
+ public static Property<Function<String>> iconRotationAlignment(Function<String> function) {
+ return new LayoutProperty<>("icon-rotation-alignment", function);
+ }
+
+ /**
+ * Scale factor for icon. 1 is original size, 3 triples the size.
+ */
+ public static Property<Float> iconSize(Float value) {
+ return new LayoutProperty<>("icon-size", value);
+ }
+
+ /**
+ * Scale factor for icon. 1 is original size, 3 triples the size.
+ */
+ public static Property<Function<Float>> iconSize(Function<Float> function) {
+ return new LayoutProperty<>("icon-size", function);
+ }
+
+ /**
+ * Position and scale an icon by the its corresponding text.
+ */
+ public static Property<String> iconTextFit(@Property.ICON_TEXT_FIT String value) {
+ return new LayoutProperty<>("icon-text-fit", value);
+ }
+
+ /**
+ * Position and scale an icon by the its corresponding text.
+ */
+ public static Property<Function<String>> iconTextFit(Function<String> function) {
+ return new LayoutProperty<>("icon-text-fit", function);
+ }
+
+ /**
+ * Size of padding area around the text-fit size in clockwise order: top, right, bottom, left.
+ */
+ public static Property<Float[]> iconTextFitPadding(Float[] value) {
+ return new LayoutProperty<>("icon-text-fit-padding", value);
+ }
+
+ /**
+ * Size of padding area around the text-fit size in clockwise order: top, right, bottom, left.
+ */
+ public static Property<Function<Float[]>> iconTextFitPadding(Function<Float[]> function) {
+ return new LayoutProperty<>("icon-text-fit-padding", function);
+ }
+
+ /**
+ * A string with {tokens} replaced, referencing the data property to pull from.
+ */
+ public static Property<String> iconImage(String value) {
+ return new LayoutProperty<>("icon-image", value);
+ }
+
+ /**
+ * A string with {tokens} replaced, referencing the data property to pull from.
+ */
+ public static Property<Function<String>> iconImage(Function<String> function) {
+ return new LayoutProperty<>("icon-image", function);
+ }
+
+ /**
+ * Rotates the icon clockwise.
+ */
+ public static Property<Float> iconRotate(Float value) {
+ return new LayoutProperty<>("icon-rotate", value);
+ }
+
+ /**
+ * Rotates the icon clockwise.
+ */
+ public static Property<Function<Float>> iconRotate(Function<Float> function) {
+ return new LayoutProperty<>("icon-rotate", function);
+ }
+
+ /**
+ * Size of the additional area around the icon bounding box used for detecting symbol collisions.
+ */
+ public static Property<Float> iconPadding(Float value) {
+ return new LayoutProperty<>("icon-padding", value);
+ }
+
+ /**
+ * Size of the additional area around the icon bounding box used for detecting symbol collisions.
+ */
+ public static Property<Function<Float>> iconPadding(Function<Float> function) {
+ return new LayoutProperty<>("icon-padding", function);
+ }
+
+ /**
+ * If true, the icon may be flipped to prevent it from being rendered upside-down.
+ */
+ public static Property<Boolean> iconKeepUpright(Boolean value) {
+ return new LayoutProperty<>("icon-keep-upright", value);
+ }
+
+ /**
+ * If true, the icon may be flipped to prevent it from being rendered upside-down.
+ */
+ public static Property<Function<Boolean>> iconKeepUpright(Function<Boolean> function) {
+ return new LayoutProperty<>("icon-keep-upright", function);
+ }
+
+ /**
+ * Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up.
+ */
+ public static Property<Float[]> iconOffset(Float[] value) {
+ return new LayoutProperty<>("icon-offset", value);
+ }
+
+ /**
+ * Offset distance of icon from its anchor. Positive values indicate right and down, while negative values indicate left and up.
+ */
+ public static Property<Function<Float[]>> iconOffset(Function<Float[]> function) {
+ return new LayoutProperty<>("icon-offset", function);
+ }
+
+ /**
+ * Aligns text to the plane of the `viewport` or the `map` when the map is pitched. Matches `text-rotation-alignment` if unspecified.
+ */
+ public static Property<String> textPitchAlignment(@Property.TEXT_PITCH_ALIGNMENT String value) {
+ return new LayoutProperty<>("text-pitch-alignment", value);
+ }
+
+ /**
+ * Aligns text to the plane of the `viewport` or the `map` when the map is pitched. Matches `text-rotation-alignment` if unspecified.
+ */
+ public static Property<Function<String>> textPitchAlignment(Function<String> function) {
+ return new LayoutProperty<>("text-pitch-alignment", function);
+ }
+
+ /**
+ * Orientation of text when map is rotated.
+ */
+ public static Property<String> textRotationAlignment(@Property.TEXT_ROTATION_ALIGNMENT String value) {
+ return new LayoutProperty<>("text-rotation-alignment", value);
+ }
+
+ /**
+ * Orientation of text when map is rotated.
+ */
+ public static Property<Function<String>> textRotationAlignment(Function<String> function) {
+ return new LayoutProperty<>("text-rotation-alignment", function);
+ }
+
+ /**
+ * Value to use for a text label. Feature properties are specified using tokens like {field_name}.
+ */
+ public static Property<String> textField(String value) {
+ return new LayoutProperty<>("text-field", value);
+ }
+
+ /**
+ * Value to use for a text label. Feature properties are specified using tokens like {field_name}.
+ */
+ public static Property<Function<String>> textField(Function<String> function) {
+ return new LayoutProperty<>("text-field", function);
+ }
+
+ /**
+ * Font stack to use for displaying text.
+ */
+ public static Property<String[]> textFont(String[] value) {
+ return new LayoutProperty<>("text-font", value);
+ }
+
+ /**
+ * Font stack to use for displaying text.
+ */
+ public static Property<Function<String[]>> textFont(Function<String[]> function) {
+ return new LayoutProperty<>("text-font", function);
+ }
+
+ /**
+ * Font size.
+ */
+ public static Property<Float> textSize(Float value) {
+ return new LayoutProperty<>("text-size", value);
+ }
+
+ /**
+ * Font size.
+ */
+ public static Property<Function<Float>> textSize(Function<Float> function) {
+ return new LayoutProperty<>("text-size", function);
+ }
+
+ /**
+ * The maximum line width for text wrapping.
+ */
+ public static Property<Float> textMaxWidth(Float value) {
+ return new LayoutProperty<>("text-max-width", value);
+ }
+
+ /**
+ * The maximum line width for text wrapping.
+ */
+ public static Property<Function<Float>> textMaxWidth(Function<Float> function) {
+ return new LayoutProperty<>("text-max-width", function);
+ }
+
+ /**
+ * Text leading value for multi-line text.
+ */
+ public static Property<Float> textLineHeight(Float value) {
+ return new LayoutProperty<>("text-line-height", value);
+ }
+
+ /**
+ * Text leading value for multi-line text.
+ */
+ public static Property<Function<Float>> textLineHeight(Function<Float> function) {
+ return new LayoutProperty<>("text-line-height", function);
+ }
+
+ /**
+ * Text tracking amount.
+ */
+ public static Property<Float> textLetterSpacing(Float value) {
+ return new LayoutProperty<>("text-letter-spacing", value);
+ }
+
+ /**
+ * Text tracking amount.
+ */
+ public static Property<Function<Float>> textLetterSpacing(Function<Float> function) {
+ return new LayoutProperty<>("text-letter-spacing", function);
+ }
+
+ /**
+ * Text justification options.
+ */
+ public static Property<String> textJustify(@Property.TEXT_JUSTIFY String value) {
+ return new LayoutProperty<>("text-justify", value);
+ }
+
+ /**
+ * Text justification options.
+ */
+ public static Property<Function<String>> textJustify(Function<String> function) {
+ return new LayoutProperty<>("text-justify", function);
+ }
+
+ /**
+ * Part of the text placed closest to the anchor.
+ */
+ public static Property<String> textAnchor(@Property.TEXT_ANCHOR String value) {
+ return new LayoutProperty<>("text-anchor", value);
+ }
+
+ /**
+ * Part of the text placed closest to the anchor.
+ */
+ public static Property<Function<String>> textAnchor(Function<String> function) {
+ return new LayoutProperty<>("text-anchor", function);
+ }
+
+ /**
+ * Maximum angle change between adjacent characters.
+ */
+ public static Property<Float> textMaxAngle(Float value) {
+ return new LayoutProperty<>("text-max-angle", value);
+ }
+
+ /**
+ * Maximum angle change between adjacent characters.
+ */
+ public static Property<Function<Float>> textMaxAngle(Function<Float> function) {
+ return new LayoutProperty<>("text-max-angle", function);
+ }
+
+ /**
+ * Rotates the text clockwise.
+ */
+ public static Property<Float> textRotate(Float value) {
+ return new LayoutProperty<>("text-rotate", value);
+ }
+
+ /**
+ * Rotates the text clockwise.
+ */
+ public static Property<Function<Float>> textRotate(Function<Float> function) {
+ return new LayoutProperty<>("text-rotate", function);
+ }
+
+ /**
+ * Size of the additional area around the text bounding box used for detecting symbol collisions.
+ */
+ public static Property<Float> textPadding(Float value) {
+ return new LayoutProperty<>("text-padding", value);
+ }
+
+ /**
+ * Size of the additional area around the text bounding box used for detecting symbol collisions.
+ */
+ public static Property<Function<Float>> textPadding(Function<Float> function) {
+ return new LayoutProperty<>("text-padding", function);
+ }
+
+ /**
+ * If true, the text may be flipped vertically to prevent it from being rendered upside-down.
+ */
+ public static Property<Boolean> textKeepUpright(Boolean value) {
+ return new LayoutProperty<>("text-keep-upright", value);
+ }
+
+ /**
+ * If true, the text may be flipped vertically to prevent it from being rendered upside-down.
+ */
+ public static Property<Function<Boolean>> textKeepUpright(Function<Boolean> function) {
+ return new LayoutProperty<>("text-keep-upright", function);
+ }
+
+ /**
+ * Specifies how to capitalize text, similar to the CSS `text-transform` property.
+ */
+ public static Property<String> textTransform(@Property.TEXT_TRANSFORM String value) {
+ return new LayoutProperty<>("text-transform", value);
+ }
+
+ /**
+ * Specifies how to capitalize text, similar to the CSS `text-transform` property.
+ */
+ public static Property<Function<String>> textTransform(Function<String> function) {
+ return new LayoutProperty<>("text-transform", function);
+ }
+
+ /**
+ * Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.
+ */
+ public static Property<Float[]> textOffset(Float[] value) {
+ return new LayoutProperty<>("text-offset", value);
+ }
+
+ /**
+ * Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.
+ */
+ public static Property<Function<Float[]>> textOffset(Function<Float[]> function) {
+ return new LayoutProperty<>("text-offset", function);
+ }
+
+ /**
+ * If true, the text will be visible even if it collides with other previously drawn symbols.
+ */
+ public static Property<Boolean> textAllowOverlap(Boolean value) {
+ return new LayoutProperty<>("text-allow-overlap", value);
+ }
+
+ /**
+ * If true, the text will be visible even if it collides with other previously drawn symbols.
+ */
+ public static Property<Function<Boolean>> textAllowOverlap(Function<Boolean> function) {
+ return new LayoutProperty<>("text-allow-overlap", function);
+ }
+
+ /**
+ * If true, other symbols can be visible even if they collide with the text.
+ */
+ public static Property<Boolean> textIgnorePlacement(Boolean value) {
+ return new LayoutProperty<>("text-ignore-placement", value);
+ }
+
+ /**
+ * If true, other symbols can be visible even if they collide with the text.
+ */
+ public static Property<Function<Boolean>> textIgnorePlacement(Function<Boolean> function) {
+ return new LayoutProperty<>("text-ignore-placement", function);
+ }
+
+ /**
+ * If true, icons will display without their corresponding text when the text collides with other symbols and the icon does not.
+ */
+ public static Property<Boolean> textOptional(Boolean value) {
+ return new LayoutProperty<>("text-optional", value);
+ }
+
+ /**
+ * If true, icons will display without their corresponding text when the text collides with other symbols and the icon does not.
+ */
+ public static Property<Function<Boolean>> textOptional(Function<Boolean> function) {
+ return new LayoutProperty<>("text-optional", function);
+ }
+
+ @SuppressLint("DefaultLocale")
+ static String colorToRgbaString(@ColorInt int value) {
+ return String.format("rgba(%d, %d, %d, %d)", (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, (value >> 24) & 0xFF);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java
new file mode 100644
index 0000000000..204c154743
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java
@@ -0,0 +1,56 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+/**
+ * Properties for Layer
+ */
+public class PropertyValue<T> {
+ private static final String TAG = PropertyValue.class.getSimpleName();
+
+ private final Object value;
+
+ /* package */ PropertyValue(Object value) {
+ this.value = value;
+ }
+
+ public boolean isNull() {
+ return value == null;
+ }
+
+ public boolean isFunction() {
+ return !isNull() && value instanceof Function;
+ }
+
+ public boolean isValue() {
+ return !isNull() && !isFunction();
+ }
+
+ @Nullable
+ public Function<T> getFunction() {
+ if (isFunction()) {
+ //noinspection unchecked
+ return (Function<T>) value;
+ } else {
+ Log.w(TAG, "not a function, try value");
+ return null;
+ }
+ }
+
+ @Nullable
+ public T getValue() {
+ if (isValue()) {
+ //noinspection unchecked
+ return (T) value;
+ } else {
+ Log.w(TAG, "not a value, try function");
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s (%s)", getClass().getSimpleName(), value != null ? value.getClass().getSimpleName() : null);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java
new file mode 100644
index 0000000000..a6872cef0f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java
@@ -0,0 +1,100 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import com.mapbox.mapboxsdk.exceptions.ConversionException;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.*;
+
+/**
+ * Raster Layer
+ */
+public class RasterLayer extends Layer {
+
+ public RasterLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public RasterLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ checkValidity();
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public RasterLayer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+
+
+ public RasterLayer withProperties(@NonNull Property<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getRasterOpacity() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetRasterOpacity());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getRasterHueRotate() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetRasterHueRotate());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getRasterBrightnessMin() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetRasterBrightnessMin());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getRasterBrightnessMax() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetRasterBrightnessMax());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getRasterSaturation() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetRasterSaturation());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getRasterContrast() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetRasterContrast());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getRasterFadeDuration() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetRasterFadeDuration());
+ }
+
+ private native Object nativeGetRasterOpacity();
+
+ private native Object nativeGetRasterHueRotate();
+
+ private native Object nativeGetRasterBrightnessMin();
+
+ private native Object nativeGetRasterBrightnessMax();
+
+ private native Object nativeGetRasterSaturation();
+
+ private native Object nativeGetRasterContrast();
+
+ private native Object nativeGetRasterFadeDuration();
+
+}
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
new file mode 100644
index 0000000000..ccbfdb411f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
@@ -0,0 +1,508 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import com.mapbox.mapboxsdk.exceptions.ConversionException;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.*;
+
+/**
+ * Symbol Layer
+ */
+public class SymbolLayer extends Layer {
+
+ public SymbolLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public SymbolLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ checkValidity();
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public SymbolLayer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+
+ public void setFilter(Filter.Statement filter) {
+ checkValidity();
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ checkValidity();
+ nativeSetFilter(filter);
+ }
+
+ public SymbolLayer withFilter(Object[] filter) {
+ setFilter(filter);
+ return this;
+ }
+
+ public SymbolLayer withFilter(Filter.Statement filter) {
+ setFilter(filter);
+ return this;
+ }
+
+
+ public SymbolLayer withProperties(@NonNull Property<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getSymbolPlacement() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetSymbolPlacement());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getSymbolSpacing() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetSymbolSpacing());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getSymbolAvoidEdges() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetSymbolAvoidEdges());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getIconAllowOverlap() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetIconAllowOverlap());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getIconIgnorePlacement() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetIconIgnorePlacement());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getIconOptional() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetIconOptional());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconRotationAlignment() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetIconRotationAlignment());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getIconSize() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetIconSize());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconTextFit() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetIconTextFit());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getIconTextFitPadding() {
+ checkValidity();
+ return (PropertyValue<Float[]>) new PropertyValue(nativeGetIconTextFitPadding());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconImage() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetIconImage());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getIconRotate() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetIconRotate());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getIconPadding() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetIconPadding());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getIconKeepUpright() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetIconKeepUpright());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getIconOffset() {
+ checkValidity();
+ return (PropertyValue<Float[]>) new PropertyValue(nativeGetIconOffset());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getTextPitchAlignment() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetTextPitchAlignment());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getTextRotationAlignment() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetTextRotationAlignment());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getTextField() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetTextField());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String[]> getTextFont() {
+ checkValidity();
+ return (PropertyValue<String[]>) new PropertyValue(nativeGetTextFont());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextSize() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextSize());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextMaxWidth() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextMaxWidth());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextLineHeight() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextLineHeight());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextLetterSpacing() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextLetterSpacing());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getTextJustify() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetTextJustify());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getTextAnchor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetTextAnchor());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextMaxAngle() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextMaxAngle());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextRotate() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextRotate());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextPadding() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextPadding());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getTextKeepUpright() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetTextKeepUpright());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getTextTransform() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetTextTransform());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getTextOffset() {
+ checkValidity();
+ return (PropertyValue<Float[]>) new PropertyValue(nativeGetTextOffset());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getTextAllowOverlap() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetTextAllowOverlap());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getTextIgnorePlacement() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetTextIgnorePlacement());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Boolean> getTextOptional() {
+ checkValidity();
+ return (PropertyValue<Boolean>) new PropertyValue(nativeGetTextOptional());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getIconOpacity() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetIconOpacity());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconColor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetIconColor());
+ }
+ /**
+ * The color of the icon. This can only be used with sdf icons.
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int getIconColorAsInt() {
+ checkValidity();
+ PropertyValue<String> value = getIconColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("icon-color was set as a Function");
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconHaloColor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetIconHaloColor());
+ }
+ /**
+ * The color of the icon's halo. Icon halos can only be used with sdf icons.
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int getIconHaloColorAsInt() {
+ checkValidity();
+ PropertyValue<String> value = getIconHaloColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("icon-halo-color was set as a Function");
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getIconHaloWidth() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetIconHaloWidth());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getIconHaloBlur() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetIconHaloBlur());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getIconTranslate() {
+ checkValidity();
+ return (PropertyValue<Float[]>) new PropertyValue(nativeGetIconTranslate());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconTranslateAnchor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetIconTranslateAnchor());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextOpacity() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextOpacity());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getTextColor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetTextColor());
+ }
+ /**
+ * The color with which the text will be drawn.
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int getTextColorAsInt() {
+ checkValidity();
+ PropertyValue<String> value = getTextColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("text-color was set as a Function");
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getTextHaloColor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetTextHaloColor());
+ }
+ /**
+ * The color of the text's halo, which helps it stand out from backgrounds.
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int getTextHaloColorAsInt() {
+ checkValidity();
+ PropertyValue<String> value = getTextHaloColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("text-halo-color was set as a Function");
+ }
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextHaloWidth() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextHaloWidth());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getTextHaloBlur() {
+ checkValidity();
+ return (PropertyValue<Float>) new PropertyValue(nativeGetTextHaloBlur());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getTextTranslate() {
+ checkValidity();
+ return (PropertyValue<Float[]>) new PropertyValue(nativeGetTextTranslate());
+ }
+
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getTextTranslateAnchor() {
+ checkValidity();
+ return (PropertyValue<String>) new PropertyValue(nativeGetTextTranslateAnchor());
+ }
+
+ private native Object nativeGetSymbolPlacement();
+
+ private native Object nativeGetSymbolSpacing();
+
+ private native Object nativeGetSymbolAvoidEdges();
+
+ private native Object nativeGetIconAllowOverlap();
+
+ private native Object nativeGetIconIgnorePlacement();
+
+ private native Object nativeGetIconOptional();
+
+ private native Object nativeGetIconRotationAlignment();
+
+ private native Object nativeGetIconSize();
+
+ private native Object nativeGetIconTextFit();
+
+ private native Object nativeGetIconTextFitPadding();
+
+ private native Object nativeGetIconImage();
+
+ private native Object nativeGetIconRotate();
+
+ private native Object nativeGetIconPadding();
+
+ private native Object nativeGetIconKeepUpright();
+
+ private native Object nativeGetIconOffset();
+
+ private native Object nativeGetTextPitchAlignment();
+
+ private native Object nativeGetTextRotationAlignment();
+
+ private native Object nativeGetTextField();
+
+ private native Object nativeGetTextFont();
+
+ private native Object nativeGetTextSize();
+
+ private native Object nativeGetTextMaxWidth();
+
+ private native Object nativeGetTextLineHeight();
+
+ private native Object nativeGetTextLetterSpacing();
+
+ private native Object nativeGetTextJustify();
+
+ private native Object nativeGetTextAnchor();
+
+ private native Object nativeGetTextMaxAngle();
+
+ private native Object nativeGetTextRotate();
+
+ private native Object nativeGetTextPadding();
+
+ private native Object nativeGetTextKeepUpright();
+
+ private native Object nativeGetTextTransform();
+
+ private native Object nativeGetTextOffset();
+
+ private native Object nativeGetTextAllowOverlap();
+
+ private native Object nativeGetTextIgnorePlacement();
+
+ private native Object nativeGetTextOptional();
+
+ private native Object nativeGetIconOpacity();
+
+ private native Object nativeGetIconColor();
+
+ private native Object nativeGetIconHaloColor();
+
+ private native Object nativeGetIconHaloWidth();
+
+ private native Object nativeGetIconHaloBlur();
+
+ private native Object nativeGetIconTranslate();
+
+ private native Object nativeGetIconTranslateAnchor();
+
+ private native Object nativeGetTextOpacity();
+
+ private native Object nativeGetTextColor();
+
+ private native Object nativeGetTextHaloColor();
+
+ private native Object nativeGetTextHaloWidth();
+
+ private native Object nativeGetTextHaloBlur();
+
+ private native Object nativeGetTextTranslate();
+
+ private native Object nativeGetTextTranslateAnchor();
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs
new file mode 100644
index 0000000000..00d9f09124
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs
@@ -0,0 +1,107 @@
+<%
+ const type = locals.type;
+ const properties = locals.properties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import com.mapbox.mapboxsdk.exceptions.ConversionException;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.*;
+
+/**
+ * <%- camelize(type) %> Layer
+ */
+public class <%- camelize(type) %>Layer extends Layer {
+
+ public <%- camelize(type) %>Layer(long nativePtr) {
+ super(nativePtr);
+ }
+
+<% if (type === 'background') { -%>
+ public <%- camelize(type) %>Layer(String layerId) {
+ initialize(layerId);
+ }
+
+ protected native void initialize(String layerId);
+<% } else { -%>
+ public <%- camelize(type) %>Layer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ checkValidity();
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public <%- camelize(type) %>Layer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+<% } -%>
+
+<% if (type !== 'background' && type !== 'raster') { -%>
+ public void setFilter(Filter.Statement filter) {
+ checkValidity();
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ checkValidity();
+ nativeSetFilter(filter);
+ }
+
+ public <%- camelize(type) %>Layer withFilter(Object[] filter) {
+ setFilter(filter);
+ return this;
+ }
+
+ public <%- camelize(type) %>Layer withFilter(Filter.Statement filter) {
+ setFilter(filter);
+ return this;
+ }
+
+<% } -%>
+
+ public <%- camelize(type) %>Layer withProperties(@NonNull Property<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+<% for (const property of properties) { -%>
+ @SuppressWarnings("unchecked")
+ public PropertyValue<<%- propertyType(property) %>> get<%- camelize(property.name) %>() {
+ checkValidity();
+ return (PropertyValue<<%- propertyType(property) %>>) new PropertyValue(nativeGet<%- camelize(property.name) %>());
+ }
+ <% if (property.type == 'color') { -%>
+ /**
+ * <%- property.doc %>
+ * @throws RuntimeException
+ */
+ @ColorInt
+ public int get<%- camelize(property.name) %>AsInt() {
+ checkValidity();
+ PropertyValue<<%- propertyType(property) %>> value = get<%- camelize(property.name) %>();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("<%- property.name %> was set as a Function");
+ }
+ }
+
+ <% } -%>
+
+<% } -%>
+<% for (const property of properties) { -%>
+ private native Object nativeGet<%- camelize(property.name) %>();
+
+<% } -%>
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property.java.ejs
new file mode 100644
index 0000000000..e734a9ff11
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property.java.ejs
@@ -0,0 +1,53 @@
+<%
+ const properties = locals.properties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Paint/Layout properties for Layer
+ */
+public abstract class Property<T> {
+
+ //visibility
+ public static final String VISIBLE = "visible";
+ public static final String NONE = "none";
+
+ @StringDef({
+ VISIBLE,
+ NONE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VISIBILITY {}
+
+<% for (const property of properties) { -%>
+ //<%- property.name %>
+<% for (const value of property.values) { -%>
+ public static final String <%- snakeCaseUpper(property.name) %>_<%- snakeCaseUpper(value) %> = "<%- value %>";
+<% } -%>
+
+ @StringDef({
+ <% for (const value of property.values) { -%>
+ <%- snakeCaseUpper(property.name) %>_<%- snakeCaseUpper(value) %>,
+ <% } -%>
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface <%- snakeCaseUpper(property.name) %> {}
+
+<% } -%>
+
+ //Class definition
+ public final String name;
+ public final T value;
+
+ /* package */ Property(String name, T value) {
+ this.name = name;
+ this.value = value;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs
new file mode 100644
index 0000000000..c7424bc31e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs
@@ -0,0 +1,78 @@
+<%
+ const paintProperties = locals.paintProperties;
+ const layoutProperties = locals.layoutProperties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.ColorInt;
+
+/**
+ * Constructs paint/layout properties for Layers
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers>Layer style documentation</a>
+ */
+public class PropertyFactory {
+
+ /**
+ * Set visibility
+ */
+ public static Property<String> visibility(@Property.VISIBILITY String value) {
+ return new LayoutProperty<>("visibility", value);
+ }
+
+ /**
+ * Set visibility
+ */
+ public static Property<Function<String>> visibility(Function<String> function) {
+ return new LayoutProperty<>("visibility", function);
+ }
+
+<% for (const property of paintProperties) { -%>
+<% if (property.type == 'color') { -%>
+ /**
+ * <%- property.doc %>
+ */
+ public static Property<String> <%- camelizeWithLeadingLowercase(property.name) %>(@ColorInt int value) {
+ return new PaintProperty<>("<%- property.name %>", colorToRgbaString(value));
+ }
+
+<% } -%>
+ /**
+ * <%- property.doc %>
+ */
+ public static Property<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %>(<%- propertyTypeAnnotation(property) %><%- iff(() => propertyTypeAnnotation(property), " ") %><%- propertyType(property) %> value) {
+ return new PaintProperty<>("<%- property.name %>", value);
+ }
+
+ /**
+ * <%- property.doc %>
+ */
+ public static Property<Function<<%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(Function<<%- propertyType(property) %>> function) {
+ return new PaintProperty<>("<%- property.name %>", function);
+ }
+
+<% } -%>
+<% for (const property of layoutProperties) { -%>
+ /**
+ * <%- property.doc %>
+ */
+ public static Property<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %>(<%- propertyTypeAnnotation(property) %><%- iff(() => propertyTypeAnnotation(property), " ") %><%- propertyType(property) %> value) {
+ return new LayoutProperty<>("<%- property.name %>", value);
+ }
+
+ /**
+ * <%- property.doc %>
+ */
+ public static Property<Function<<%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(Function<<%- propertyType(property) %>> function) {
+ return new LayoutProperty<>("<%- property.name %>", function);
+ }
+
+<% } -%>
+ @SuppressLint("DefaultLocale")
+ static String colorToRgbaString(@ColorInt int value) {
+ return String.format("rgba(%d, %d, %d, %d)", (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, (value >> 24) & 0xFF);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
new file mode 100644
index 0000000000..fc7928015e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
@@ -0,0 +1,44 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import java.net.URL;
+import java.util.HashMap;
+
+/**
+ * A GeoJson source.
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">the style specification</a>
+ */
+public class GeoJsonSource extends Source {
+ public static final String TYPE = "geojson";
+ private static final String DATA_KEY = "data";
+
+ /**
+ * Create a GeoJsonSource from a raw json string
+ *
+ * @param id the source id
+ * @param geoJson raw Json body
+ */
+ public GeoJsonSource(String id, String geoJson) {
+ super(id, TYPE);
+ if (geoJson == null || geoJson.startsWith("http")) {
+ throw new IllegalArgumentException("Expected a raw json body");
+ }
+
+ //Wrap the String in a map as an Object is expected by the
+ //style conversion template
+ HashMap<String, String> wrapper = new HashMap<>();
+ wrapper.put(DATA_KEY, geoJson);
+ this.put(DATA_KEY, wrapper);
+ }
+
+ /**
+ * Create a GeoJsonSource from a remote geo json file
+ *
+ * @param id the source id
+ * @param url remote json file
+ */
+ public GeoJsonSource(String id, URL url) {
+ super(id, TYPE);
+ this.put(DATA_KEY, url.toExternalForm());
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java
new file mode 100644
index 0000000000..f5db6f2a37
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java
@@ -0,0 +1,33 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import java.net.URL;
+
+/**
+ * Construct a Raster Source.
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-raster">The style specificition</a>
+ */
+public class RasterSource extends Source {
+ public static final String TYPE = "raster";
+ private static final String URL_KEY = "url";
+ private static final String TILE_SIZE_KEY = "tileSize";
+
+ public RasterSource(String id, URL url) {
+ this(id, url.toExternalForm());
+ }
+
+ public RasterSource(String id, String url) {
+ super(id, TYPE);
+ this.put(URL_KEY, url);
+ }
+
+ public RasterSource(String id, TileSet tileSet) {
+ super(id, TYPE);
+ this.putAll(tileSet.toValueObject());
+ }
+
+ public RasterSource withTileSize(int tileSize) {
+ this.put(TILE_SIZE_KEY, (float) tileSize);
+ return this;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java
new file mode 100644
index 0000000000..d9aacdc80d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java
@@ -0,0 +1,16 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import java.util.HashMap;
+
+public abstract class Source extends HashMap<String, Object> {
+ private final String id;
+
+ protected Source(String id, String type) {
+ this.put("type", type);
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/TileSet.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/TileSet.java
new file mode 100644
index 0000000000..1da8827d72
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/TileSet.java
@@ -0,0 +1,290 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import android.support.annotation.Size;
+
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tile set
+ *
+ * @see <a href="https://github.com/mapbox/tilejson-spec/tree/master/2.1.0">The tileset specification</a>
+ */
+public class TileSet {
+ private final String tilejson;
+ private String name;
+ private String description;
+ private String version;
+ private String attribution;
+ private String template;
+ private String legend;
+ private String scheme;
+ private final String[] tiles;
+ private String[] grids;
+ private String[] data;
+ private Float minZoom;
+ private Float maxZoom;
+ private Float[] bounds;
+ private Float[] center;
+
+ /**
+ * @param tilejson A semver.org style version number. Describes the version of the TileJSON spec that is implemented by this JSON object.
+ * @param tiles An array of tile endpoints. {z}, {x} and {y}, if present, are replaced with the corresponding integers.
+ * If multiple endpoints are specified, clients may use any combination of endpoints. All endpoints MUST return the same
+ * content for the same URL. The array MUST contain at least one endpoint.
+ * Example: "http:localhost:8888/admin/1.0.0/world-light,broadband/{z}/{x}/{y}.png"
+ */
+ public TileSet(String tilejson, String... tiles) {
+ this.tilejson = tilejson;
+ this.tiles = tiles;
+ }
+
+ public String getTilejson() {
+ return tilejson;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * A name describing the tileset. The name can
+ * contain any legal character. Implementations SHOULD NOT interpret the
+ * name as HTML.
+ * "name": "compositing",
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * A text description of the tileset. The
+ * description can contain any legal character.
+ * Implementations SHOULD NOT
+ * interpret the description as HTML.
+ * "description": "A simple, light grey world."
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getAttribution() {
+ return attribution;
+ }
+
+ /**
+ * Default: null. Contains an attribution to be displayed
+ * when the map is shown to a user. Implementations MAY decide to treat this
+ * as HTML or literal text. For security reasons, make absolutely sure that
+ * this field can't be abused as a vector for XSS or beacon tracking.
+ * "attribution": "<a href='http:openstreetmap.org'>OSM contributors</a>",
+ */
+ public void setAttribution(String attribution) {
+ this.attribution = attribution;
+ }
+
+ public String getTemplate() {
+ return template;
+ }
+
+ /**
+ * Contains a mustache template to be used to
+ * format data from grids for interaction.
+ * See https:github.com/mapbox/utfgrid-spec/tree/master/1.2
+ * for the interactivity specification.
+ * "template": "{{#__teaser__}}{{NAME}}{{/__teaser__}}"
+ */
+ public void setTemplate(String template) {
+ this.template = template;
+ }
+
+ public String getLegend() {
+ return legend;
+ }
+
+ /**
+ * Contains a legend to be displayed with the map.
+ * Implementations MAY decide to treat this as HTML or literal text.
+ * For security reasons, make absolutely sure that this field can't be
+ * abused as a vector for XSS or beacon tracking.
+ * "legend": "Dangerous zones are red, safe zones are green"
+ */
+ public void setLegend(String legend) {
+ this.legend = legend;
+ }
+
+ public String getScheme() {
+ return scheme;
+ }
+
+ /**
+ * Default: "xyz". Either "xyz" or "tms". Influences the y
+ * direction of the tile coordinates.
+ * The global-mercator (aka Spherical Mercator) profile is assumed.
+ * "scheme": "xyz"
+ */
+ public void setScheme(String scheme) {
+ this.scheme = scheme;
+ }
+
+ public String[] getTiles() {
+ return tiles;
+ }
+
+ public String[] getGrids() {
+ return grids;
+ }
+
+ /**
+ * An array of interactivity endpoints. {z}, {x}
+ * and {y}, if present, are replaced with the corresponding integers. If multiple
+ * endpoints are specified, clients may use any combination of endpoints.
+ * All endpoints MUST return the same content for the same URL.
+ * If the array doesn't contain any entries, interactivity is not supported
+ * for this tileset. See https:github.com/mapbox/utfgrid-spec/tree/master/1.2
+ * for the interactivity specification.
+ * <p/>
+ * Example: "http:localhost:8888/admin/1.0.0/broadband/{z}/{x}/{y}.grid.json"
+ */
+ public void setGrids(String... grids) {
+ this.grids = grids;
+ }
+
+ public String[] getData() {
+ return data;
+ }
+
+ /**
+ * An array of data files in GeoJSON format.
+ * {z}, {x} and {y}, if present,
+ * are replaced with the corresponding integers. If multiple
+ * endpoints are specified, clients may use any combination of endpoints.
+ * All endpoints MUST return the same content for the same URL.
+ * If the array doesn't contain any entries, then no data is present in
+ * the map.
+ * <p/>
+ * "http:localhost:8888/admin/data.geojson"
+ */
+ public void setData(String... data) {
+ this.data = data;
+ }
+
+ public float getMinZoom() {
+ return minZoom;
+ }
+
+ /**
+ * 0. >= 0, <= 22. An integer specifying the minimum zoom level.
+ */
+ public void setMinZoom(float minZoom) {
+ this.minZoom = minZoom;
+ }
+
+ public float getMaxZoom() {
+ return maxZoom;
+ }
+
+ /**
+ * 0. >= 0, <= 22. An integer specifying the maximum zoom level.
+ */
+ public void setMaxZoom(float maxZoom) {
+ this.maxZoom = maxZoom;
+ }
+
+ public Float[] getBounds() {
+ return bounds;
+ }
+
+ /**
+ * Default: [-180, -90, 180, 90]. The maximum extent of available map tiles. Bounds MUST define an area
+ * covered by all zoom levels. The bounds are represented in WGS:84
+ * latitude and longitude values, in the order left, bottom, right, top.
+ * Values may be integers or floating point numbers.
+ */
+ public void setBounds(@Size(value = 4) Float... bounds) {
+ this.bounds = bounds;
+ }
+
+ public Float[] getCenter() {
+ return center;
+ }
+
+ /**
+ * The first value is the longitude, the second is latitude (both in
+ * WGS:84 values), the third value is the zoom level as an integer.
+ * Longitude and latitude MUST be within the specified bounds.
+ * The zoom level MUST be between minzoom and maxzoom.
+ * Implementations can use this value to set the default location. If the
+ * value is null, implementations may use their own algorithm for
+ * determining a default location.
+ */
+ public void setCenter(@Size(value = 2) Float... center) {
+ this.center = center;
+ }
+
+ public void setCenter(LatLng center) {
+ this.center = new Float[]{(float) center.getLongitude(), (float) center.getLatitude()};
+ }
+
+ Map<String, Object> toValueObject() {
+ Map<String, Object> result = new HashMap<>();
+ result.put("tilejson", tilejson);
+ result.put("tiles", tiles);
+
+ if (name != null) {
+ result.put("name", name);
+ }
+ if (description != null) {
+ result.put("description", description);
+ }
+ if (version != null) {
+ result.put("version", version);
+ }
+ if (attribution != null) {
+ result.put("attribution", attribution);
+ }
+ if (template != null) {
+ result.put("template", template);
+ }
+ if (legend != null) {
+ result.put("legend", legend);
+ }
+ if (scheme != null) {
+ result.put("scheme", scheme);
+ }
+ if (grids != null) {
+ result.put("grids", grids);
+ }
+ if (data != null) {
+ result.put("data", data);
+ }
+ if (minZoom != null) {
+ result.put("minzoom", minZoom);
+ }
+ if (maxZoom != null) {
+ result.put("maxzoom", maxZoom);
+ }
+ if (bounds != null) {
+ result.put("bounds", bounds);
+ }
+ if (center != null) {
+ result.put("center", center);
+ }
+
+ return result;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
new file mode 100644
index 0000000000..381294083a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
@@ -0,0 +1,45 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import java.net.URL;
+
+/**
+ * A vector source.
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-vector">the style specification</a>
+ */
+public class VectorSource extends Source {
+ public static final String TYPE = "vector";
+ private static final String URL_KEY = "url";
+
+ /**
+ * Create a vector source from a remote url
+ *
+ * @param id the source id
+ * @param url the url
+ */
+ public VectorSource(String id, URL url) {
+ this(id, url.toExternalForm());
+ }
+
+ /**
+ * Create a vector source from a remote url
+ *
+ * @param id the source id
+ * @param url the url
+ */
+ public VectorSource(String id, String url) {
+ super(id, TYPE);
+ this.put(URL_KEY, url);
+ }
+
+ /**
+ * Create a vector source from a tilset
+ *
+ * @param id the source id
+ * @param tileSet the tileset
+ */
+ public VectorSource(String id, TileSet tileSet) {
+ super(id, TYPE);
+ this.putAll(tileSet.toValueObject());
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/GzipRequestInterceptor.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/GzipRequestInterceptor.java
new file mode 100644
index 0000000000..1b13e9502f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/GzipRequestInterceptor.java
@@ -0,0 +1,54 @@
+package com.mapbox.mapboxsdk.telemetry;
+
+import android.util.Log;
+import java.io.IOException;
+import okhttp3.Interceptor;
+import okhttp3.MediaType;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okio.BufferedSink;
+import okio.GzipSink;
+import okio.Okio;
+
+/**
+ * OkHttp Interceptor for Gzipping Telemetry Data requests to the server.
+ * Based on: https://github.com/square/okhttp/wiki/Interceptors
+ */
+public final class GzipRequestInterceptor implements Interceptor {
+
+ private static final String TAG = "GzipRequestInterceptor";
+
+ @Override public Response intercept(Interceptor.Chain chain) throws IOException {
+ Request originalRequest = chain.request();
+ if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
+ Log.d(TAG, "Not compressing");
+ return chain.proceed(originalRequest);
+ }
+
+ Log.d(TAG, "Compressing");
+ Request compressedRequest = originalRequest.newBuilder()
+ .header("Content-Encoding", "gzip")
+ .method(originalRequest.method(), gzip(originalRequest.body()))
+ .build();
+ return chain.proceed(compressedRequest);
+ }
+
+ private RequestBody gzip(final RequestBody body) {
+ return new RequestBody() {
+ @Override public MediaType contentType() {
+ return body.contentType();
+ }
+
+ @Override public long contentLength() {
+ return -1; // We don't know the compressed length in advance!
+ }
+
+ @Override public void writeTo(BufferedSink sink) throws IOException {
+ BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
+ body.writeTo(gzipSink);
+ gzipSink.close();
+ }
+ };
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEvent.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEvent.java
index 41dec08ee9..22e37ec539 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEvent.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEvent.java
@@ -7,7 +7,7 @@ import java.io.Serializable;
*/
public class MapboxEvent implements Serializable {
public static final int VERSION_NUMBER = 2;
- public static final String MAPBOX_EVENTS_BASE_URL = "https://api.mapbox.com";
+ public static final String MAPBOX_EVENTS_BASE_URL = "https://events.mapbox.com";
public static final String SOURCE_MAPBOX = "mapbox";
// Event Types
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java
index b386ed6549..84df822ce9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java
@@ -88,8 +88,12 @@ public class MapboxEventManager {
private static long flushDelayInMillis = 1000 * 60 * 3; // 3 Minutes
private static final int SESSION_ID_ROTATION_HOURS = 24;
+ private static final int FLUSH_EVENTS_CAP = 1000;
+
private static MessageDigest messageDigest = null;
+ private static final double locationEventAccuracy = 10000000;
+
private Timer timer = null;
/**
@@ -159,10 +163,12 @@ public class MapboxEventManager {
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
String stagingURL = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_STAGING_SERVER);
String stagingAccessToken = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_STAGING_ACCESS_TOKEN);
- String appName = context.getPackageManager().getApplicationLabel(appInfo).toString();
- PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
- String versionName = packageInfo.versionName;
- int versionCode = packageInfo.versionCode;
+
+ if (TextUtils.isEmpty(stagingURL) || TextUtils.isEmpty(stagingAccessToken)) {
+ Log.d(TAG, "Looking in SharedPreferences for Staging Credentials");
+ stagingURL = prefs.getString(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_STAGING_URL, null);
+ stagingAccessToken = prefs.getString(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_STAGING_ACCESS_TOKEN, null);
+ }
if (!TextUtils.isEmpty(stagingURL)) {
eventsURL = stagingURL;
@@ -172,6 +178,11 @@ public class MapboxEventManager {
this.accessToken = stagingAccessToken;
}
+ String appName = context.getPackageManager().getApplicationLabel(appInfo).toString();
+ PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
+ String versionName = packageInfo.versionName;
+ int versionCode = packageInfo.versionCode;
+
// Build User Agent
if (TextUtils.equals(userAgent, BuildConfig.MAPBOX_EVENTS_USER_AGENT_BASE) && !TextUtils.isEmpty(appName) && !TextUtils.isEmpty(versionName)) {
userAgent = appName + "/" + versionName + "/" + versionCode + " " + userAgent;
@@ -309,23 +320,48 @@ public class MapboxEventManager {
}
/**
+ * Centralized method for adding populated event to the queue allowing for cap size checking
+ * @param event Event to add to the Events Queue
+ */
+ private void putEventOnQueue(@NonNull Hashtable<String, Object> event) {
+ if (event == null) {
+ return;
+ }
+ events.add(event);
+ if (events.size() == FLUSH_EVENTS_CAP) {
+ Log.d(TAG, "eventsSize == flushCap so send data.");
+ flushEventsQueueImmediately();
+ }
+ }
+
+ /**
* Adds a Location Event to the system for processing
* @param location Location event
*/
public void addLocationEvent(Location location) {
+
+ // NaN and Infinite checks to prevent JSON errors at send to server time
+ if (Double.isNaN(location.getLatitude()) || Double.isNaN(location.getLongitude()) || Double.isNaN(location.getAltitude())) {
+ return;
+ }
+
+ if (Double.isInfinite(location.getLatitude()) || Double.isInfinite(location.getLongitude()) || Double.isInfinite(location.getAltitude())) {
+ return;
+ }
+
// Add Location even to queue
Hashtable<String, Object> event = new Hashtable<>();
event.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_LOCATION);
event.put(MapboxEvent.ATTRIBUTE_CREATED, generateCreateDate());
event.put(MapboxEvent.ATTRIBUTE_SOURCE, MapboxEvent.SOURCE_MAPBOX);
event.put(MapboxEvent.ATTRIBUTE_SESSION_ID, encodeString(mapboxSessionId));
- event.put(MapboxEvent.KEY_LATITUDE, location.getLatitude());
- event.put(MapboxEvent.KEY_LONGITUDE, location.getLongitude());
+ event.put(MapboxEvent.KEY_LATITUDE, Math.floor(location.getLatitude() * locationEventAccuracy) / locationEventAccuracy);
+ event.put(MapboxEvent.KEY_LONGITUDE, Math.floor(location.getLongitude() * locationEventAccuracy) / locationEventAccuracy);
event.put(MapboxEvent.KEY_ALTITUDE, location.getAltitude());
event.put(MapboxEvent.ATTRIBUTE_OPERATING_SYSTEM, operatingSystem);
event.put(MapboxEvent.ATTRIBUTE_APPLICATION_STATE, getApplicationState());
- events.add(event);
+ putEventOnQueue(event);
rotateSessionId();
}
@@ -364,7 +400,7 @@ public class MapboxEventManager {
eventWithAttributes.put(MapboxEvent.ATTRIBUTE_WIFI, getConnectedToWifi());
// Put Map Load on events before Turnstile clears it
- events.add(eventWithAttributes);
+ putEventOnQueue(eventWithAttributes);
// Turnstile
pushTurnstileEvent();
@@ -391,7 +427,7 @@ public class MapboxEventManager {
return;
}
- events.add(eventWithAttributes);
+ putEventOnQueue(eventWithAttributes);
}
/**
@@ -608,7 +644,9 @@ public class MapboxEventManager {
// =========
JSONArray jsonArray = new JSONArray();
- for (Hashtable<String, Object> evt : events) {
+ Vector<Hashtable<String, Object>> eventsClone = (Vector<Hashtable<String, Object>>) events.clone();
+
+ for (Hashtable<String, Object> evt : eventsClone) {
JSONObject jsonObject = new JSONObject();
// Build the JSON but only if there's a value for it in the evt
@@ -686,19 +724,23 @@ public class MapboxEventManager {
.add("cloudfront-staging.tilestream.net", "sha256/sPbNCVpVasMJxps3IqFfLTRKkVnRCLrTlZVc5kspqlkw=")
.add("cloudfront-staging.tilestream.net", "sha256/h6801m+z8v3zbgkRHpq6L29Esgfzhj89C1SyUCOQmqU=")
// Prod - Geotrust
- .add("api.mapbox.com", "sha256/svaiYM/ZVIfxC+CMDe4kj1KsviQmzyZ9To8nQqUJwFI=")
- .add("api.mapbox.com", "sha256/owrR9U9FWDWtrFF+myoRIu75JwU4sJwzvhCNLZoY37g=")
- .add("api.mapbox.com", "sha256/SQVGZiOrQXi+kqxcvWWE96HhfydlLVqFr4lQTqI5qqo=")
+ .add("events.mapbox.com", "sha256/BhynraKizavqoC5U26qgYuxLZst6pCu9J5stfL6RSYY=")
+ .add("events.mapbox.com", "sha256/owrR9U9FWDWtrFF+myoRIu75JwU4sJwzvhCNLZoY37g=")
+ .add("events.mapbox.com", "sha256/SQVGZiOrQXi+kqxcvWWE96HhfydlLVqFr4lQTqI5qqo=")
// Prod - DigiCert
- .add("api.mapbox.com", "sha256/JL+uwAwpA2U1UVl/AFdZy1ZnvkZJ1P1hRfmfPaPVSLU=")
- .add("api.mapbox.com", "sha256/RRM1dGqnDFsCJXBTHky16vi1obOlCgFFn/yOhI/y+ho=")
- .add("api.mapbox.com", "sha256/WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=")
+ .add("events.mapbox.com", "sha256/Tb0uHZ/KQjWh8N9+CZFLc4zx36LONQ55l6laDi1qtT4=")
+ .add("events.mapbox.com", "sha256/RRM1dGqnDFsCJXBTHky16vi1obOlCgFFn/yOhI/y+ho=")
+ .add("events.mapbox.com", "sha256/WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=")
.build();
- OkHttpClient client = new OkHttpClient.Builder().certificatePinner(certificatePinner).build();
+ OkHttpClient client = new OkHttpClient.Builder()
+ .certificatePinner(certificatePinner)
+ .addInterceptor(new GzipRequestInterceptor())
+ .build();
RequestBody body = RequestBody.create(JSON, jsonArray.toString());
String url = eventsURL + "/events/v2?access_token=" + accessToken;
+// Log.d(TAG, "Events URL = " + url);
Request request = new Request.Builder()
.url(url)
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java
index a0de07c5f1..ef404840e8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java
@@ -3,6 +3,7 @@ package com.mapbox.mapboxsdk.utils;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
@@ -11,6 +12,10 @@ import android.util.TypedValue;
import android.widget.ImageView;
import com.mapbox.mapboxsdk.R;
+import com.mapbox.mapboxsdk.exceptions.ConversionException;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class ColorUtils {
@@ -87,4 +92,26 @@ public class ColorUtils {
Drawable wrappedDrawable = DrawableCompat.wrap(originalDrawable);
DrawableCompat.setTintList(wrappedDrawable, getSelector(tintColor));
}
+
+ static int normalizeColorComponent(String value) {
+ return (int) (Float.parseFloat(value) * 255);
+ }
+
+ /**
+ * Convert an rgba string to a Color int
+ *
+ * @throws ConversionException on illegal input
+ */
+ @ColorInt
+ public static int rgbaToColor(String value) {
+ Pattern c = Pattern.compile("rgba?\\s*\\(\\s*(\\d+\\.?\\d*)\\s*,\\s*(\\d+\\.?\\d*)\\s*,\\s*(\\d+\\.?\\d*)\\s*,?\\s*(\\d+\\.?\\d*)?\\s*\\)");
+ Matcher m = c.matcher(value);
+ if (m.matches() && m.groupCount() == 3) {
+ return Color.rgb(normalizeColorComponent(m.group(1)), normalizeColorComponent(m.group(2)), normalizeColorComponent(m.group(3)));
+ } else if (m.matches() && m.groupCount() == 4) {
+ return Color.argb(normalizeColorComponent(m.group(4)), normalizeColorComponent(m.group(1)), normalizeColorComponent(m.group(2)), normalizeColorComponent(m.group(3)));
+ } else {
+ throw new ConversionException("Not a valid rgb/rgba value");
+ }
+ }
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/default_markerview.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/default_markerview.png
new file mode 100644
index 0000000000..651482f3ee
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/default_markerview.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/default_markerview.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/default_markerview.png
new file mode 100644
index 0000000000..63cb7b5f4b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/default_markerview.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/default_markerview.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/default_markerview.png
new file mode 100644
index 0000000000..175f88ff88
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/default_markerview.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/default_markerview.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/default_markerview.png
new file mode 100644
index 0000000000..be782e1d4b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/default_markerview.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/default_markerview.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/default_markerview.png
new file mode 100644
index 0000000000..fe1c486518
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/default_markerview.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml
index 9799487f12..2616724865 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml
@@ -1,11 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <TextureView
- android:id="@+id/textureView"
+ <SurfaceView
+ android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
+ <FrameLayout
+ android:id="@+id/markerViewContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/transparent"/>
+
<com.mapbox.mapboxsdk.maps.widgets.CompassView
android:id="@+id/compassView"
android:layout_width="wrap_content"
@@ -23,15 +29,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
+ android:background="@drawable/bg_default_selector"
android:clickable="true"
android:contentDescription="@string/attributionsIconContentDescription"
android:padding="7dp"
- android:src="@drawable/ic_info_outline_24dp_selector"
- android:background="@drawable/bg_default_selector"/>
+ android:src="@drawable/ic_info_outline_24dp_selector" />
<com.mapbox.mapboxsdk.maps.widgets.MyLocationView
android:id="@+id/userLocationView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
</merge> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/view_image_marker.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/view_image_marker.xml
new file mode 100644
index 0000000000..7e4a079063
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/view_image_marker.xml
@@ -0,0 +1,5 @@
+<?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" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
index d6cca7090f..fd0f4b98e7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
@@ -9,6 +9,5 @@
<dimen name="ten_dp">10dp</dimen>
<dimen name="sixteen_dp">16dp</dimen>
<dimen name="seventy_six_dp">76dp</dimen>
- <dimen name="my_locationview_size">64dp</dimen>
<dimen name="my_locationview_outer_circle">18dp</dimen>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
index becbcce0b0..687b85b2d8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
@@ -11,11 +11,14 @@
<string name="infoWindowAddress">Address</string>
<!-- these are public -->
- <!-- {@deprecated Use Style.getXStyleUrl(int version) instead.} -->
+ <!-- Using one of these constants means your map style will always use the latest version and
+ may change as we improve the style. -->
<string name="style_mapbox_streets">mapbox://styles/mapbox/streets-v9</string>
+ <string name="style_outdoors">mapbox://styles/mapbox/outdoors-v9</string>
+ <!-- Note: Emerald style has been deprecated and will be removed in a future release-->
<string name="style_emerald">mapbox://styles/mapbox/emerald-v8</string>
<string name="style_light">mapbox://styles/mapbox/light-v9</string>
<string name="style_dark">mapbox://styles/mapbox/dark-v9</string>
<string name="style_satellite">mapbox://styles/mapbox/satellite-v9</string>
- <string name="style_satellite_streets">mapbox://styles/mapbox/satellite-hybrid-v9</string>
+ <string name="style_satellite_streets">mapbox://styles/mapbox/satellite-streets-v9</string>
</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 8c1dcc38ce..63cccc00ef 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=4.0.0
+fabric-version=4.1.1
fabric-build-type=binary
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
index 5c6265c725..93def53b5c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
@@ -90,13 +90,8 @@ dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
- // Directions SDK
- compile('com.mapbox.mapboxsdk:mapbox-android-directions:1.0.0@aar') {
- transitive = true
- }
-
- // Geocoder SDK
- compile('com.mapbox.mapboxsdk:mapbox-android-geocoder:1.0.0@aar') {
+ // Mapbox Android Services
+ compile('com.mapbox.mapboxsdk:mapbox-android-services:1.1.0@aar') {
transitive = true
}
@@ -107,7 +102,6 @@ dependencies {
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test:rules:0.4.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
- androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.5.4'
}
checkstyle {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BaseMapboxMapTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BaseMapboxMapTest.java
new file mode 100644
index 0000000000..1b1211b94c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BaseMapboxMapTest.java
@@ -0,0 +1,36 @@
+package com.mapbox.mapboxsdk.activity;
+
+import android.app.Activity;
+import android.support.test.espresso.Espresso;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.activity.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import org.junit.After;
+import org.junit.Before;
+
+public abstract class BaseMapboxMapTest extends BaseTest {
+
+ private OnMapReadyIdlingResource idlingResource;
+ protected MapboxMap mapboxMap;
+
+ @Before
+ public void registerIdlingResource() {
+ Log.e(MapboxConstants.TAG, "@Before test");
+ idlingResource = new OnMapReadyIdlingResource(getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ checkViewIsDisplayed(R.id.mapView);
+ mapboxMap = idlingResource.getMapboxMap();
+ }
+
+ public abstract Activity getActivity();
+
+ @After
+ public void unregisterIdlingResource() {
+ Log.e(MapboxConstants.TAG, "@After test");
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BaseTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BaseTest.java
index e9292332d5..ac2816cba4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BaseTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BaseTest.java
@@ -1,8 +1,8 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.app.Activity;
-import com.mapbox.mapboxsdk.testapp.espresso.utils.ScreenshotUtil;
+import com.mapbox.mapboxsdk.activity.utils.ScreenshotUtil;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BulkMarkerActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BulkMarkerActivityTest.java
index b85fe41243..8fc3d2b2ed 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/BulkMarkerActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/BulkMarkerActivityTest.java
@@ -1,11 +1,11 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.annotation.BulkMarkerActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.annotation.BulkMarkerActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DirectionsActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/DirectionsActivityTest.java
index a84345b14d..4c5b3e1f3d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DirectionsActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/DirectionsActivityTest.java
@@ -1,12 +1,12 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.app.Activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.directions.DirectionsActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.directions.DirectionsActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DynamicMarkerChangeActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/DynamicMarkerChangeActivityTest.java
index 700da5e3bc..07b1489f34 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/DynamicMarkerChangeActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/DynamicMarkerChangeActivityTest.java
@@ -1,12 +1,12 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.annotation.DynamicMarkerChangeActivity;
+import com.mapbox.mapboxsdk.activity.utils.ViewUtils;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.espresso.utils.ViewUtils;
+import com.mapbox.mapboxsdk.testapp.activity.annotation.DynamicMarkerChangeActivity;
import org.junit.Rule;
import org.junit.Test;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/GeocoderActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/GeocoderActivityTest.java
index 9bd7a49b9c..0197388543 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/GeocoderActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/GeocoderActivityTest.java
@@ -1,12 +1,12 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.app.Activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.geocoding.GeocoderActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.geocoding.GeocoderActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/InfoWindowActivityTest.java
index feb0bb1a02..df32ec9e9e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/InfoWindowActivityTest.java
@@ -1,11 +1,11 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.infowindow.InfoWindowActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.infowindow.InfoWindowActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowAdapterActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/InfoWindowAdapterActivityTest.java
index 0c62387e36..34dc06ed1b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/InfoWindowAdapterActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/InfoWindowAdapterActivityTest.java
@@ -1,11 +1,11 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.infowindow.InfoWindowAdapterActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.infowindow.InfoWindowAdapterActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/LatLngBoundsActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/LatLngBoundsActivityTest.java
index 39e7e4766e..09d164c975 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/LatLngBoundsActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/LatLngBoundsActivityTest.java
@@ -1,11 +1,11 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.camera.LatLngBoundsActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.camera.LatLngBoundsActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ManualZoomActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/ManualZoomActivityTest.java
index 5c88bd371a..3fa1390e9a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ManualZoomActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/ManualZoomActivityTest.java
@@ -1,12 +1,12 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.app.Activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.camera.ManualZoomActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.camera.ManualZoomActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapFragmentActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MapFragmentActivityTest.java
index d7522b2d7f..1847e2db02 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapFragmentActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MapFragmentActivityTest.java
@@ -1,11 +1,11 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.fragment.MapFragmentActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.fragment.MapFragmentActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapPaddingActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MapPaddingActivityTest.java
index fe6c4eb487..5edca393f4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MapPaddingActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MapPaddingActivityTest.java
@@ -1,12 +1,12 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.app.Activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.maplayout.MapPaddingActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.maplayout.MapPaddingActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MaxMinZoomActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MaxMinZoomActivityTest.java
index 48152286e9..901d2b0b2a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MaxMinZoomActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MaxMinZoomActivityTest.java
@@ -1,12 +1,12 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.app.Activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.camera.MaxMinZoomActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.camera.MaxMinZoomActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MyLocationTrackingModeActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MyLocationTrackingModeActivityTest.java
index ceda7cff2c..8a4b7d856d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/MyLocationTrackingModeActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/MyLocationTrackingModeActivityTest.java
@@ -1,11 +1,11 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.userlocation.MyLocationTrackingModeActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.userlocation.MyLocationTrackingModeActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/PolylineActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/PolylineActivityTest.java
index 425d803952..2172d226d1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/PolylineActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/PolylineActivityTest.java
@@ -1,11 +1,11 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
-import com.mapbox.mapboxsdk.testapp.activity.annotation.PolylineActivity;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.annotation.PolylineActivity;
import org.junit.Before;
import org.junit.Rule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScrollByActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/ScrollByActivityTest.java
index c545bc6e04..f337f38c79 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScrollByActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/ScrollByActivityTest.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/SupportMapFragmentActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/SupportMapFragmentActivityTest.java
index e6c1cadf1e..bc32cd8d85 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/SupportMapFragmentActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/SupportMapFragmentActivityTest.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
+package com.mapbox.mapboxsdk.activity;
import android.app.Activity;
import android.support.test.rule.ActivityTestRule;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/DrawerUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/DrawerUtils.java
index 7143fa948f..c1ee47d05e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/DrawerUtils.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/DrawerUtils.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.testapp.espresso.utils;
+package com.mapbox.mapboxsdk.activity.utils;
import android.support.annotation.StringRes;
import android.support.test.espresso.Espresso;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/GestureUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/GestureUtils.java
index e2807af5b5..da26e938b2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/GestureUtils.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/GestureUtils.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.testapp.espresso.utils;
+package com.mapbox.mapboxsdk.activity.utils;
import android.support.annotation.IdRes;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/OnMapReadyIdlingResource.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/OnMapReadyIdlingResource.java
new file mode 100644
index 0000000000..95db88d96b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/OnMapReadyIdlingResource.java
@@ -0,0 +1,57 @@
+package com.mapbox.mapboxsdk.activity.utils;
+
+import android.app.Activity;
+import android.support.test.espresso.IdlingResource;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+
+import java.lang.reflect.Field;
+
+public class OnMapReadyIdlingResource implements IdlingResource {
+
+ private final Activity activity;
+ private MapboxMap mapboxMap;
+ private IdlingResource.ResourceCallback resourceCallback;
+
+ public OnMapReadyIdlingResource(Activity activity) {
+ this.activity = activity;
+ }
+
+ @Override
+ public String getName() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public boolean isIdleNow() {
+ boolean idle = isMapboxMapReady();
+ if (idle && resourceCallback != null) {
+ resourceCallback.onTransitionToIdle();
+ }
+ return idle;
+ }
+
+ @Override
+ public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
+ this.resourceCallback = resourceCallback;
+ }
+
+ private boolean isMapboxMapReady() {
+ try {
+ Field field = activity.getClass().getDeclaredField("mapboxMap");
+ field.setAccessible(true);
+ mapboxMap = (MapboxMap) field.get(activity);
+ Log.e(MapboxConstants.TAG, "isMapboxReady called with value " + (mapboxMap != null));
+ return mapboxMap != null;
+ } catch (Exception e) {
+ Log.e(MapboxConstants.TAG, "could not reflect", e);
+ return false;
+ }
+ }
+
+ public MapboxMap getMapboxMap() {
+ return mapboxMap;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ScreenshotUtil.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/ScreenshotUtil.java
index fdb06e52d8..f28780b9e2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ScreenshotUtil.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/ScreenshotUtil.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.testapp.espresso.utils;
+package com.mapbox.mapboxsdk.activity.utils;
import android.app.Activity;
import android.graphics.Bitmap;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ViewUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/ViewUtils.java
index 6611c83545..9ba02f8e6a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/utils/ViewUtils.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/utils/ViewUtils.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.testapp.espresso.utils;
+package com.mapbox.mapboxsdk.activity.utils;
import android.support.annotation.IdRes;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/RotateTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/RotateTest.java
new file mode 100644
index 0000000000..948ed99570
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/RotateTest.java
@@ -0,0 +1,67 @@
+package com.mapbox.mapboxsdk.camera;
+
+import android.app.Activity;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.rule.ActivityTestRule;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.activity.BaseMapboxMapTest;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMapUtils;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.infowindow.InfoWindowActivity;
+
+import org.hamcrest.Matcher;
+import org.junit.Rule;
+import org.junit.Test;
+
+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;
+
+public class RotateTest extends BaseMapboxMapTest {
+
+ @Rule
+ public final ActivityTestRule<InfoWindowActivity> rule = new ActivityTestRule<>(InfoWindowActivity.class);
+
+ @Test
+ public void testRotate() {
+ onView(withId(R.id.mapView)).perform(new RotateAction(0, 180));
+ }
+
+ @Override
+ public Activity getActivity() {
+ return rule.getActivity();
+ }
+
+ private class RotateAction implements ViewAction {
+
+ private float startDegree;
+ private float endDegree;
+
+ public RotateAction(float startDegree, float endDegree) {
+ this.startDegree = startDegree;
+ this.endDegree = endDegree;
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayed();
+ }
+
+ @Override
+ public String getDescription() {
+ return "rotateAction";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadForAtLeast(500);
+ for (float i = startDegree; i < endDegree; i++) {
+ MapboxMapUtils.setDirection((MapView) view, i);
+ uiController.loopMainThreadForAtLeast(1);
+ }
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/TiltTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/TiltTest.java
new file mode 100644
index 0000000000..28061ae1ff
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/TiltTest.java
@@ -0,0 +1,67 @@
+package com.mapbox.mapboxsdk.camera;
+
+import android.app.Activity;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.rule.ActivityTestRule;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.activity.BaseMapboxMapTest;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMapUtils;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.annotation.DynamicMarkerChangeActivity;
+
+import org.hamcrest.Matcher;
+import org.junit.Rule;
+import org.junit.Test;
+
+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;
+
+public class TiltTest extends BaseMapboxMapTest {
+
+ @Rule
+ public final ActivityTestRule<DynamicMarkerChangeActivity> rule = new ActivityTestRule<>(DynamicMarkerChangeActivity.class);
+
+ @Test
+ public void testTilt() {
+ onView(withId(R.id.mapView)).perform(new TiltAction(0, 60));
+ }
+
+ @Override
+ public Activity getActivity() {
+ return rule.getActivity();
+ }
+
+ private class TiltAction implements ViewAction {
+
+ private float startDegree;
+ private float endDegree;
+
+ public TiltAction(float startDegree, float endDegree) {
+ this.startDegree = startDegree;
+ this.endDegree = endDegree;
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayed();
+ }
+
+ @Override
+ public String getDescription() {
+ return "tiltAction";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadForAtLeast(500);
+ for (float i = startDegree; i < endDegree; i++) {
+ MapboxMapUtils.setTilt((MapView) view, i);
+ uiController.loopMainThreadForAtLeast(1);
+ }
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/ZoomTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/ZoomTest.java
new file mode 100644
index 0000000000..f84bcaa2a0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/ZoomTest.java
@@ -0,0 +1,74 @@
+package com.mapbox.mapboxsdk.camera;
+
+import android.app.Activity;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.rule.ActivityTestRule;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.activity.BaseMapboxMapTest;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.maplayout.DebugModeActivity;
+
+import org.hamcrest.Matcher;
+import org.junit.Rule;
+import org.junit.Test;
+
+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;
+
+public class ZoomTest extends BaseMapboxMapTest {
+
+ @Rule
+ public final ActivityTestRule<DebugModeActivity> rule = new ActivityTestRule<>(DebugModeActivity.class);
+
+ @Test
+ public void testZoom() throws Exception {
+ onView(withId(R.id.mapView)).perform(new ZoomAction(MapboxConstants.MINIMUM_ZOOM, MapboxConstants.MAXIMUM_ZOOM));
+ }
+
+ @Override
+ public Activity getActivity() {
+ return rule.getActivity();
+ }
+
+ private class ZoomAction implements ViewAction {
+
+ private float fromZoom;
+ private float toZoom;
+
+ public ZoomAction(float fromZoom, float toZoom) {
+ this.fromZoom = fromZoom;
+ this.toZoom = toZoom;
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayed();
+ }
+
+ @Override
+ public String getDescription() {
+ return "zoomAction";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ // move camera above denver
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(new LatLng(39.749750, -104.949559))
+ .build()));
+
+ uiController.loopMainThreadForAtLeast(500);
+
+ for (float i = fromZoom; i < toZoom; i++) {
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(i));
+ uiController.loopMainThreadForAtLeast(200);
+ }
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapUtils.java
index d41c692509..4c793a7db1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapUtils.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapUtils.java
@@ -5,24 +5,11 @@ package com.mapbox.mapboxsdk.maps;
*/
public class MapboxMapUtils {
- /**
- * Get the MapboxMap instance linked to passed MapView
- *
- * @param mapView
- * @return
- */
- public static MapboxMap getMapboxMap(MapView mapView) {
- return mapView.getMapboxMap();
+ public static void setDirection(MapView mapView, float direction) {
+ mapView.setBearing(direction);
}
- /**
- * Set the direction of the user
- *
- * @param mapView
- * @param direction
- */
- public static void setDirection(MapView mapView, double direction) {
- mapView.setDirection(direction);
+ public static void setTilt(MapView mapView, float tilt) {
+ mapView.setTilt((double) tilt);
}
-
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/BackgroundLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/BackgroundLayerTest.java
new file mode 100644
index 0000000000..009e07b00e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/BackgroundLayerTest.java
@@ -0,0 +1,142 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.activity.BaseTest;
+import com.mapbox.mapboxsdk.activity.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.BackgroundLayer;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundPattern;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Basic smoke tests for BackgroundLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class BackgroundLayerTest extends BaseTest {
+ private static final String TAG = BackgroundLayerTest.class.getSimpleName();
+
+ @Rule
+ public final ActivityTestRule<RuntimeStyleTestActivity> rule = new ActivityTestRule<>(RuntimeStyleTestActivity.class);
+
+ private BackgroundLayer layer;
+
+ private OnMapReadyIdlingResource idlingResource;
+
+ private MapboxMap mapboxMap;
+
+ @Before
+ public void setup() {
+ idlingResource = new OnMapReadyIdlingResource(rule.getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ }
+
+ @Test
+ public void testSetVisibility() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ Log.i(TAG, "Retrieving layer");
+ layer = mapboxMap.getLayerAs("background");
+ Log.i(TAG, "visibility");
+ assertNotNull(layer);
+
+ //Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ //Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+
+ @Test
+ public void testBackgroundColor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ Log.i(TAG, "Retrieving layer");
+ layer = mapboxMap.getLayerAs("background");
+ Log.i(TAG, "background-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(backgroundColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getBackgroundColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testBackgroundColorAsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ Log.i(TAG, "Retrieving layer");
+ layer = mapboxMap.getLayerAs("background");
+ Log.i(TAG, "background-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(backgroundColor(Color.RED));
+ assertEquals(layer.getBackgroundColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testBackgroundPattern() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ Log.i(TAG, "Retrieving layer");
+ layer = mapboxMap.getLayerAs("background");
+ Log.i(TAG, "background-pattern");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(backgroundPattern("pedestrian-polygon"));
+ assertEquals((String) layer.getBackgroundPattern().getValue(), (String) "pedestrian-polygon");
+ }
+
+ @Test
+ public void testBackgroundOpacity() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ Log.i(TAG, "Retrieving layer");
+ layer = mapboxMap.getLayerAs("background");
+ Log.i(TAG, "background-opacity");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(backgroundOpacity(0.3f));
+ assertEquals((Float) layer.getBackgroundOpacity().getValue(), (Float) 0.3f);
+ }
+
+
+ @After
+ public void unregisterIntentServiceIdlingResource() {
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/CircleLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/CircleLayerTest.java
new file mode 100644
index 0000000000..51d0cd94b1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/CircleLayerTest.java
@@ -0,0 +1,255 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.CircleLayer;
+import com.mapbox.mapboxsdk.activity.BaseTest;
+import com.mapbox.mapboxsdk.activity.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+/**
+ * Basic smoke tests for CircleLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class CircleLayerTest extends BaseTest {
+ private static final String TAG = CircleLayerTest.class.getSimpleName();
+
+ @Rule
+ public final ActivityTestRule<RuntimeStyleTestActivity> rule = new ActivityTestRule<>(RuntimeStyleTestActivity.class);
+
+ private CircleLayer layer;
+
+ private OnMapReadyIdlingResource idlingResource;
+
+ private MapboxMap mapboxMap;
+
+ @Before
+ public void setup() {
+ idlingResource = new OnMapReadyIdlingResource(rule.getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ }
+
+ @Test
+ public void testSetVisibility() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "visibility");
+ assertNotNull(layer);
+
+ //Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ //Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+
+ @Test
+ public void testCircleRadius() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "circle-radius");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(circleRadius(0.3f));
+ assertEquals((Float) layer.getCircleRadius().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testCircleColor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "circle-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(circleColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getCircleColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testCircleColorAsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "circle-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(circleColor(Color.RED));
+ assertEquals(layer.getCircleColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testCircleBlur() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "circle-blur");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(circleBlur(0.3f));
+ assertEquals((Float) layer.getCircleBlur().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testCircleOpacity() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "circle-opacity");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(circleOpacity(0.3f));
+ assertEquals((Float) layer.getCircleOpacity().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testCircleTranslate() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "circle-translate");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(circleTranslate(new Float[]{0f,0f}));
+ assertEquals((Float[]) layer.getCircleTranslate().getValue(), (Float[]) new Float[]{0f,0f});
+ }
+
+ @Test
+ public void testCircleTranslateAnchor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "circle-translate-anchor");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(circleTranslateAnchor(CIRCLE_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getCircleTranslateAnchor().getValue(), (String) CIRCLE_TRANSLATE_ANCHOR_MAP);
+ }
+
+ @Test
+ public void testCirclePitchScale() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "circle-pitch-scale");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(circlePitchScale(CIRCLE_PITCH_SCALE_MAP));
+ assertEquals((String) layer.getCirclePitchScale().getValue(), (String) CIRCLE_PITCH_SCALE_MAP);
+ }
+
+
+ @After
+ public void unregisterIntentServiceIdlingResource() {
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/FillLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/FillLayerTest.java
new file mode 100644
index 0000000000..0773e28ae9
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/FillLayerTest.java
@@ -0,0 +1,287 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.activity.BaseTest;
+import com.mapbox.mapboxsdk.activity.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.FillLayer;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.layers.Property.FILL_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillAntialias;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOutlineColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillPattern;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Basic smoke tests for FillLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class FillLayerTest extends BaseTest {
+ private static final String TAG = FillLayerTest.class.getSimpleName();
+
+ @Rule
+ public final ActivityTestRule<RuntimeStyleTestActivity> rule = new ActivityTestRule<>(RuntimeStyleTestActivity.class);
+
+ private FillLayer layer;
+
+ private OnMapReadyIdlingResource idlingResource;
+
+ private MapboxMap mapboxMap;
+
+ @Before
+ public void setup() {
+ idlingResource = new OnMapReadyIdlingResource(rule.getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ }
+
+ @Test
+ public void testSetVisibility() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "visibility");
+ assertNotNull(layer);
+
+ //Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ //Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+
+ @Test
+ public void testFillAntialias() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "fill-antialias");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(fillAntialias(true));
+ assertEquals((Boolean) layer.getFillAntialias().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testFillOpacity() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "fill-opacity");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(fillOpacity(0.3f));
+ assertEquals((Float) layer.getFillOpacity().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testFillColor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "fill-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(fillColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getFillColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testFillColorAsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "fill-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(fillColor(Color.RED));
+ assertEquals(layer.getFillColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testFillOutlineColor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "fill-outline-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(fillOutlineColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getFillOutlineColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testFillOutlineColorAsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "fill-outline-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(fillOutlineColor(Color.RED));
+ assertEquals(layer.getFillOutlineColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testFillTranslate() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "fill-translate");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(fillTranslate(new Float[]{0f, 0f}));
+ assertEquals((Float[]) layer.getFillTranslate().getValue(), (Float[]) new Float[]{0f, 0f});
+ }
+
+ @Test
+ public void testFillTranslateAnchor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "fill-translate-anchor");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getFillTranslateAnchor().getValue(), (String) FILL_TRANSLATE_ANCHOR_MAP);
+ }
+
+ @Test
+ public void testFillPattern() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "fill-pattern");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(fillPattern("pedestrian-polygon"));
+ assertEquals((String) layer.getFillPattern().getValue(), (String) "pedestrian-polygon");
+ }
+
+
+ @After
+ public void unregisterIntentServiceIdlingResource() {
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/LineLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/LineLayerTest.java
new file mode 100644
index 0000000000..5fee78bc63
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/LineLayerTest.java
@@ -0,0 +1,428 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.activity.BaseTest;
+import com.mapbox.mapboxsdk.activity.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.LineLayer;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.layers.Property.LINE_CAP_BUTT;
+import static com.mapbox.mapboxsdk.style.layers.Property.LINE_JOIN_BEVEL;
+import static com.mapbox.mapboxsdk.style.layers.Property.LINE_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineBlur;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineCap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineDasharray;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineGapWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineJoin;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineMiterLimit;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineOffset;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.linePattern;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineRoundLimit;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Basic smoke tests for LineLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class LineLayerTest extends BaseTest {
+ private static final String TAG = LineLayerTest.class.getSimpleName();
+
+ @Rule
+ public final ActivityTestRule<RuntimeStyleTestActivity> rule = new ActivityTestRule<>(RuntimeStyleTestActivity.class);
+
+ private LineLayer layer;
+
+ private OnMapReadyIdlingResource idlingResource;
+
+ private MapboxMap mapboxMap;
+
+ @Before
+ public void setup() {
+ idlingResource = new OnMapReadyIdlingResource(rule.getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ }
+
+ @Test
+ public void testSetVisibility() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "visibility");
+ assertNotNull(layer);
+
+ //Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ //Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+
+ @Test
+ public void testLineCap() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-cap");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineCap(LINE_CAP_BUTT));
+ assertEquals((String) layer.getLineCap().getValue(), (String) LINE_CAP_BUTT);
+ }
+
+ @Test
+ public void testLineJoin() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-join");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineJoin(LINE_JOIN_BEVEL));
+ assertEquals((String) layer.getLineJoin().getValue(), (String) LINE_JOIN_BEVEL);
+ }
+
+ @Test
+ public void testLineMiterLimit() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-miter-limit");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineMiterLimit(0.3f));
+ assertEquals((Float) layer.getLineMiterLimit().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testLineRoundLimit() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-round-limit");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineRoundLimit(0.3f));
+ assertEquals((Float) layer.getLineRoundLimit().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testLineOpacity() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-opacity");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineOpacity(0.3f));
+ assertEquals((Float) layer.getLineOpacity().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testLineColor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getLineColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testLineColorAsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineColor(Color.RED));
+ assertEquals(layer.getLineColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testLineTranslate() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-translate");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineTranslate(new Float[]{0f, 0f}));
+ assertEquals((Float[]) layer.getLineTranslate().getValue(), (Float[]) new Float[]{0f, 0f});
+ }
+
+ @Test
+ public void testLineTranslateAnchor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-translate-anchor");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineTranslateAnchor(LINE_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getLineTranslateAnchor().getValue(), (String) LINE_TRANSLATE_ANCHOR_MAP);
+ }
+
+ @Test
+ public void testLineWidth() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-width");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineWidth(0.3f));
+ assertEquals((Float) layer.getLineWidth().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testLineGapWidth() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-gap-width");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineGapWidth(0.3f));
+ assertEquals((Float) layer.getLineGapWidth().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testLineOffset() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-offset");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineOffset(0.3f));
+ assertEquals((Float) layer.getLineOffset().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testLineBlur() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-blur");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineBlur(0.3f));
+ assertEquals((Float) layer.getLineBlur().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testLineDasharray() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-dasharray");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(lineDasharray(new Float[]{}));
+ assertEquals((Float[]) layer.getLineDasharray().getValue(), (Float[]) new Float[]{});
+ }
+
+ @Test
+ public void testLinePattern() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "line-pattern");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(linePattern("pedestrian-polygon"));
+ assertEquals((String) layer.getLinePattern().getValue(), (String) "pedestrian-polygon");
+ }
+
+
+ @After
+ public void unregisterIntentServiceIdlingResource() {
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RasterLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RasterLayerTest.java
new file mode 100644
index 0000000000..a40127148b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RasterLayerTest.java
@@ -0,0 +1,242 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.activity.BaseTest;
+import com.mapbox.mapboxsdk.activity.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.RasterLayer;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterBrightnessMax;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterBrightnessMin;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterContrast;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterFadeDuration;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterHueRotate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterSaturation;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Basic smoke tests for RasterLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class RasterLayerTest extends BaseTest {
+ private static final String TAG = RasterLayerTest.class.getSimpleName();
+
+ @Rule
+ public final ActivityTestRule<RuntimeStyleTestActivity> rule = new ActivityTestRule<>(RuntimeStyleTestActivity.class);
+
+ private RasterLayer layer;
+
+ private OnMapReadyIdlingResource idlingResource;
+
+ private MapboxMap mapboxMap;
+
+ @Before
+ public void setup() {
+ idlingResource = new OnMapReadyIdlingResource(rule.getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ }
+
+ @Test
+ public void testSetVisibility() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new RasterLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "visibility");
+ assertNotNull(layer);
+
+ //Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ //Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+
+ @Test
+ public void testRasterOpacity() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new RasterLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "raster-opacity");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(rasterOpacity(0.3f));
+ assertEquals((Float) layer.getRasterOpacity().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testRasterHueRotate() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new RasterLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "raster-hue-rotate");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(rasterHueRotate(0.3f));
+ assertEquals((Float) layer.getRasterHueRotate().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testRasterBrightnessMin() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new RasterLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "raster-brightness-min");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(rasterBrightnessMin(0.3f));
+ assertEquals((Float) layer.getRasterBrightnessMin().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testRasterBrightnessMax() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new RasterLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "raster-brightness-max");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(rasterBrightnessMax(0.3f));
+ assertEquals((Float) layer.getRasterBrightnessMax().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testRasterSaturation() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new RasterLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "raster-saturation");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(rasterSaturation(0.3f));
+ assertEquals((Float) layer.getRasterSaturation().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testRasterContrast() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new RasterLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "raster-contrast");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(rasterContrast(0.3f));
+ assertEquals((Float) layer.getRasterContrast().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testRasterFadeDuration() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new RasterLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "raster-fade-duration");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(rasterFadeDuration(0.3f));
+ assertEquals((Float) layer.getRasterFadeDuration().getValue(), (Float) 0.3f);
+ }
+
+
+ @After
+ public void unregisterIntentServiceIdlingResource() {
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleBackgroundLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleBackgroundLayerTest.java
new file mode 100644
index 0000000000..70ebf78556
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleBackgroundLayerTest.java
@@ -0,0 +1,61 @@
+package com.mapbox.mapboxsdk.style;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.BackgroundLayer;
+import com.mapbox.mapboxsdk.style.layers.Property;
+import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Basic smoke tests for BackgroundLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class RuntimeStyleBackgroundLayerTest
+ extends ActivityInstrumentationTestCase2<RuntimeStyleTestActivity> {
+ private static final String TAG = RuntimeStyleBackgroundLayerTest.class.getSimpleName();
+
+ public RuntimeStyleBackgroundLayerTest() {
+ super(RuntimeStyleTestActivity.class);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+ }
+
+ @Test
+ public void testSetVisibility() {
+ getActivity().mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ Log.i(TAG, "visibility");
+ BackgroundLayer layer = mapboxMap.getLayerAs("background");
+ assertNotNull(layer);
+
+ //Get initial
+ assertEquals(layer.getVisibility().getValue(), Property.VISIBLE);
+
+ //Set
+ layer.setProperties(PropertyFactory.visibility(Property.NONE));
+ assertEquals(layer.getVisibility().getValue(), Property.NONE);
+ }
+ });
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleTests.java
new file mode 100644
index 0000000000..a99a84ea9f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleTests.java
@@ -0,0 +1,91 @@
+package com.mapbox.mapboxsdk.style;
+
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.FillLayer;
+import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
+import com.mapbox.mapboxsdk.style.layers.Property;
+import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
+import com.mapbox.mapboxsdk.style.sources.VectorSource;
+import com.mapbox.mapboxsdk.activity.BaseTest;
+import com.mapbox.mapboxsdk.activity.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Basic smoke tests for Layer and Source
+ */
+@RunWith(AndroidJUnit4.class)
+public class RuntimeStyleTests extends BaseTest {
+
+ @Rule
+ public final ActivityTestRule<RuntimeStyleTestActivity> rule = new ActivityTestRule<>(RuntimeStyleTestActivity.class);
+
+ private OnMapReadyIdlingResource idlingResource;
+
+ @Before
+ public void registerIdlingResource() {
+ idlingResource = new OnMapReadyIdlingResource(rule.getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ }
+
+ @Test
+ public void testGetAddRemoveLayer() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ MapboxMap mapboxMap = rule.getActivity().getMapboxMap();
+
+ //Get initial
+ assertNotNull(mapboxMap.getLayer("building"));
+
+ //Remove
+ try {
+ mapboxMap.removeLayer("building");
+ } catch (NoSuchLayerException e) {
+ assertFalse(true);
+ }
+ assertNull(mapboxMap.getLayer("building"));
+
+ //Add
+ FillLayer layer = new FillLayer("building", "composite");
+ layer.setSourceLayer("building");
+ mapboxMap.addLayer(layer);
+
+ assertNotNull(mapboxMap.getLayer("building"));
+
+ try {
+ layer.setProperties(PropertyFactory.visibility(Property.VISIBLE));
+ assertTrue("Never reached as the reference is invalid after adding", false);
+ } catch (Exception e) {
+ //Expected, reference is no longer valid
+ }
+ }
+
+ @Test
+ public void testAddRemoveSource() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ MapboxMap mapboxMap = rule.getActivity().getMapboxMap();
+ mapboxMap.addSource(new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2"));
+ mapboxMap.removeSource("my-source");
+ }
+
+ @After
+ public void unregisterIntentServiceIdlingResource() {
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleTimingTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleTimingTests.java
new file mode 100644
index 0000000000..fd166b7b33
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/RuntimeStyleTimingTests.java
@@ -0,0 +1,45 @@
+package com.mapbox.mapboxsdk.style;
+
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.mapbox.mapboxsdk.activity.BaseTest;
+import com.mapbox.mapboxsdk.activity.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTimingTestActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Basic smoke tests for adding Layer and Source as early as possible (in onCreate)
+ */
+@RunWith(AndroidJUnit4.class)
+public class RuntimeStyleTimingTests extends BaseTest {
+
+ @Rule
+ public final ActivityTestRule<RuntimeStyleTimingTestActivity> rule = new ActivityTestRule<>(RuntimeStyleTimingTestActivity.class);
+
+ private OnMapReadyIdlingResource idlingResource;
+
+ @Before
+ public void registerIdlingResource() {
+ idlingResource = new OnMapReadyIdlingResource(rule.getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ }
+
+ @Test
+ public void testGetAddRemoveLayer() {
+ checkViewIsDisplayed(R.id.mapView);
+ //We're good if it didn't crash
+ }
+
+ @After
+ public void unregisterIntentServiceIdlingResource() {
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/SymbolLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/SymbolLayerTest.java
new file mode 100644
index 0000000000..3b860866a5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/SymbolLayerTest.java
@@ -0,0 +1,1283 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.activity.BaseTest;
+import com.mapbox.mapboxsdk.activity.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ROTATION_ALIGNMENT_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_TEXT_FIT_NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.SYMBOL_PLACEMENT_POINT;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_ANCHOR_CENTER;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_JUSTIFY_LEFT;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_PITCH_ALIGNMENT_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_ROTATION_ALIGNMENT_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_TRANSFORM_NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconHaloBlur;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconHaloColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconHaloWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconKeepUpright;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOptional;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconPadding;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotationAlignment;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconTextFit;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconTextFitPadding;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.symbolAvoidEdges;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.symbolPlacement;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.symbolSpacing;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAllowOverlap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textFont;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textHaloBlur;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textHaloColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textHaloWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textIgnorePlacement;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textJustify;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textKeepUpright;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textLetterSpacing;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textLineHeight;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textMaxAngle;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textMaxWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textOffset;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textOptional;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textPadding;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textPitchAlignment;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textRotate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textRotationAlignment;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textTransform;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Basic smoke tests for SymbolLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class SymbolLayerTest extends BaseTest {
+ private static final String TAG = SymbolLayerTest.class.getSimpleName();
+
+ @Rule
+ public final ActivityTestRule<RuntimeStyleTestActivity> rule = new ActivityTestRule<>(RuntimeStyleTestActivity.class);
+
+ private SymbolLayer layer;
+
+ private OnMapReadyIdlingResource idlingResource;
+
+ private MapboxMap mapboxMap;
+
+ @Before
+ public void setup() {
+ idlingResource = new OnMapReadyIdlingResource(rule.getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ }
+
+ @Test
+ public void testSetVisibility() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "visibility");
+ assertNotNull(layer);
+
+ //Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ //Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+
+ @Test
+ public void testSymbolPlacement() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "symbol-placement");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(symbolPlacement(SYMBOL_PLACEMENT_POINT));
+ assertEquals((String) layer.getSymbolPlacement().getValue(), (String) SYMBOL_PLACEMENT_POINT);
+ }
+
+ @Test
+ public void testSymbolSpacing() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "symbol-spacing");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(symbolSpacing(0.3f));
+ assertEquals((Float) layer.getSymbolSpacing().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testSymbolAvoidEdges() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "symbol-avoid-edges");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(symbolAvoidEdges(true));
+ assertEquals((Boolean) layer.getSymbolAvoidEdges().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testIconAllowOverlap() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-allow-overlap");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconAllowOverlap(true));
+ assertEquals((Boolean) layer.getIconAllowOverlap().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testIconIgnorePlacement() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-ignore-placement");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconIgnorePlacement(true));
+ assertEquals((Boolean) layer.getIconIgnorePlacement().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testIconOptional() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-optional");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconOptional(true));
+ assertEquals((Boolean) layer.getIconOptional().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testIconRotationAlignment() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-rotation-alignment");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP));
+ assertEquals((String) layer.getIconRotationAlignment().getValue(), (String) ICON_ROTATION_ALIGNMENT_MAP);
+ }
+
+ @Test
+ public void testIconSize() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-size");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconSize(0.3f));
+ assertEquals((Float) layer.getIconSize().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testIconTextFit() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-text-fit");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconTextFit(ICON_TEXT_FIT_NONE));
+ assertEquals((String) layer.getIconTextFit().getValue(), (String) ICON_TEXT_FIT_NONE);
+ }
+
+ @Test
+ public void testIconTextFitPadding() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-text-fit-padding");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconTextFitPadding(new Float[]{0f, 0f, 0f, 0f}));
+ assertEquals((Float[]) layer.getIconTextFitPadding().getValue(), (Float[]) new Float[]{0f, 0f, 0f, 0f});
+ }
+
+ @Test
+ public void testIconImage() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-image");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconImage("undefined"));
+ assertEquals((String) layer.getIconImage().getValue(), (String) "undefined");
+ }
+
+ @Test
+ public void testIconRotate() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-rotate");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconRotate(0.3f));
+ assertEquals((Float) layer.getIconRotate().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testIconPadding() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-padding");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconPadding(0.3f));
+ assertEquals((Float) layer.getIconPadding().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testIconKeepUpright() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-keep-upright");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconKeepUpright(true));
+ assertEquals((Boolean) layer.getIconKeepUpright().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testIconOffset() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-offset");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconOffset(new Float[]{0f, 0f}));
+ assertEquals((Float[]) layer.getIconOffset().getValue(), (Float[]) new Float[]{0f, 0f});
+ }
+
+ @Test
+ public void testTextPitchAlignment() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-pitch-alignment");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textPitchAlignment(TEXT_PITCH_ALIGNMENT_MAP));
+ assertEquals((String) layer.getTextPitchAlignment().getValue(), (String) TEXT_PITCH_ALIGNMENT_MAP);
+ }
+
+ @Test
+ public void testTextRotationAlignment() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-rotation-alignment");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textRotationAlignment(TEXT_ROTATION_ALIGNMENT_MAP));
+ assertEquals((String) layer.getTextRotationAlignment().getValue(), (String) TEXT_ROTATION_ALIGNMENT_MAP);
+ }
+
+ @Test
+ public void testTextField() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-field");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textField(""));
+ assertEquals((String) layer.getTextField().getValue(), (String) "");
+ }
+
+ @Test
+ public void testTextFont() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-font");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textFont(new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}));
+ assertEquals((String[]) layer.getTextFont().getValue(), (String[]) new String[]{"Open Sans Regular", "Arial Unicode MS Regular"});
+ }
+
+ @Test
+ public void testTextSize() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-size");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textSize(0.3f));
+ assertEquals((Float) layer.getTextSize().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextMaxWidth() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-max-width");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textMaxWidth(0.3f));
+ assertEquals((Float) layer.getTextMaxWidth().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextLineHeight() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-line-height");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textLineHeight(0.3f));
+ assertEquals((Float) layer.getTextLineHeight().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextLetterSpacing() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-letter-spacing");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textLetterSpacing(0.3f));
+ assertEquals((Float) layer.getTextLetterSpacing().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextJustify() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-justify");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textJustify(TEXT_JUSTIFY_LEFT));
+ assertEquals((String) layer.getTextJustify().getValue(), (String) TEXT_JUSTIFY_LEFT);
+ }
+
+ @Test
+ public void testTextAnchor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-anchor");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textAnchor(TEXT_ANCHOR_CENTER));
+ assertEquals((String) layer.getTextAnchor().getValue(), (String) TEXT_ANCHOR_CENTER);
+ }
+
+ @Test
+ public void testTextMaxAngle() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-max-angle");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textMaxAngle(0.3f));
+ assertEquals((Float) layer.getTextMaxAngle().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextRotate() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-rotate");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textRotate(0.3f));
+ assertEquals((Float) layer.getTextRotate().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextPadding() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-padding");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textPadding(0.3f));
+ assertEquals((Float) layer.getTextPadding().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextKeepUpright() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-keep-upright");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textKeepUpright(true));
+ assertEquals((Boolean) layer.getTextKeepUpright().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testTextTransform() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-transform");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textTransform(TEXT_TRANSFORM_NONE));
+ assertEquals((String) layer.getTextTransform().getValue(), (String) TEXT_TRANSFORM_NONE);
+ }
+
+ @Test
+ public void testTextOffset() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-offset");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textOffset(new Float[]{0f, 0f}));
+ assertEquals((Float[]) layer.getTextOffset().getValue(), (Float[]) new Float[]{0f, 0f});
+ }
+
+ @Test
+ public void testTextAllowOverlap() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-allow-overlap");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textAllowOverlap(true));
+ assertEquals((Boolean) layer.getTextAllowOverlap().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testTextIgnorePlacement() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-ignore-placement");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textIgnorePlacement(true));
+ assertEquals((Boolean) layer.getTextIgnorePlacement().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testTextOptional() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-optional");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textOptional(true));
+ assertEquals((Boolean) layer.getTextOptional().getValue(), (Boolean) true);
+ }
+
+ @Test
+ public void testIconOpacity() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-opacity");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconOpacity(0.3f));
+ assertEquals((Float) layer.getIconOpacity().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testIconColor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getIconColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testIconColorAsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconColor(Color.RED));
+ assertEquals(layer.getIconColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testIconHaloColor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-halo-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconHaloColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getIconHaloColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testIconHaloColorAsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-halo-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconHaloColor(Color.RED));
+ assertEquals(layer.getIconHaloColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testIconHaloWidth() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-halo-width");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconHaloWidth(0.3f));
+ assertEquals((Float) layer.getIconHaloWidth().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testIconHaloBlur() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-halo-blur");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconHaloBlur(0.3f));
+ assertEquals((Float) layer.getIconHaloBlur().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testIconTranslate() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-translate");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconTranslate(new Float[]{0f, 0f}));
+ assertEquals((Float[]) layer.getIconTranslate().getValue(), (Float[]) new Float[]{0f, 0f});
+ }
+
+ @Test
+ public void testIconTranslateAnchor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "icon-translate-anchor");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(iconTranslateAnchor(ICON_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getIconTranslateAnchor().getValue(), (String) ICON_TRANSLATE_ANCHOR_MAP);
+ }
+
+ @Test
+ public void testTextOpacity() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-opacity");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textOpacity(0.3f));
+ assertEquals((Float) layer.getTextOpacity().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextColor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getTextColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testTextColorAsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textColor(Color.RED));
+ assertEquals(layer.getTextColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testTextHaloColor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-halo-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textHaloColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getTextHaloColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testTextHaloColorAsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-halo-color");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textHaloColor(Color.RED));
+ assertEquals(layer.getTextHaloColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testTextHaloWidth() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-halo-width");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textHaloWidth(0.3f));
+ assertEquals((Float) layer.getTextHaloWidth().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextHaloBlur() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-halo-blur");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textHaloBlur(0.3f));
+ assertEquals((Float) layer.getTextHaloBlur().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testTextTranslate() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-translate");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textTranslate(new Float[]{0f, 0f}));
+ assertEquals((Float[]) layer.getTextTranslate().getValue(), (Float[]) new Float[]{0f, 0f});
+ }
+
+ @Test
+ public void testTextTranslateAnchor() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ Log.i(TAG, "text-translate-anchor");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(textTranslateAnchor(TEXT_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getTextTranslateAnchor().getValue(), (String) TEXT_TRANSLATE_ANCHOR_MAP);
+ }
+
+
+ @After
+ public void unregisterIntentServiceIdlingResource() {
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/layer.junit.ejs b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/layer.junit.ejs
new file mode 100644
index 0000000000..115a27832b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/layer.junit.ejs
@@ -0,0 +1,146 @@
+<%
+ const type = locals.type;
+ const properties = locals.properties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.<%- camelize(type) %>Layer;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+import com.mapbox.mapboxsdk.testapp.BaseTest;
+import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+/**
+ * Basic smoke tests for <%- camelize(type) %>Layer
+ */
+@RunWith(AndroidJUnit4.class)
+public class <%- camelize(type) %>LayerTest extends BaseTest {
+ private static final String TAG = <%- camelize(type) %>LayerTest.class.getSimpleName();
+
+ @Rule
+ public final ActivityTestRule<RuntimeStyleTestActivity> rule = new ActivityTestRule<>(RuntimeStyleTestActivity.class);
+
+ private <%- camelize(type) %>Layer layer;
+
+ private OnMapReadyIdlingResource idlingResource;
+
+ private MapboxMap mapboxMap;
+
+ @Before
+ public void setup() {
+ idlingResource = new OnMapReadyIdlingResource(rule.getActivity());
+ Espresso.registerIdlingResources(idlingResource);
+ }
+
+ @Test
+ public void testSetVisibility() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+<% if (type === 'background') { -%>
+ Log.i(TAG, "Retrieving layer");
+ layer = mapboxMap.getLayerAs("background");
+<% } else { -%>
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new <%- camelize(type) %>Layer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+<% } -%>
+ Log.i(TAG, "visibility");
+ assertNotNull(layer);
+
+ //Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ //Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+
+<% for (const property of properties) { -%>
+ @Test
+ public void test<%- camelize(property.name) %>() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+<% if (type === 'background') { -%>
+ Log.i(TAG, "Retrieving layer");
+ layer = mapboxMap.getLayerAs("background");
+<% } else { -%>
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new <%- camelize(type) %>Layer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+<% } -%>
+ Log.i(TAG, "<%- property.name %>");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>));
+ assertEquals((<%- propertyType(property) %>) layer.get<%- camelize(property.name) %>().getValue(), (<%- propertyType(property) %>) <%- defaultValueJava(property) %>);
+ }
+<% if (property.type == 'color') { -%>
+
+ @Test
+ public void test<%- camelize(property.name) %>AsInt() {
+ checkViewIsDisplayed(R.id.mapView);
+
+ mapboxMap = rule.getActivity().getMapboxMap();
+
+<% if (type === 'background') { -%>
+ Log.i(TAG, "Retrieving layer");
+ layer = mapboxMap.getLayerAs("background");
+<% } else { -%>
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Log.i(TAG, "Adding layer");
+ layer = new <%- camelize(type) %>Layer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ //Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+<% } -%>
+ Log.i(TAG, "<%- property.name %>");
+ assertNotNull(layer);
+
+ //Set and Get
+ layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(Color.RED));
+ assertEquals(layer.get<%- camelize(property.name) %>AsInt(), Color.RED);
+ }
+<% } -%>
+
+<% } -%>
+
+ @After
+ public void unregisterIntentServiceIdlingResource() {
+ Espresso.unregisterIdlingResources(idlingResource);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScreenshotActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScreenshotActivityTest.java
deleted file mode 100644
index 60649ad4ae..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/espresso/ScreenshotActivityTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.espresso;
-
-import android.app.Activity;
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.IdlingPolicies;
-import android.support.test.espresso.IdlingResource;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.text.format.DateUtils;
-
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.camera.LatLngBoundsActivity;
-import com.mapbox.mapboxsdk.testapp.espresso.utils.ScreenshotUtil;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.TimeUnit;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class
-ScreenshotActivityTest {
-
- @Rule
- public ActivityTestRule<LatLngBoundsActivity> mActivityRule = new ActivityTestRule<>(LatLngBoundsActivity.class);
-
- private Activity mActivity = null;
-
- @Before
- public void setActivity() {
- mActivity = mActivityRule.getActivity();
-
- // Make sure Espresso does not time out
- IdlingPolicies.setMasterPolicyTimeout(60, TimeUnit.SECONDS);
- IdlingPolicies.setIdlingResourceTimeout(26, TimeUnit.SECONDS);
- }
-
- @Test
- public void testScreenshot() {
- waitFor(DateUtils.SECOND_IN_MILLIS * 5, mActivity);
- }
-
- private static void waitFor(long waitingTime, Activity a) {
- // Start
- onView(withId(R.id.mapView)).perform(click());
-
- // Make sure Espresso does not time out
- IdlingPolicies.setMasterPolicyTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
- IdlingPolicies.setIdlingResourceTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
-
- // Now we wait
- IdlingResource idlingResource = new ElapsedTimeIdlingResource(waitingTime);
- Espresso.registerIdlingResources(idlingResource);
-
- // Stop and verify
- onView(withId(R.id.mapView)).perform(click());
-
- ScreenshotUtil.take(a, "testing");
-
- // Clean up
- Espresso.unregisterIdlingResources(idlingResource);
- }
-
-// @Test
-// public void takeScreenshot() {
-// // Start
-// checkViewIsDisplayed(R.id.mapView);
-//
-//
-// // Now we wait
-// IdlingResource idlingResource = new ElapsedTimeIdlingResource(4);
-// Espresso.registerIdlingResources(idlingResource);
-//
-// // We take a screenshot
-// ScreenshotUtil.take(mActivity, "test");
-//
-// // Clean up
-// Espresso.unregisterIdlingResources(idlingResource);
-// }
-
-// @Test
-// public void testSanity() {
-// checkViewIsDisplayed(R.id.mapView);
-// }
-
- public static class ElapsedTimeIdlingResource implements IdlingResource {
- private final long startTime;
- private final long waitingTime;
- private ResourceCallback resourceCallback;
-
- public ElapsedTimeIdlingResource(long waitingTime) {
- this.startTime = System.currentTimeMillis();
- this.waitingTime = waitingTime;
- }
-
- @Override
- public String getName() {
- return ElapsedTimeIdlingResource.class.getName() + ":" + waitingTime;
- }
-
- @Override
- public boolean isIdleNow() {
- long elapsed = System.currentTimeMillis() - startTime;
- boolean idle = (elapsed >= waitingTime);
- if (idle) {
- resourceCallback.onTransitionToIdle();
- }
- return idle;
- }
-
- @Override
- public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
- this.resourceCallback = resourceCallback;
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/BaseMainActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/BaseMainActivityTest.java
deleted file mode 100644
index 0eedca5458..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/BaseMainActivityTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.robotium;
-
-import android.test.ActivityInstrumentationTestCase2;
-
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.MapboxMapUtils;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.annotation.PolygonActivity;
-import com.mapbox.mapboxsdk.testapp.activity.annotation.PolylineActivity;
-import com.mapbox.mapboxsdk.testapp.activity.infowindow.InfoWindowActivity;
-import com.robotium.solo.Solo;
-
-import org.junit.Test;
-
-public abstract class BaseMainActivityTest{
-//
-//} extends ActivityInstrumentationTestCase2<InfoWindowActivity> {
-//
-// protected Solo solo;
-// protected MapView mapView;
-// protected MapboxMap mapboxMap;
-//
-// public BaseMainActivityTest() {
-// super(InfoWindowActivity.class);
-// }
-//
-// @Override
-// public void setUp() throws Exception {
-// solo = new Solo(getInstrumentation(), getActivity());
-// mapView = (MapView) solo.getView(R.id.mapView);
-// mapboxMap = MapboxMapUtils.getMapboxMap(mapView);
-// }
-//
-// @Override
-// public void tearDown() throws Exception {
-// solo.finishOpenedActivities();
-// }
-//
-// @Test
-// public void testSanity() throws Exception {
-// assertNotNull("Should be non null", solo);
-// assertNotNull("Should be non null", mapView);
-// assertNotNull("Should be non null", mapboxMap);
-// }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/CompassViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/CompassViewTest.java
deleted file mode 100644
index 9990d71d67..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/CompassViewTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.robotium;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.widgets.CompassView;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.robotium.utils.ViewAssertion;
-
-import org.junit.Test;
-
-public class CompassViewTest {
-// extends
-//} BaseMainActivityTest {
-//
-// private CompassView compassView;
-//
-// @Override
-// public void setUp() throws Exception {
-// super.setUp();
-// compassView = (CompassView) solo.getView(R.id.compassView);
-// }
-//
-//// @Test
-//// public void testCompass() {
-//// setCameraDirection(0);
-//// solo.sleep(200);
-//// ViewAssertion.isInvisible(compassView);
-//// }
-//
-// @Test
-// public void testCompassClick() throws Exception {
-// setCameraDirection(20);
-// solo.sleep(200);
-// ViewAssertion.isVisible(compassView);
-// solo.clickOnView(compassView);
-// solo.sleep(200);
-// ViewAssertion.isVisible(compassView);
-// }
-//
-// private void setCameraDirection(float bearing) {
-// mapboxMap.setCameraPosition(new CameraPosition.Builder()
-// .target(new LatLng(0, 0))
-// .tilt(0)
-// .zoom(15)
-// .bearing(bearing)
-// .build());
-// }
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleRotateTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleRotateTest.java
deleted file mode 100644
index 8d1feed6ff..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleRotateTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.robotium;
-
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMapUtils;
-
-import org.junit.Test;
-
-public class SimpleRotateTest {
-// extends
-//} BaseMainActivityTest {
-//
-// @Override
-// public void setUp() throws Exception {
-// super.setUp();
-// }
-//
-// @Test
-// public void testRotate() {
-// mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(47.376271, 8.539116), 15));
-// double rotation = 0;
-// for (int i = 0; i < 180; i++) {
-// rotation += 1;
-// MapboxMapUtils.setDirection(mapView, rotation);
-// solo.sleep(100);
-// }
-// }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleZoomTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleZoomTest.java
deleted file mode 100644
index bc8e9caac0..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/SimpleZoomTest.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.robotium;
-
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-
-import org.junit.Test;
-
-public class SimpleZoomTest {
-// extends
-//} BaseMainActivityTest {
-//
-// @Override
-// public void setUp() throws Exception {
-// super.setUp();
-// }
-//
-// @Test
-// public void testZoomIn() {
-// float currentZoomlevel = MapboxConstants.MINIMUM_ZOOM;
-// while (currentZoomlevel < MapboxConstants.MAXIMUM_ZOOM - 5) {
-// mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(47.376271, 8.539116),currentZoomlevel));
-// currentZoomlevel++;
-// solo.sleep(1400);
-// }
-// }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/utils/ViewAssertion.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/utils/ViewAssertion.java
deleted file mode 100644
index 74410cce50..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/robotium/utils/ViewAssertion.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.robotium.utils;
-
-import android.view.View;
-
-import static junit.framework.Assert.assertEquals;
-
-public class ViewAssertion {
-
- public static void isVisible(View view) {
- assertEquals(view.getClass().getSimpleName() + " should be visible", view.getVisibility(), View.VISIBLE);
- }
-
- public static void isInvisible(View view) {
- assertEquals(view.getClass().getSimpleName() + " should be visible", view.getVisibility(), View.INVISIBLE);
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 7268b7d551..cafca62135 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -6,13 +6,13 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".MapboxApplication"
android:allowBackup="true"
android:fullBackupContent="true"
- android:icon="@drawable/icon_burned"
+ android:icon="@drawable/icon"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
@@ -24,7 +24,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
-
<activity
android:name=".activity.infowindow.InfoWindowActivity"
android:description="@string/description_info_window"
@@ -42,6 +41,14 @@
android:value="@string/category_infowindow" />
</activity>
<activity
+ android:name=".activity.infowindow.DynamicInfoWindowAdapterActivity"
+ android:description="@string/description_dynamic_info_window_adapter"
+ android:label="@string/activity_dynamic_infowindow_adapter">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_infowindow" />
+ </activity>
+ <activity
android:name=".activity.annotation.BulkMarkerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:description="@string/description_add_bulk_markers"
@@ -66,9 +73,10 @@
android:name="@string/category"
android:value="@string/category_annotation" />
</activity>
- <activity android:name=".activity.annotation.PressForMarkerActivity"
- android:description="@string/description_press_for_marker"
- android:label="@string/activity_press_for_marker">
+ <activity
+ android:name=".activity.annotation.PressForMarkerActivity"
+ android:description="@string/description_press_for_marker"
+ android:label="@string/activity_press_for_marker">
<meta-data
android:name="@string/category"
android:value="@string/category_annotation" />
@@ -92,8 +100,8 @@
<activity
android:name=".activity.camera.LatLngBoundsActivity"
android:description="@string/description_visible_bounds"
- android:screenOrientation="portrait"
- android:label="@string/activity_visible_coordinate_bounds">
+ android:label="@string/activity_visible_coordinate_bounds"
+ android:screenOrientation="portrait">
<meta-data
android:name="@string/category"
android:value="@string/category_camera" />
@@ -178,7 +186,6 @@
android:name="@string/category"
android:value="@string/category_annotation" />
</activity>
-
<activity
android:name=".activity.annotation.PolylineActivity"
android:description="@string/description_polyline"
@@ -252,7 +259,6 @@
android:name="@string/category"
android:value="@string/category_maplayout" />
</activity>
-
<activity
android:name=".activity.annotation.MarkerViewActivity"
android:description="@string/description_view_marker"
@@ -261,6 +267,67 @@
android:name="@string/category"
android:value="@string/category_annotation" />
</activity>
+ <activity
+ android:name=".activity.annotation.MarkerViewScaleActivity"
+ android:description="@string/description_view_marker_scale"
+ android:label="@string/activity_view_marker_scale">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_annotation" />
+ </activity>
+ <activity
+ android:name=".activity.navigation.LocationPickerActivity"
+ android:description="@string/description_location_picker"
+ android:label="@string/activity_location_picker">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_navigation" />
+ </activity>
+ <activity
+ android:name=".activity.fragment.ViewPagerActivity"
+ android:description="@string/description_viewpager"
+ android:label="@string/activity_viewpager">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_fragment" />
+ </activity>
+ <activity
+ android:name=".activity.maplayout.NavigationDrawerActivity"
+ android:description="@string/description_navigation_drawer"
+ android:label="@string/title_activity_navigation_drawer"
+ android:theme="@style/AppTheme.ActionBar.Transparent">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_fragment" />
+ </activity>
+ <activity
+ android:name=".activity.style.RuntimeStyleActivity"
+ android:description="@string/description_runtime_style"
+ android:label="@string/activity_runtime_style">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_style" />
+ </activity>
+ <activity
+ android:name=".activity.imagegenerator.PrintActivity"
+ android:description="@string/description_print"
+ android:label="@string/activity_print">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_imagegenerator" />
+ </activity>
+ <activity
+ android:name=".activity.maplayout.SurfaceViewMediaControlActivity"
+ android:description="@string/description_surfaceview_mediacontrols"
+ android:label="@string/activity_surfaceview_overlay">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_maplayout" />
+ </activity>
+
+ <!-- For Unit tests -->
+ <activity android:name=".activity.style.RuntimeStyleTestActivity" />
+ <activity android:name=".activity.style.RuntimeStyleTimingTestActivity" />
<!-- Configuration Settings -->
<meta-data
@@ -270,8 +337,13 @@
android:name="com.mapbox.TestEventsAccessToken"
android:value="sk.eyJ1IjoiYmxlZWdlIiwiYSI6InNpcml1c2x5In0.KyT-boMyC_xZYTYojTc8zg" />
+ <!-- Comment out this setting to switch to external storage (and disable internal) in your app -->
+ <!-- <meta-data -->
+ <!-- android:name="com.mapbox.SetStorageExternal" -->
+ <!-- android:value="true" /> -->
+
<service android:name="com.mapbox.mapboxsdk.telemetry.TelemetryService" />
</application>
-</manifest>
+</manifest> \ No newline at end of file
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 9d514b2870..359aedf096 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
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.testapp.activity;
+import android.Manifest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -9,7 +10,11 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
@@ -61,10 +66,16 @@ public class FeatureOverviewActivity extends AppCompatActivity {
@Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
if (!sectionAdapter.isSectionHeaderPosition(position)) {
- Intent intent = new Intent();
int realPosition = sectionAdapter.getConvertedPosition(position);
- intent.setComponent(new ComponentName(getPackageName(), features.get(realPosition).getName()));
- startActivity(intent);
+ Feature feature = features.get(realPosition);
+ if (feature.getCategory().equals(getString(R.string.category_userlocation))) {
+ if ((ContextCompat.checkSelfPermission(FeatureOverviewActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) ||
+ (ContextCompat.checkSelfPermission(FeatureOverviewActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
+ ActivityCompat.requestPermissions(FeatureOverviewActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, realPosition);
+ return;
+ }
+ }
+ startFeature(feature);
}
}
});
@@ -104,6 +115,22 @@ public class FeatureOverviewActivity extends AppCompatActivity {
recyclerView.setAdapter(sectionAdapter);
}
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ startFeature(features.get(requestCode));
+ } else {
+ Snackbar.make(findViewById(android.R.id.content), "Can't open without the location permission.", Snackbar.LENGTH_SHORT).show();
+ }
+ }
+
+ private void startFeature(Feature feature) {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(getPackageName(), feature.getName()));
+ startActivity(intent);
+ }
+
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -120,7 +147,7 @@ public class FeatureOverviewActivity extends AppCompatActivity {
String packageName = getApplicationContext().getPackageName();
String metaDataKey = getString(R.string.category);
for (ActivityInfo info : app.activities) {
- if (info.name.startsWith(packageName) && !info.name.equals(FeatureOverviewActivity.class.getName())) {
+ if (info.labelRes != 0 && info.name.startsWith(packageName) && !info.name.equals(FeatureOverviewActivity.class.getName())) {
String label = getString(info.labelRes);
String description = resolveString(info.descriptionRes);
String category = resolveMetaData(info.metaData, metaDataKey);
@@ -149,10 +176,10 @@ public class FeatureOverviewActivity extends AppCompatActivity {
return category;
}
- private String resolveString(@StringRes int stringRes){
- try{
+ private String resolveString(@StringRes int stringRes) {
+ try {
return getString(stringRes);
- }catch (Resources.NotFoundException e){
+ } catch (Resources.NotFoundException e) {
return "-";
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
index 1e15c9ea36..705716355e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
@@ -1,27 +1,44 @@
package com.mapbox.mapboxsdk.testapp.activity.annotation;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.os.Bundle;
+import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
-import android.view.Menu;
import android.view.MenuItem;
import android.view.animation.AccelerateDecelerateInterpolator;
+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.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
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 java.util.Random;
+
public class AnimatedMarkerActivity extends AppCompatActivity {
private MapView mMapView;
+ private MapboxMap mMapboxMap;
+ private Random random = new Random();
+
+ private LatLng dupontCircle = new LatLng(38.90962, -77.04341);
+
+ private Marker passengerMarker = null;
+ private MarkerView carMarker = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -40,34 +57,121 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
mMapView = (MapView) findViewById(R.id.mapView);
mMapView.onCreate(savedInstanceState);
mMapView.getMapAsync(new OnMapReadyCallback() {
+
@Override
public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- LatLng brussels = new LatLng(50.900446, 4.485251);
- LatLng washington = new LatLng(38.897108, -77.036716);
-
- final Marker marker = mapboxMap.addMarker(new MarkerOptions().position(brussels));
- ValueAnimator markerAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), (Object[]) new LatLng[]{brussels, washington});
- markerAnimator.setDuration(5000);
- markerAnimator.setRepeatCount(ValueAnimator.INFINITE);
- markerAnimator.setRepeatMode(ValueAnimator.REVERSE);
- markerAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
- markerAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (marker != null) {
- marker.setPosition((LatLng) animation.getAnimatedValue());
- }
- }
- });
- markerAnimator.start();
+ mMapboxMap = mapboxMap;
+ setupMap();
+
+ for (int i = 0; i < 10; i++) {
+ addRandomCar();
+ }
+
+ addPassenger();
+ addMainCar();
+ animateMoveToPassenger(carMarker);
}
});
}
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_zoom, menu);
- return true;
+ private void setupMap() {
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .target(dupontCircle)
+ .zoom(15)
+ .build();
+ mMapboxMap.setCameraPosition(cameraPosition);
+ }
+
+ private void addPassenger() {
+ LatLng randomLatLng = getLatLngInBounds();
+
+ if (passengerMarker == null) {
+ Icon icon = IconFactory.getInstance(AnimatedMarkerActivity.this)
+ .fromResource(R.drawable.ic_directions_run_black_24dp);
+ passengerMarker = mMapboxMap.addMarker(new MarkerViewOptions()
+ .position(randomLatLng)
+ .icon(icon));
+ } else {
+ passengerMarker.setPosition(randomLatLng);
+ }
+ }
+
+ private void addMainCar() {
+ LatLng randomLatLng = getLatLngInBounds();
+
+ if (carMarker == null) {
+ carMarker = createCarMarker(randomLatLng, R.drawable.ic_taxi_top);
+ } else {
+ carMarker.setPosition(randomLatLng);
+ }
+
+ // Make sure the car marker is selected so that it's always brought to the front (#5285)
+ mMapboxMap.selectMarker(carMarker);
+ }
+
+ private void animateMoveToPassenger(final MarkerView car) {
+ ValueAnimator animator = animateMoveMarker(car, passengerMarker.getPosition());
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ addPassenger();
+ animateMoveToPassenger(car);
+ }
+ });
+ }
+
+ protected void addRandomCar() {
+ MarkerView car = createCarMarker(getLatLngInBounds(), R.drawable.ic_car_top);
+ randomlyMoveMarker(car);
+ }
+
+ private void randomlyMoveMarker(final MarkerView marker) {
+ ValueAnimator animator = animateMoveMarker(marker, getLatLngInBounds());
+
+ //Add listener to restart animation on end
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ randomlyMoveMarker(marker);
+ }
+ });
+ }
+
+ private ValueAnimator animateMoveMarker(final MarkerView marker, LatLng to) {
+ marker.setRotation((float) getBearing(marker.getPosition(), to));
+
+ final ValueAnimator markerAnimator = ObjectAnimator.ofObject(marker, "position", new LatLngEvaluator(), marker.getPosition(), to);
+ markerAnimator.setDuration((long) (10 * marker.getPosition().distanceTo(to)));
+ markerAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
+
+ // Start
+ markerAnimator.start();
+
+ return markerAnimator;
+ }
+
+ private MarkerView createCarMarker(LatLng start, @DrawableRes int carResource) {
+ Icon icon = IconFactory.getInstance(AnimatedMarkerActivity.this)
+ .fromResource(carResource);
+
+ //View Markers
+ return mMapboxMap.addMarker(new MarkerViewOptions()
+ .position(start)
+ .icon(icon));
+
+ //GL Markers
+// return mMapboxMap.addMarker(new MarkerOptions()
+// .position(start)
+// .icon(icon));
+
+ }
+
+ private LatLng getLatLngInBounds() {
+ LatLngBounds bounds = mMapboxMap.getProjection().getVisibleRegion().latLngBounds;
+ Random generator = new Random();
+ double randomLat = bounds.getLatSouth() + generator.nextDouble() * (bounds.getLatNorth() - bounds.getLatSouth());
+ double randomLon = bounds.getLonWest() + generator.nextDouble() * (bounds.getLonEast() - bounds.getLonWest());
+ return new LatLng(randomLat, randomLon);
}
@Override
@@ -111,15 +215,33 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
mMapView.onLowMemory();
}
- private class LatLngEvaluator implements TypeEvaluator<LatLng> {
+ /**
+ * Evaluator for LatLng pairs
+ */
+ private static class LatLngEvaluator implements TypeEvaluator<LatLng> {
private LatLng mLatLng = new LatLng();
@Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
- mLatLng.setLatitude(startValue.getLatitude() + (endValue.getLatitude() - startValue.getLatitude()) * fraction);
- mLatLng.setLongitude(startValue.getLongitude() + (endValue.getLongitude() - startValue.getLongitude()) * fraction);
+ mLatLng.setLatitude(startValue.getLatitude() + ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
+ mLatLng.setLongitude(startValue.getLongitude() + ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
return mLatLng;
}
}
+
+ private double getBearing(LatLng from, LatLng to) {
+ double degrees2radians = Math.PI / 180;
+ double radians2degrees = 180 / Math.PI;
+
+ double lon1 = degrees2radians * from.getLongitude();
+ double lon2 = degrees2radians * to.getLongitude();
+ double lat1 = degrees2radians * from.getLatitude();
+ double lat2 = degrees2radians * to.getLatitude();
+ double a = Math.sin(lon2 - lon1) * Math.cos(lat2);
+ double b = Math.cos(lat1) * Math.sin(lat2) -
+ Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
+
+ return radians2degrees * Math.atan2(a, b);
+ }
}
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 7a2241a84c..e4bf383c96 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
@@ -3,28 +3,28 @@ package com.mapbox.mapboxsdk.testapp.activity.annotation;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.ProgressDialog;
-import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
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.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
-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.Spinner;
import android.widget.TextView;
import android.widget.Toast;
+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.annotations.MarkerView;
import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
@@ -86,36 +86,6 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
}
}
- public static class TextAdapter extends MapboxMap.MarkerViewAdapter<MarkerView> {
-
- private LayoutInflater inflater;
-
- public TextAdapter(@NonNull Context context) {
- super(context);
- this.inflater = LayoutInflater.from(context);
- }
-
- @Nullable
- @Override
- public View getView(@NonNull MarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
- ViewHolder viewHolder;
- if (convertView == null) {
- viewHolder = new ViewHolder();
- convertView = inflater.inflate(R.layout.view_text_marker, parent, false);
- viewHolder.title = (TextView) convertView.findViewById(R.id.textView);
- convertView.setTag(viewHolder);
- } else {
- viewHolder = (ViewHolder) convertView.getTag();
- }
- viewHolder.title.setText(marker.getTitle());
- return convertView;
- }
-
- private static class ViewHolder {
- TextView title;
- }
- }
-
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
int amount = Integer.valueOf(getResources().getStringArray(R.array.bulk_marker_list)[position]);
@@ -126,7 +96,6 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
}
}
-
private void onLatLngListLoaded(List<LatLng> latLngs, int amount) {
mLocations = latLngs;
showMarkers(amount);
@@ -140,26 +109,32 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
}
if (mCustomMarkerView) {
- showNativeMarkers(amount);
+ showViewMarkers(amount);
} else {
showGlMarkers(amount);
}
}
- private void showNativeMarkers(int amount) {
+ private void showViewMarkers(int amount) {
DecimalFormat formatter = new DecimalFormat("#.#####");
Random random = new Random();
int randomIndex;
+ Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_droppin_24dp, getTheme());
+ int redColor = ResourcesCompat.getColor(getResources(), android.R.color.holo_red_dark, getTheme());
+ drawable.setColorFilter(redColor, PorterDuff.Mode.SRC_IN);
+ Icon icon = IconFactory.getInstance(this).fromDrawable(drawable);
+
for (int i = 0; i < amount; i++) {
randomIndex = random.nextInt(mLocations.size());
LatLng latLng = mLocations.get(randomIndex);
mMapboxMap.addMarker(new MarkerViewOptions()
.position(latLng)
+ .icon(icon)
.title(String.valueOf(i))
.snippet(formatter.format(latLng.getLatitude()) + ", " + formatter.format(latLng.getLongitude())));
}
- }
+ }
private void showGlMarkers(int amount) {
List<MarkerOptions> markerOptionsList = new ArrayList<>();
@@ -247,9 +222,6 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
showMarkers(amount);
}
- // add adapter
- mMapboxMap.getMarkerViewManager().addMarkerViewAdapter(new TextAdapter(BulkMarkerActivity.this));
-
mMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
@Override
public void onMapChanged(@MapView.MapChange int change) {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java
index fcddf8ca54..53909dc051 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java
@@ -13,8 +13,8 @@ import android.view.View;
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.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;
@@ -25,10 +25,10 @@ public class DynamicMarkerChangeActivity extends AppCompatActivity {
private static final LatLng LAT_LNG_CHELSEA = new LatLng(51.481670, -0.190849);
private static final LatLng LAT_LNG_ARSENAL = new LatLng(51.555062, -0.108417);
- private MapView mMapView;
- private MapboxMap mMapboxMap;
- private IconFactory mIconFactory;
- private Marker mMarker;
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private IconFactory iconFactory;
+ private Marker marker;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -44,32 +44,33 @@ public class DynamicMarkerChangeActivity extends AppCompatActivity {
actionBar.setDisplayShowHomeEnabled(true);
}
- mIconFactory = IconFactory.getInstance(this);
+ iconFactory = IconFactory.getInstance(this);
- mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setTag(false);
- mMapView.onCreate(savedInstanceState);
- mMapView.getMapAsync(new OnMapReadyCallback() {
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.setTag(false);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
+ DynamicMarkerChangeActivity.this.mapboxMap = mapboxMap;
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.506675, -0.128699), 10));
// Create marker
MarkerOptions markerOptions = new MarkerOptions()
.position(LAT_LNG_CHELSEA)
- .icon(mIconFactory.fromResource(R.drawable.ic_chelsea));
- mMarker = mapboxMap.addMarker(markerOptions);
+ .icon(iconFactory.fromResource(R.drawable.ic_chelsea))
+ .title(getString(R.string.dynamic_marker_chelsea_title))
+ .snippet(getString(R.string.dynamic_marker_chelsea_snippet));
+ marker = mapboxMap.addMarker(markerOptions);
}
});
-
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(this, R.color.primary));
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (mMapboxMap != null) {
+ if (mapboxMap != null) {
updateMarker();
}
}
@@ -78,42 +79,44 @@ public class DynamicMarkerChangeActivity extends AppCompatActivity {
private void updateMarker() {
// update model
- boolean first = (boolean) mMapView.getTag();
- mMapView.setTag(!first);
+ boolean first = (boolean) mapView.getTag();
+ mapView.setTag(!first);
// update marker
- mMarker.setPosition(first ? LAT_LNG_CHELSEA : LAT_LNG_ARSENAL);
- mMarker.setIcon(mIconFactory.fromResource(first ? R.drawable.ic_chelsea : R.drawable.ic_arsenal));
+ marker.setPosition(first ? LAT_LNG_CHELSEA : LAT_LNG_ARSENAL);
+ marker.setIcon(iconFactory.fromResource(first ? R.drawable.ic_chelsea : R.drawable.ic_arsenal));
+ marker.setTitle(first ? getString(R.string.dynamic_marker_chelsea_title) : getString(R.string.dynamic_marker_arsenal_title));
+ marker.setSnippet(first ? getString(R.string.dynamic_marker_chelsea_snippet) : getString(R.string.dynamic_marker_arsenal_snippet));
}
@Override
public void onResume() {
super.onResume();
- mMapView.onResume();
+ mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
- mMapView.onPause();
+ mapView.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- mMapView.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
}
@Override
protected void onDestroy() {
super.onDestroy();
- mMapView.onDestroy();
+ mapView.onDestroy();
}
@Override
public void onLowMemory() {
super.onLowMemory();
- mMapView.onLowMemory();
+ mapView.onLowMemory();
}
@Override
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 65a5afa602..0c4eeae5ad 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
@@ -1,7 +1,11 @@
package com.mapbox.mapboxsdk.testapp.activity.annotation;
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.content.Context;
-import android.graphics.Point;
+import android.graphics.PointF;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
@@ -17,7 +21,10 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
+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.annotations.MarkerView;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
@@ -26,24 +33,31 @@ 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.model.annotations.CountryMarkerOptions;
import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerView;
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.Random;
public class MarkerViewActivity extends AppCompatActivity {
private MapboxMap mMapboxMap;
private MapView mMapView;
+ private MarkerView movingMarkerOne, movingMarkerTwo;
+ private Random randomAnimator = new Random();
+ private Handler locationUpdateHandler = new Handler();
+ private Runnable moveMarkerRunnable = new MoveMarkerRunnable();
+ private int rotation = 0;
+
private final static LatLng[] LAT_LNGS = new LatLng[]{
- new LatLng(38.907327, -77.041293),
+ new LatLng(38.897424, -77.036508),
new LatLng(38.909698, -77.029642),
new LatLng(38.907227, -77.036530),
new LatLng(38.905607, -77.031916),
- new LatLng(38.897424, -77.036508),
- new LatLng(38.897642, -77.041980),
- new LatLng(38.889876, -77.008849),
- new LatLng(38.889441, -77.050134)
+ new LatLng(38.889441, -77.050134),
+ new LatLng(38.888000, -77.050000) //Slight overlap to show re-ordering on selection
};
@Override
@@ -68,38 +82,56 @@ public class MarkerViewActivity extends AppCompatActivity {
public void onMapReady(@NonNull MapboxMap mapboxMap) {
mMapboxMap = mapboxMap;
- // add text markers
+ final MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager();
+
+ Icon usFlag = IconFactory.getInstance(MarkerViewActivity.this)
+ .fromResource(R.drawable.ic_us);
+
+ // add default ViewMarker markers
for (int i = 0; i < LAT_LNGS.length; i++) {
mMapboxMap.addMarker(new MarkerViewOptions()
.position(LAT_LNGS[i])
.title(String.valueOf(i))
- .selectAnimatorResource(R.animator.scale_up)
- .deselectAnimatorResource(R.animator.scale_down)
+ .alpha(0.5f)
+ .icon(usFlag)
);
}
- // add flag marker
+ // add custom ViewMarker
CountryMarkerViewOptions options = new CountryMarkerViewOptions();
- options.title("United States");
- options.abbrevName("us");
- options.flagRes(R.drawable.ic_us);
+ options.flagRes(R.drawable.icon_burned);
+ options.abbrevName("Mapbox");
+ options.title("Hello");
options.position(new LatLng(38.899774, -77.023237));
- options.selectAnimatorResource(R.animator.rotate_360);
- options.deselectAnimatorResource(R.animator.rotate_360);
options.flat(true);
mapboxMap.addMarker(options);
- // default GL marker
- mMapboxMap.addMarker(new CountryMarkerOptions()
+ mMapboxMap.addMarker(new MarkerOptions()
.title("United States")
.position(new LatLng(38.902580, -77.050102))
);
- // set adapters
- final MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager();
- markerViewManager.addMarkerViewAdapter(new TextAdapter(MarkerViewActivity.this));
- markerViewManager.addMarkerViewAdapter(new CountryAdapter(MarkerViewActivity.this));
+ mMapboxMap.addMarker(new TextMarkerViewOptions()
+ .text("A")
+ .position(new LatLng(38.889876, -77.008849))
+ );
+
+ mMapboxMap.addMarker(new TextMarkerViewOptions()
+ .text("B")
+ .position(new LatLng(38.907327, -77.041293))
+ );
+ mMapboxMap.addMarker(new TextMarkerViewOptions()
+ .text("C")
+ .position(new LatLng(38.897642, -77.041980))
+ );
+
+ // if you want to customise a ViewMarker you need to extend ViewMarker and provide an adapter implementation
+ // set adapters for child classes of ViewMarker
+ markerViewManager.addMarkerViewAdapter(new CountryAdapter(MarkerViewActivity.this, mapboxMap));
+ markerViewManager.addMarkerViewAdapter(new TextAdapter(MarkerViewActivity.this, mapboxMap));
+
+ // add a change listener to validate the size of amount of child views
mMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
@Override
public void onMapChanged(@MapView.MapChange int change) {
@@ -111,6 +143,7 @@ public class MarkerViewActivity extends AppCompatActivity {
}
});
+ // add a OnMarkerView click listener
mMapboxMap.getMarkerViewManager().setOnMarkerViewClickListener(new MapboxMap.OnMarkerViewClickListener() {
@Override
public boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MapboxMap.MarkerViewAdapter adapter) {
@@ -118,73 +151,219 @@ public class MarkerViewActivity extends AppCompatActivity {
return false;
}
});
+
+ movingMarkerOne = mMapboxMap.addMarker(new MarkerViewOptions()
+ .position(CarLocation.CAR_0_LNGS[0])
+ .icon(IconFactory.getInstance(mMapView.getContext())
+ .fromResource(R.drawable.ic_chelsea))
+ );
+
+ movingMarkerTwo = mapboxMap.addMarker(new MarkerViewOptions()
+ .position(CarLocation.CAR_1_LNGS[0])
+ .icon(IconFactory.getInstance(mMapView.getContext())
+ .fromResource(R.drawable.ic_arsenal))
+ );
}
});
}
- private static class TextAdapter extends MapboxMap.MarkerViewAdapter<MarkerView> {
+ @Override
+ protected void onStart() {
+ super.onStart();
+ loopMarkerMove();
+ }
+
+ private void loopMarkerMove() {
+ locationUpdateHandler.postDelayed(moveMarkerRunnable, randomAnimator.nextInt(3000) + 1000);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ locationUpdateHandler.removeCallbacks(moveMarkerRunnable);
+ }
+
+ private class MoveMarkerRunnable implements Runnable {
+ @Override
+ public void run() {
+ int i = randomAnimator.nextInt(9);
+ if (randomAnimator.nextInt() % 2 == 0) {
+ movingMarkerOne.setPosition(CarLocation.CAR_0_LNGS[i]);
+ movingMarkerOne.setRotation(rotation = rotation + 45);
+ } else {
+ movingMarkerTwo.setPosition(CarLocation.CAR_1_LNGS[i]);
+ movingMarkerTwo.setRotation(rotation = rotation + 90);
+ }
+ loopMarkerMove();
+ }
+ }
+
+ private static class CountryAdapter extends MapboxMap.MarkerViewAdapter<CountryMarkerView> {
private LayoutInflater inflater;
+ private MapboxMap mapboxMap;
- public TextAdapter(@NonNull Context context) {
+ public CountryAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
super(context);
this.inflater = LayoutInflater.from(context);
+ this.mapboxMap = mapboxMap;
}
@Nullable
@Override
- public View getView(@NonNull MarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
+ public View getView(@NonNull CountryMarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
- convertView = inflater.inflate(R.layout.view_text_marker, parent, false);
- viewHolder.title = (TextView) convertView.findViewById(R.id.textView);
+ convertView = inflater.inflate(R.layout.view_custom_marker, parent, false);
+ viewHolder.flag = (ImageView) convertView.findViewById(R.id.imageView);
+ viewHolder.abbrev = (TextView) convertView.findViewById(R.id.textView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
- viewHolder.title.setText(marker.getTitle());
+ viewHolder.flag.setImageResource(marker.getFlagRes());
+ viewHolder.abbrev.setText(marker.getAbbrevName());
return convertView;
}
+ @Override
+ public boolean onSelect(@NonNull final CountryMarkerView marker, @NonNull final View convertView, boolean reselectionForViewReuse) {
+ convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(convertView, View.ROTATION, 0, 360);
+ rotateAnimator.setDuration(reselectionForViewReuse ? 0 : 350);
+ rotateAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ convertView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mapboxMap.selectMarker(marker);
+ }
+ });
+ rotateAnimator.start();
+
+ // false indicates that we are calling selectMarker after our animation ourselves
+ // true will let the system call it for you, which will result in showing an InfoWindow instantly
+ return false;
+ }
+
+ @Override
+ public void onDeselect(@NonNull CountryMarkerView marker, @NonNull final View convertView) {
+ convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(convertView, View.ROTATION, 360, 0);
+ rotateAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ convertView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
+ rotateAnimator.start();
+ }
+
private static class ViewHolder {
- TextView title;
+ ImageView flag;
+ TextView abbrev;
}
}
- private static class CountryAdapter extends MapboxMap.MarkerViewAdapter<CountryMarkerView> {
+
+ private static class TextAdapter extends MapboxMap.MarkerViewAdapter<TextMarkerView> {
private LayoutInflater inflater;
+ private MapboxMap mapboxMap;
- public CountryAdapter(@NonNull Context context) {
+ public TextAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
super(context);
this.inflater = LayoutInflater.from(context);
+ this.mapboxMap = mapboxMap;
}
@Nullable
@Override
- public View getView(@NonNull CountryMarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
+ public View getView(@NonNull TextMarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
- convertView = inflater.inflate(R.layout.view_custom_marker, parent, false);
- viewHolder.flag = (ImageView) convertView.findViewById(R.id.imageView);
- viewHolder.abbrev = (TextView) convertView.findViewById(R.id.textView);
+ convertView = inflater.inflate(R.layout.view_text_marker, parent, false);
+ viewHolder.textView = (TextView) convertView.findViewById(R.id.textView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
- viewHolder.flag.setImageResource(marker.getFlagRes());
- viewHolder.abbrev.setText(marker.getAbbrevName());
+ viewHolder.textView.setText(marker.getText());
return convertView;
}
+ @Override
+ public boolean onSelect(@NonNull final TextMarkerView marker, @NonNull final View convertView, boolean reselectionForViewReuse) {
+ animateGrow(marker, convertView, 0);
+
+ // false indicates that we are calling selectMarker after our animation ourselves
+ // true will let the system call it for you, which will result in showing an InfoWindow instantly
+ return false;
+ }
+
+ @Override
+ public void onDeselect(@NonNull TextMarkerView marker, @NonNull final View convertView) {
+ animateShrink(convertView, 350);
+ }
+
+ @Override
+ public boolean prepareViewForReuse(@NonNull MarkerView marker, @NonNull View convertView) {
+ // this method is called before a view will be reused, we need to restore view state
+ // as we have scaled the view in onSelect. If not correctly applied other MarkerView will
+ // become large since these have been recycled
+
+ // cancel ongoing animation
+ convertView.animate().cancel();
+
+ if (marker.isSelected()) {
+ // shrink view to be able to be reused
+ animateShrink(convertView, 0);
+ }
+
+ // true if you want reuse to occur automatically, false if you want to manage this yourself
+ return true;
+ }
+
+ private void animateGrow(@NonNull final MarkerView marker, @NonNull final View convertView, int duration) {
+ convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ Animator animator = AnimatorInflater.loadAnimator(convertView.getContext(), R.animator.scale_up);
+ animator.setDuration(duration);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ convertView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mapboxMap.selectMarker(marker);
+ }
+ });
+ animator.setTarget(convertView);
+ animator.start();
+ }
+
+ private void animateShrink(@NonNull final View convertView, int duration) {
+ convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ Animator animator = AnimatorInflater.loadAnimator(convertView.getContext(), R.animator.scale_down);
+ animator.setDuration(duration);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ convertView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
+ animator.setTarget(convertView);
+ animator.start();
+ }
+
private static class ViewHolder {
- ImageView flag;
- TextView abbrev;
+ TextView textView;
}
}
+
@Override
public void onResume() {
super.onResume();
@@ -225,4 +404,32 @@ public class MarkerViewActivity extends AppCompatActivity {
return super.onOptionsItemSelected(item);
}
}
+
+ private static class CarLocation {
+
+ public static LatLng[] CAR_0_LNGS = new LatLng[]{
+ new LatLng(38.92334425495122, -77.0533673443786),
+ new LatLng(38.9234737236897, -77.05389484528261),
+ new LatLng(38.9257094658146, -76.98819752280579),
+ new LatLng(38.8324328369647, -77.00690648325929),
+ new LatLng(38.87540698725855, -77.0093148713099),
+ new LatLng(38.96499498141065, -77.07707916040054),
+ new LatLng(38.90794910679896, -76.99695304153806),
+ new LatLng(38.86234025281626, -76.9950528034839),
+ new LatLng(38.862930274733635, -76.99647808241964)
+ };
+
+ public static LatLng[] CAR_1_LNGS = new LatLng[]{
+ new LatLng(38.94237975070426, -76.98324549005675),
+ new LatLng(38.941520236084486, -76.98234257804742),
+ new LatLng(38.85972219720714, -76.98955808483929),
+ new LatLng(38.944289166113776, -76.98584257252891),
+ new LatLng(38.94375860578053, -76.98470344318412),
+ new LatLng(38.943167431929645, -76.98373163938666),
+ new LatLng(38.882834728904605, -77.02862535635137),
+ new LatLng(38.882869724926245, -77.02992539231113),
+ new LatLng(38.9371988177896, -76.97786740676564)
+ };
+
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewScaleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewScaleActivity.java
new file mode 100644
index 0000000000..37b1d32a68
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewScaleActivity.java
@@ -0,0 +1,141 @@
+package com.mapbox.mapboxsdk.testapp.activity.annotation;
+
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
+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 java.util.Locale;
+
+public class MarkerViewScaleActivity extends AppCompatActivity {
+
+ private MapView mMapView;
+ private View mMarkerView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_marker_view_scale);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ final SeekBar xBar = (SeekBar) findViewById(R.id.seekbar_factor);
+ TextView xText = (TextView) findViewById(R.id.textview_factor);
+ xBar.setOnSeekBarChangeListener(new FactorChangeListener(xText));
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ Icon icon = IconFactory.getInstance(MarkerViewScaleActivity.this)
+ .fromResource(R.drawable.ic_circle);
+
+ MarkerView mMarker = mapboxMap.addMarker(new MarkerViewOptions()
+ .position(new LatLng(38.907192, -77.036871))
+ .icon(icon)
+ .flat(true));
+
+ mMarkerView = mapboxMap.getMarkerViewManager().getView(mMarker);
+ }
+ });
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ private class FactorChangeListener implements SeekBar.OnSeekBarChangeListener {
+
+ private TextView xText;
+
+ public FactorChangeListener(TextView xText) {
+ this.xText = xText;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ float newScale = getScale(progress);
+ xText.setText(String.format(Locale.US, "Scale: %.1f", newScale));
+ if (MarkerViewScaleActivity.this.mMarkerView != null) {
+ mMarkerView.setScaleX(newScale);
+ mMarkerView.setScaleY(newScale);
+
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // Not used
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // Not used
+ }
+
+ private float getScale(int progress) {
+ float scale = 1.0f * progress / 25;
+ return scale < 1.0 ? 1.0f : scale;
+ }
+ }
+
+}
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 f08d65163b..9f3940bd3f 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
@@ -5,9 +5,11 @@ import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
+import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
+import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.Style;
@@ -17,14 +19,45 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
import java.util.ArrayList;
import java.util.List;
public class PolygonActivity extends AppCompatActivity {
+ private static final int BLUE_COLOR = Color.parseColor("#3bb2d0");
+ private static final int RED_COLOR = Color.parseColor("#AF0000");
+
+ private static final float FULL_ALPHA = 1.0f;
+ private static final float PARTIAL_ALPHA = 0.5f;
+ private static final float NO_ALPHA = 0.0f;
+
+ private static final List<LatLng> POINTS = new ArrayList<LatLng>() {{
+ add(new LatLng(45.522585, -122.685699));
+ add(new LatLng(45.534611, -122.708873));
+ add(new LatLng(45.530883, -122.678833));
+ add(new LatLng(45.547115, -122.667503));
+ add(new LatLng(45.530643, -122.660121));
+ add(new LatLng(45.533529, -122.636260));
+ add(new LatLng(45.521743, -122.659091));
+ add(new LatLng(45.510677, -122.648792));
+ add(new LatLng(45.515008, -122.664070));
+ add(new LatLng(45.502496, -122.669048));
+ }};
+
+ private List<LatLng> ADDITIONAL_POINTS = new ArrayList<LatLng>() {{
+ add(new LatLng(45.515369, -122.678489));
+ add(new LatLng(45.506346, -122.702007));
+ add(new LatLng(45.522585, -122.685699));
+ }};
+
private MapView mapView;
+ private Polygon polygon;
+
+ private boolean fullAlpha = true;
+ private boolean visible = true;
+ private boolean allPoints = true;
+ private boolean color = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -42,9 +75,8 @@ public class PolygonActivity extends AppCompatActivity {
// configure inital map state
MapboxMapOptions options = new MapboxMapOptions()
- .attributionTintColor(Color.RED)
- .accessToken(getString(R.string.mapbox_access_token))
- .styleUrl(Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION))
+ .attributionTintColor(RED_COLOR)
+ .styleUrl(Style.MAPBOX_STREETS)
.camera(new CameraPosition.Builder()
.target(new LatLng(45.520486, -122.673541))
.zoom(12)
@@ -57,23 +89,10 @@ public class PolygonActivity extends AppCompatActivity {
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(MapboxMap mapboxMap) {
- List<LatLng> polygon = new ArrayList<>();
- polygon.add(new LatLng(45.522585, -122.685699));
- polygon.add(new LatLng(45.534611, -122.708873));
- polygon.add(new LatLng(45.530883, -122.678833));
- polygon.add(new LatLng(45.547115, -122.667503));
- polygon.add(new LatLng(45.530643, -122.660121));
- polygon.add(new LatLng(45.533529, -122.636260));
- polygon.add(new LatLng(45.521743, -122.659091));
- polygon.add(new LatLng(45.510677, -122.648792));
- polygon.add(new LatLng(45.515008, -122.664070));
- polygon.add(new LatLng(45.502496, -122.669048));
- polygon.add(new LatLng(45.515369, -122.678489));
- polygon.add(new LatLng(45.506346, -122.702007));
- polygon.add(new LatLng(45.522585, -122.685699));
- mapboxMap.addPolygon(new PolygonOptions()
- .addAll(polygon)
- .fillColor(Color.parseColor("#3bb2d0")));
+ POINTS.addAll(ADDITIONAL_POINTS);
+ polygon = mapboxMap.addPolygon(new PolygonOptions()
+ .addAll(POINTS)
+ .fillColor(BLUE_COLOR));
}
});
@@ -117,8 +136,39 @@ public class PolygonActivity extends AppCompatActivity {
case android.R.id.home:
onBackPressed();
return true;
+
+ case R.id.action_id_alpha:
+ fullAlpha = !fullAlpha;
+ polygon.setAlpha(fullAlpha ? FULL_ALPHA : PARTIAL_ALPHA);
+ return true;
+
+ case R.id.action_id_visible:
+ visible = !visible;
+ polygon.setAlpha(visible ? (fullAlpha ? FULL_ALPHA : PARTIAL_ALPHA) : NO_ALPHA);
+ return true;
+
+ case R.id.action_id_points:
+ allPoints = !allPoints;
+ if (allPoints) {
+ POINTS.addAll(ADDITIONAL_POINTS);
+ } else {
+ POINTS.removeAll(ADDITIONAL_POINTS);
+ }
+ polygon.setPoints(POINTS);
+ return true;
+
+ case R.id.action_id_color:
+ color = !color;
+ polygon.setFillColor(color ? BLUE_COLOR : RED_COLOR);
+
default:
return super.onOptionsItemSelected(item);
}
}
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_polygon, menu);
+ return true;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java
index 9eb4bc1741..5b45223da0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java
@@ -25,6 +25,7 @@ import java.util.List;
public class PolylineActivity extends AppCompatActivity {
private static final String STATE_POLYLINE_OPTIONS = "polylineOptions";
+
private static final LatLng ANDORRA = new LatLng(42.505777, 1.52529);
private static final LatLng LUXEMBOURG = new LatLng(49.815273, 6.129583);
private static final LatLng MONACO = new LatLng(43.738418, 7.424616);
@@ -32,11 +33,20 @@ public class PolylineActivity extends AppCompatActivity {
private static final LatLng SAN_MARINO = new LatLng(43.942360, 12.457777);
private static final LatLng LIECHTENSTEIN = new LatLng(47.166000, 9.555373);
+ private static final float FULL_ALPHA = 1.0f;
+ private static final float PARTIAL_ALPHA = 0.5f;
+ private static final float NO_ALPHA = 0.0f;
+
private List<Polyline> mPolylines;
private ArrayList<PolylineOptions> mPolylineOptions = new ArrayList<>();
private MapView mMapView;
private MapboxMap mMapboxMap;
+ private boolean fullAlpha = true;
+ private boolean visible = true;
+ private boolean width = true;
+ private boolean color = true;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -67,25 +77,29 @@ public class PolylineActivity extends AppCompatActivity {
}
});
- findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mMapboxMap != null) {
- if (mPolylines != null && mPolylines.size() > 0) {
- if (mPolylines.size() == 1) {
- // test for removing annotation
- mMapboxMap.removeAnnotation(mPolylines.get(0));
- } else {
- // test for removing annotations
- mMapboxMap.removeAnnotations(mPolylines);
+ View fab = findViewById(R.id.fab);
+ if (fab != null) {
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mMapboxMap != null) {
+ if (mPolylines != null && mPolylines.size() > 0) {
+ if (mPolylines.size() == 1) {
+ // test for removing annotation
+ mMapboxMap.removeAnnotation(mPolylines.get(0));
+ } else {
+ // test for removing annotations
+ mMapboxMap.removeAnnotations(mPolylines);
+ }
}
+ mPolylineOptions.clear();
+ mPolylineOptions.addAll(getRandomLine());
+ mPolylines = mMapboxMap.addPolylines(mPolylineOptions);
+
}
- mPolylineOptions.clear();
- mPolylineOptions.addAll(getRandomLine());
- mPolylines = mMapboxMap.addPolylines(mPolylineOptions);
}
- }
- });
+ });
+ }
}
private List<PolylineOptions> getAllPolylines() {
@@ -161,6 +175,34 @@ public class PolylineActivity extends AppCompatActivity {
mMapboxMap.clear();
return true;
+ case R.id.action_id_alpha:
+ fullAlpha = !fullAlpha;
+ for (Polyline p : mPolylines) {
+ p.setAlpha(fullAlpha ? FULL_ALPHA : PARTIAL_ALPHA);
+ }
+ return true;
+
+ case R.id.action_id_color:
+ color = !color;
+ for (Polyline p : mPolylines) {
+ p.setColor(color ? Color.RED : Color.BLUE);
+ }
+ return true;
+
+ case R.id.action_id_width:
+ width = !width;
+ for (Polyline p : mPolylines) {
+ p.setWidth(width ? 3.0f : 5.0f);
+ }
+ return true;
+
+ case R.id.action_id_visible:
+ visible = !visible;
+ for (Polyline p : mPolylines) {
+ p.setAlpha(visible ? (fullAlpha ? FULL_ALPHA : PARTIAL_ALPHA) : NO_ALPHA);
+ }
+ return true;
+
case android.R.id.home:
onBackPressed();
return true;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java
index 88008d64fb..0c014f7693 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java
@@ -9,14 +9,14 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
+
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+
import java.text.DecimalFormat;
import java.util.ArrayList;
@@ -50,7 +50,6 @@ public class PressForMarkerActivity extends AppCompatActivity {
@Override
public void onMapReady(final MapboxMap map) {
mapboxMap = map;
- mapboxMap.setStyleUrl(Style.getOutdoorsStyleUrl(9));
resetMap();
mapboxMap.setOnMapLongClickListener(new MapboxMap.OnMapLongClickListener() {
@@ -83,7 +82,6 @@ public class PressForMarkerActivity extends AppCompatActivity {
if (mapboxMap == null) {
return;
}
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(45.1855569, 5.7215506), 11));
mapboxMap.removeAnnotations();
}
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 b9a438b7d5..3ab8f7e763 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
@@ -9,17 +9,19 @@ import android.util.Log;
import android.view.MenuItem;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.UiSettings;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+
+import java.util.ArrayList;
+import java.util.List;
public class LatLngBoundsActivity extends AppCompatActivity {
@@ -43,7 +45,7 @@ public class LatLngBoundsActivity extends AppCompatActivity {
}
mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setStyleUrl(Style.getDarkStyleUrl(AppConstant.STYLE_VERSION));
+ mMapView.setStyleUrl(Style.DARK);
mMapView.onCreate(savedInstanceState);
mMapView.getMapAsync(new OnMapReadyCallback() {
@Override
@@ -62,10 +64,13 @@ public class LatLngBoundsActivity extends AppCompatActivity {
.snippet("City Hall")
.position(NEW_YORK));
+ List<LatLng> points = new ArrayList<>();
+ points.add(NEW_YORK);
+ points.add(LOS_ANGELES);
+
// Create Bounds
final LatLngBounds bounds = new LatLngBounds.Builder()
- .include(NEW_YORK)
- .include(LOS_ANGELES)
+ .includes(points)
.build();
// Add map padding
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java
index bc3691a2f7..897380d1aa 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java
@@ -12,12 +12,11 @@ import android.view.View;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
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.maps.UiSettings;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
public class ManualZoomActivity extends AppCompatActivity {
@@ -39,7 +38,7 @@ public class ManualZoomActivity extends AppCompatActivity {
}
mMapView = (MapView) findViewById(R.id.manualZoomMapView);
- mMapView.setStyleUrl(Style.getSatelliteStyleUrl(AppConstant.STYLE_VERSION));
+ mMapView.setStyleUrl(Style.SATELLITE);
mMapView.onCreate(savedInstanceState);
mMapView.getMapAsync(new OnMapReadyCallback() {
@Override
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 98c86d4313..4070216537 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
@@ -6,18 +6,21 @@ import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
+import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.layers.CustomLayer;
+import com.mapbox.mapboxsdk.style.layers.CustomLayer;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.model.customlayer.ExampleCustomLayer;
public class CustomLayerActivity extends AppCompatActivity {
+ private static final String TAG = CustomLayerActivity.class.getSimpleName();
private MapboxMap mapboxMap;
private MapView mapView;
@@ -66,10 +69,14 @@ public class CustomLayerActivity extends AppCompatActivity {
private void swapCustomLayer() {
if (isShowingCustomLayer) {
- mapboxMap.removeCustomLayer("custom");
+ try {
+ mapboxMap.removeLayer("custom");
+ } catch (NoSuchLayerException e) {
+ Log.e(TAG, "No custom layer to remove");
+ }
fab.setImageResource(R.drawable.ic_layers_24dp);
} else {
- mapboxMap.addCustomLayer(new CustomLayer("custom",
+ mapboxMap.addLayer(new CustomLayer("custom",
ExampleCustomLayer.createContext(),
ExampleCustomLayer.InitializeFunction,
ExampleCustomLayer.RenderFunction,
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/directions/DirectionsActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/directions/DirectionsActivity.java
index d5f248f89d..7d46cdb6bb 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/directions/DirectionsActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/directions/DirectionsActivity.java
@@ -8,12 +8,6 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
-
-import com.mapbox.directions.DirectionsCriteria;
-import com.mapbox.directions.MapboxDirections;
-import com.mapbox.directions.service.models.DirectionsResponse;
-import com.mapbox.directions.service.models.DirectionsRoute;
-import com.mapbox.directions.service.models.Waypoint;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
@@ -23,12 +17,18 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.maps.MapView;
-
-import java.util.List;
-
-import retrofit.Callback;
-import retrofit.Response;
-import retrofit.Retrofit;
+import com.mapbox.services.Constants;
+import com.mapbox.services.commons.ServicesException;
+import com.mapbox.services.commons.geojson.LineString;
+import com.mapbox.services.commons.models.Position;
+import com.mapbox.services.directions.v4.DirectionsCriteria;
+import com.mapbox.services.directions.v4.MapboxDirections;
+import com.mapbox.services.directions.v4.models.DirectionsResponse;
+import com.mapbox.services.directions.v4.models.DirectionsRoute;
+import com.mapbox.services.directions.v4.models.Waypoint;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
public class DirectionsActivity extends AppCompatActivity {
@@ -94,49 +94,55 @@ public class DirectionsActivity extends AppCompatActivity {
}
private void getRoute(Waypoint origin, Waypoint destination) {
- MapboxDirections md = new MapboxDirections.Builder()
- .setAccessToken(getString(R.string.mapbox_access_token))
- .setOrigin(origin)
- .setDestination(destination)
- .setProfile(DirectionsCriteria.PROFILE_WALKING)
- .build();
-
- md.enqueue(new Callback<DirectionsResponse>() {
- @Override
- public void onResponse(Response<DirectionsResponse> response, Retrofit retrofit) {
- // You can get generic HTTP info about the response
- Log.d(LOG_TAG, "Response code: " + response.code());
-
- // Print some info about the route
- DirectionsRoute currentRoute = response.body().getRoutes().get(0);
- Log.d(LOG_TAG, "Distance: " + currentRoute.getDistance());
-
- // Draw the route on the map
- drawRoute(currentRoute);
- }
-
- @Override
- public void onFailure(Throwable t) {
- Log.e(LOG_TAG, "Error: " + t.getMessage());
- }
- });
+ try {
+ MapboxDirections md = new MapboxDirections.Builder()
+ .setAccessToken(getString(R.string.mapbox_access_token))
+ .setOrigin(origin)
+ .setDestination(destination)
+ .setProfile(DirectionsCriteria.PROFILE_WALKING)
+ .build();
+
+ md.enqueueCall(new Callback<DirectionsResponse>() {
+
+ @Override
+ public void onFailure(Call<DirectionsResponse> call, Throwable t) {
+ Log.e(LOG_TAG, "Error: " + t.getMessage());
+ }
+
+ @Override
+ public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
+ // You can get generic HTTP info about the response
+ Log.d(LOG_TAG, "Response code: " + response.code());
+
+ // Print some info about the route
+ DirectionsRoute currentRoute = response.body().getRoutes().get(0);
+ Log.d(LOG_TAG, "Distance: " + currentRoute.getDistance());
+
+ // Draw the route on the map
+ drawRoute(currentRoute);
+ }
+
+ });
+ } catch (ServicesException e) {
+ Log.e(LOG_TAG, "Error displaying route: " + e.toString());
+ e.printStackTrace();
+ }
}
private void drawRoute(DirectionsRoute route) {
- // Convert List<Waypoint> into LatLng[]
- List<Waypoint> waypoints = route.getGeometry().getWaypoints();
- LatLng[] point = new LatLng[waypoints.size()];
- for (int i = 0; i < waypoints.size(); i++) {
- point[i] = new LatLng(
- waypoints.get(i).getLatitude(),
- waypoints.get(i).getLongitude());
+
+ PolylineOptions builder = new PolylineOptions();
+ builder.color(Color.parseColor("#3887be"));
+ builder.alpha(0.5f);
+ builder.width(5);
+ builder.width(5);
+ LineString lineString = route.asLineString(Constants.OSRM_PRECISION_V4);
+ for (Position coordinates : lineString.getCoordinates()) {
+ builder.add(new LatLng(coordinates.getLatitude(), coordinates.getLongitude()));
}
// Draw Points on MapView
- mMapboxMap.addPolyline(new PolylineOptions()
- .add(point)
- .color(Color.parseColor("#3887be"))
- .width(5));
+ mMapboxMap.addPolyline(builder);
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java
index 5987855aac..9d72c2b02a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java
@@ -17,7 +17,6 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
public class MapFragmentActivity extends AppCompatActivity {
@@ -40,7 +39,7 @@ public class MapFragmentActivity extends AppCompatActivity {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
MapboxMapOptions options = new MapboxMapOptions();
- options.styleUrl(Style.getOutdoorsStyleUrl(AppConstant.STYLE_VERSION));
+ options.styleUrl(Style.OUTDOORS);
options.scrollGesturesEnabled(false);
options.zoomGesturesEnabled(false);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java
index bb391fc93d..cc61eb4698 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java
@@ -17,7 +17,6 @@ import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.SupportMapFragment;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
public class SupportMapFragmentActivity extends AppCompatActivity {
@@ -40,7 +39,7 @@ public class SupportMapFragmentActivity extends AppCompatActivity {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
MapboxMapOptions options = new MapboxMapOptions();
- options.styleUrl(Style.getSatelliteStreetsStyleUrl(AppConstant.STYLE_VERSION));
+ options.styleUrl(Style.SATELLITE_STREETS);
options.scrollGesturesEnabled(false);
options.zoomGesturesEnabled(false);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java
new file mode 100644
index 0000000000..bc1d742113
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java
@@ -0,0 +1,111 @@
+package com.mapbox.mapboxsdk.testapp.activity.fragment;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.constants.MyLocationTracking;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapFragment;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.maps.SupportMapFragment;
+import com.mapbox.mapboxsdk.maps.TrackingSettings;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class ViewPagerActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_viewpager);
+
+ final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
+ if (viewPager != null) {
+ MapFragmentAdapter adapter = new MapFragmentAdapter(getSupportFragmentManager());
+ viewPager.setAdapter(adapter);
+ }
+ }
+
+ public static class MapFragmentAdapter extends FragmentPagerAdapter {
+
+ private static int NUM_ITEMS = 3;
+
+ public MapFragmentAdapter(FragmentManager fragmentManager) {
+ super(fragmentManager);
+ }
+
+ @Override
+ public int getCount() {
+ return NUM_ITEMS;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ SupportMapFragment fragment = null;
+ MapboxMapOptions options = new MapboxMapOptions();
+
+ switch (position) {
+ case 0:
+ options.styleUrl(Style.MAPBOX_STREETS);
+ options.camera(new CameraPosition.Builder().target(new LatLng(34.920526, 102.634774)).zoom(3).build());
+ fragment = SupportMapFragment.newInstance(options);
+ break;
+ case 1:
+ options.styleUrl(Style.DARK);
+ options.camera(new CameraPosition.Builder().target(new LatLng(62.326440, 92.764913)).zoom(3).build());
+ fragment = SupportMapFragment.newInstance(options);
+ break;
+ case 2:
+ options.styleUrl(Style.SATELLITE);
+ options.camera(new CameraPosition.Builder().target(new LatLng(-25.007786, 133.623852)).zoom(3).build());
+ fragment = SupportMapFragment.newInstance(options);
+ break;
+ }
+ return fragment;
+ }
+
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return "Page " + position;
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java
index fc03066a4e..7c99e16b2e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java
@@ -14,26 +14,27 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
-
-import com.mapbox.geocoder.GeocoderCriteria;
-import com.mapbox.geocoder.MapboxGeocoder;
-import com.mapbox.geocoder.service.models.GeocoderFeature;
-import com.mapbox.geocoder.service.models.GeocoderResponse;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Projection;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
+import com.mapbox.services.commons.ServicesException;
+import com.mapbox.services.commons.models.Position;
+import com.mapbox.services.geocoding.v5.GeocodingCriteria;
+import com.mapbox.services.geocoding.v5.MapboxGeocoding;
+import com.mapbox.services.geocoding.v5.models.GeocodingFeature;
+import com.mapbox.services.geocoding.v5.models.GeocodingResponse;
import java.util.List;
-import retrofit.Callback;
-import retrofit.Response;
-import retrofit.Retrofit;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
public class GeocoderActivity extends AppCompatActivity {
@@ -60,7 +61,7 @@ public class GeocoderActivity extends AppCompatActivity {
setMessage(getString(R.string.geocoder_instructions));
mapView = (MapView) findViewById(R.id.mapView);
- mapView.setStyleUrl(Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION));
+ mapView.setStyleUrl(Style.MAPBOX_STREETS);
mapView.onCreate(savedInstanceState);
final ImageView dropPinView = new ImageView(this);
@@ -124,40 +125,38 @@ public class GeocoderActivity extends AppCompatActivity {
*/
private void geocode(final LatLng point) {
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- MapboxGeocoder client = new MapboxGeocoder.Builder()
- .setAccessToken(getString(R.string.mapbox_access_token))
- .setCoordinates(point.getLongitude(), point.getLatitude())
- .setType(GeocoderCriteria.TYPE_POI)
- .build();
-
- client.enqueue(new Callback<GeocoderResponse>()
-
- {
- @Override
- public void onResponse(Response<GeocoderResponse> response, Retrofit retrofit) {
- List<GeocoderFeature> results = response.body().getFeatures();
- if (results.size() > 0) {
- String placeName = results.get(0).getPlaceName();
- setSuccess(placeName);
- } else {
- setMessage("No results.");
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- setError(t.getMessage());
- }
- }
-
- );
- return null;
- }
- }.execute();
+ try {
+ MapboxGeocoding client = new MapboxGeocoding.Builder()
+ .setAccessToken(getString(R.string.mapbox_access_token))
+ .setCoordinates(Position.fromCoordinates(point.getLongitude(), point.getLatitude()))
+ .setType(GeocodingCriteria.TYPE_POI)
+ .build();
+
+ client.enqueueCall(new Callback<GeocodingResponse>() {
+ @Override
+ public void onResponse(Call<GeocodingResponse> call, Response<GeocodingResponse> response) {
+
+ List<GeocodingFeature> results = response.body().getFeatures();
+ if (results.size() > 0) {
+ String placeName = results.get(0).getPlaceName();
+ setSuccess(placeName);
+ } else {
+ setMessage("No results.");
+ }
+
+ }
+
+ @Override
+ public void onFailure(Call<GeocodingResponse> call, Throwable t) {
+ setError(t.getMessage());
+ }
+ });
+ } catch (ServicesException e) {
+ Log.e(LOG_TAG, "Error geocoding: " + e.toString());
+ e.printStackTrace();
+ setError(e.getMessage());
+ }
}
/*
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java
new file mode 100644
index 0000000000..4d19b4702e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java
@@ -0,0 +1,112 @@
+package com.mapbox.mapboxsdk.testapp.activity.imagegenerator;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.app.Activity;
+import android.support.annotation.NonNull;
+import android.support.v4.print.PrintHelper;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+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.activity.annotation.BulkMarkerActivity;
+
+public class PrintActivity extends AppCompatActivity implements MapboxMap.SnapshotReadyCallback {
+
+ private MapView mMapView;
+ private MapboxMap mMapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_print);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+ mMapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+ }
+ });
+
+ final View fab = findViewById(R.id.fab);
+ if (fab != null) {
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if(mMapboxMap!=null) {
+ mMapboxMap.snapshot(PrintActivity.this);
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onSnapshotReady(Bitmap snapshot) {
+ PrintHelper photoPrinter = new PrintHelper(this);
+ photoPrinter.setScaleMode(PrintHelper.SCALE_MODE_FIT);
+ photoPrinter.printBitmap("map.jpg - mapbox print job", snapshot);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
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 93ebdcd157..84e394c72d 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
@@ -12,7 +12,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
-
+import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
@@ -41,6 +41,7 @@ public class SnapshotActivity extends AppCompatActivity {
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ mapboxMap.setStyleUrl(Style.OUTDOORS);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(SnapshotActivity.this, R.color.primary));
fab.setOnClickListener(new View.OnClickListener() {
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
new file mode 100644
index 0000000000..5b2cae5e04
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java
@@ -0,0 +1,169 @@
+package com.mapbox.mapboxsdk.testapp.activity.infowindow;
+
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.InfoWindow;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
+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;
+
+/**
+ * Shows how to dynamically update InfoWindow when Using an MapboxMap.InfoWindowAdapter
+ */
+public class DynamicInfoWindowAdapterActivity extends AppCompatActivity {
+
+ private MapView mapView;
+
+ private LatLng paris = new LatLng(48.864716, 2.349014);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_infowindow_adapter);
+
+ setupActionBar();
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull final MapboxMap mapboxMap) {
+ //Add info window adapter
+ addCustomInfoWindowAdapter(mapboxMap);
+
+ //Keep info windows open on click
+ mapboxMap.getUiSettings().setDeselectMarkersOnTap(false);
+
+ //Add a marker
+ final MarkerView marker = addMarker(mapboxMap);
+ mapboxMap.selectMarker(marker);
+
+ //On map click, change the info window contents
+ mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
+ @Override
+ public void onMapClick(@NonNull LatLng point) {
+ //Distance from click to marker
+ double distanceKm = marker.getPosition().distanceTo(point) / 1000;
+
+ //Get the info window
+ 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();
+ }
+ }
+ });
+
+ //Focus on Paris
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(paris));
+ }
+ });
+ }
+
+ private MarkerView addMarker(MapboxMap mapboxMap) {
+ IconFactory iconFactory = IconFactory.getInstance(this);
+ Drawable iconDrawable = ContextCompat.getDrawable(this, R.drawable.ic_location_city_24dp);
+ iconDrawable.setColorFilter(getResources().getColor(R.color.mapbox_blue), PorterDuff.Mode.SRC_IN);
+
+ return mapboxMap.addMarker(
+ new MarkerViewOptions()
+ .position(paris)
+ .icon(iconFactory.fromDrawable(iconDrawable))
+ );
+ }
+
+ private void addCustomInfoWindowAdapter(final MapboxMap mapboxMap) {
+ 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.setPadding(padding, padding, padding, padding);
+
+ return textView;
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @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();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void setupActionBar() {
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java
index 7ac3c59667..7ff0acdb29 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java
@@ -8,7 +8,6 @@ import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
-
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
@@ -16,7 +15,6 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.maps.MapView;
-
import java.text.DecimalFormat;
public class InfoWindowActivity extends AppCompatActivity implements OnMapReadyCallback, MapboxMap.OnInfoWindowCloseListener, MapboxMap.OnMapLongClickListener, MapboxMap.OnInfoWindowClickListener, MapboxMap.OnInfoWindowLongClickListener {
@@ -61,6 +59,12 @@ public class InfoWindowActivity extends AppCompatActivity implements OnMapReadyC
.snippet("E St NW with 17th St NW")
.position(new LatLng(38.8954236, -77.0394623)));
+ mapboxMap.addMarker(new MarkerOptions().title("The Ellipse").position(new LatLng(38.89393, -77.03654)));
+
+ mapboxMap.addMarker(new MarkerOptions().position(new LatLng(38.89596, -77.03434)));
+
+ mapboxMap.addMarker(new MarkerOptions().snippet("Lafayette Square").position(new LatLng(38.89949, -77.03656)));
+
Marker marker = mapboxMap.addMarker(new MarkerOptions()
.title("White House")
.snippet("The official residence and principal workplace of the President of the United States, located at 1600 Pennsylvania Avenue NW in Washington, D.C. It has been the residence of every U.S. president since John Adams in 1800.")
@@ -82,6 +86,10 @@ public class InfoWindowActivity extends AppCompatActivity implements OnMapReadyC
mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(allowConcurrentInfoWindow);
}
+ private void toggleDeselectMarkersOnTap(boolean deselectMarkersOnTap) {
+ mapboxMap.getUiSettings().setDeselectMarkersOnTap(deselectMarkersOnTap);
+ }
+
@Override
public boolean onInfoWindowClick(@NonNull Marker marker) {
Toast.makeText(getApplicationContext(), "OnClick: " + marker.getTitle(), Toast.LENGTH_LONG).show();
@@ -157,6 +165,10 @@ public class InfoWindowActivity extends AppCompatActivity implements OnMapReadyC
toggleConcurrentInfoWindow(!item.isChecked());
item.setChecked(!item.isChecked());
return true;
+ case R.id.action_toggle_deselect_markers_on_tap:
+ toggleDeselectMarkersOnTap(!item.isChecked());
+ item.setChecked(!item.isChecked());
+ return true;
case android.R.id.home:
onBackPressed();
return true;
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 f5835ab7bd..760501c17c 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
@@ -15,7 +15,6 @@ 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.model.constants.AppConstant;
public class DebugModeActivity extends AppCompatActivity {
@@ -27,12 +26,12 @@ public class DebugModeActivity extends AppCompatActivity {
private int currentStyleIndex = 0;
private static final String[] STYLES = new String[]{
- Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION),
- Style.getOutdoorsStyleUrl(AppConstant.STYLE_VERSION),
- Style.getLightStyleUrl(AppConstant.STYLE_VERSION),
- Style.getDarkStyleUrl(AppConstant.STYLE_VERSION),
- Style.getSatelliteStyleUrl(AppConstant.STYLE_VERSION),
- Style.getSatelliteStreetsStyleUrl(AppConstant.STYLE_VERSION)
+ Style.MAPBOX_STREETS,
+ Style.OUTDOORS,
+ Style.LIGHT,
+ Style.DARK,
+ Style.SATELLITE,
+ Style.SATELLITE_STREETS
};
@Override
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 288817d670..a3517df205 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
@@ -23,7 +23,6 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.TrackingSettings;
import com.mapbox.mapboxsdk.maps.UiSettings;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
public class DoubleMapActivity extends AppCompatActivity {
@@ -73,7 +72,7 @@ public class DoubleMapActivity extends AppCompatActivity {
mMapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mapboxMap.setStyleUrl(Style.getDarkStyleUrl(AppConstant.STYLE_VERSION));
+ mapboxMap.setStyleUrl(Style.DARK);
mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(18));
try {
@@ -92,7 +91,7 @@ public class DoubleMapActivity extends AppCompatActivity {
mMapViewMini.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
- mapboxMap.setStyleUrl(Style.getLightStyleUrl(AppConstant.STYLE_VERSION));
+ mapboxMap.setStyleUrl(Style.LIGHT);
mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4));
UiSettings uiSettings = mapboxMap.getUiSettings();
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
new file mode 100644
index 0000000000..baa4142489
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java
@@ -0,0 +1,238 @@
+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.graphics.Color;
+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.MapboxMapOptions;
+import com.mapbox.mapboxsdk.testapp.R;
+
+public class NavigationDrawerActivity extends AppCompatActivity {
+
+ private boolean firstStyle = true;
+
+ @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:
+ 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());
+ FragmentManager fragmentManager = getFragmentManager();
+ fragmentManager.beginTransaction()
+ .replace(R.id.container, MapFragment.newInstance(options))
+ .commit();
+
+ 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 mDrawerToggle;
+
+ private DrawerLayout mDrawerLayout;
+ private ListView mDrawerListView;
+ private View mFragmentContainerView;
+
+ private int mCurrentSelectedPosition = 0;
+ private boolean mFromSavedInstanceState;
+ private boolean mUserLearnedDrawer;
+
+ public NavigationDrawerFragment() {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
+ mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
+
+ if (savedInstanceState != null) {
+ mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
+ mFromSavedInstanceState = true;
+ }
+ selectItem(mCurrentSelectedPosition);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mDrawerListView = (ListView) inflater.inflate(
+ R.layout.drawer_navigation_drawer, container, false);
+ mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ selectItem(position);
+ }
+ });
+ mDrawerListView.setAdapter(new ArrayAdapter<>(
+ inflater.getContext(),
+ android.R.layout.simple_list_item_activated_1,
+ android.R.id.text1,
+ new String[]{
+ getString(R.string.title_section1),
+ getString(R.string.title_section2)
+ }));
+ mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
+ return mDrawerListView;
+ }
+
+ /**
+ * 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) {
+ mFragmentContainerView = activity.findViewById(fragmentId);
+ mDrawerLayout = drawerLayout;
+// mDrawerLayout.setScrimColor(Color.TRANSPARENT);
+
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setHomeButtonEnabled(true);
+
+ mDrawerToggle = new ActionBarDrawerToggle(
+ activity,
+ mDrawerLayout,
+ 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 (!mUserLearnedDrawer) {
+ mUserLearnedDrawer = true;
+ SharedPreferences sp = PreferenceManager
+ .getDefaultSharedPreferences(getActivity());
+ sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply();
+ }
+ getActivity().invalidateOptionsMenu();
+ }
+ };
+
+ if (!mUserLearnedDrawer && !mFromSavedInstanceState) {
+ mDrawerLayout.openDrawer(mFragmentContainerView);
+ }
+ mDrawerLayout.post(new Runnable() {
+ @Override
+ public void run() {
+ mDrawerToggle.syncState();
+ }
+ });
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+ }
+
+ private void selectItem(int position) {
+ mCurrentSelectedPosition = position;
+ if (mDrawerListView != null) {
+ mDrawerListView.setItemChecked(position, true);
+ }
+ if (mDrawerLayout != null) {
+ mDrawerLayout.closeDrawer(mFragmentContainerView);
+ }
+
+ 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, mCurrentSelectedPosition);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (mDrawerToggle.onOptionsItemSelected(item)) {
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SurfaceViewMediaControlActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SurfaceViewMediaControlActivity.java
new file mode 100644
index 0000000000..4e193573dd
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SurfaceViewMediaControlActivity.java
@@ -0,0 +1,142 @@
+package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+public class SurfaceViewMediaControlActivity extends AppCompatActivity {
+
+ private MapView mMapView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_surfaceview_mediacontrols);
+
+ final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+
+ mMapView = (MapView) findViewById(R.id.mapView);
+ mMapView.onCreate(savedInstanceState);
+
+ // add another SurfaceView to the Layout
+ FrameLayout container = (FrameLayout) findViewById(R.id.container);
+ GLSurfaceView mediaControlSurfaceView = new ClearGLSurfaceView(this);
+ mediaControlSurfaceView.setZOrderMediaOverlay(true);
+ container.addView(mediaControlSurfaceView);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mMapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mMapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mMapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mMapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mMapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_padding, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ class ClearGLSurfaceView extends GLSurfaceView {
+
+ private ClearRenderer mRenderer;
+
+ public ClearGLSurfaceView(Context context) {
+ super(context);
+ mRenderer = new ClearRenderer();
+ setRenderer(mRenderer);
+ }
+
+ public boolean onTouchEvent(final MotionEvent event) {
+ queueEvent(new Runnable() {
+ public void run() {
+ mRenderer.setColor(event.getRawX() / getWidth(),
+ event.getRawY() / getHeight(), 1.0f);
+ }
+ });
+ return true;
+ }
+ }
+
+ class ClearRenderer implements GLSurfaceView.Renderer {
+
+ private float mRed;
+ private float mGreen;
+ private float mBlue;
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ // Do nothing special.
+ }
+
+ public void onSurfaceChanged(GL10 gl, int w, int h) {
+ gl.glViewport(0, 0, w, h);
+ }
+
+ public void onDrawFrame(GL10 gl) {
+ gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ }
+
+ public void setColor(float r, float g, float b) {
+ mRed = r;
+ mGreen = g;
+ mBlue = b;
+ }
+ }
+}
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/navigation/LocationPickerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/navigation/LocationPickerActivity.java
new file mode 100644
index 0000000000..7a6e425652
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/navigation/LocationPickerActivity.java
@@ -0,0 +1,467 @@
+package com.mapbox.mapboxsdk.testapp.activity.navigation;
+
+import android.Manifest;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.PointF;
+import android.location.Location;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+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.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.location.LocationListener;
+import com.mapbox.mapboxsdk.location.LocationServices;
+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.model.annotations.PulseMarkerView;
+import com.mapbox.mapboxsdk.testapp.model.annotations.PulseMarkerViewOptions;
+import com.mapbox.services.commons.ServicesException;
+import com.mapbox.services.commons.models.Position;
+import com.mapbox.services.geocoding.v5.GeocodingCriteria;
+import com.mapbox.services.geocoding.v5.MapboxGeocoding;
+import com.mapbox.services.geocoding.v5.models.GeocodingFeature;
+import com.mapbox.services.geocoding.v5.models.GeocodingResponse;
+
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+/**
+ * Sample Activity to show a typical location picker use case
+ */
+public class LocationPickerActivity extends AppCompatActivity {
+ private static final String TAG = "LocationPickerActivity";
+ private static final int REQUEST_PERMISSIONS = 101;
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ private ImageView dropPinView;
+ private Marker addressPin;
+ private ImageButton clearDisplayViewButton;
+ private MarkerView userMarker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_location_picker);
+
+ setupActionBar();
+
+ //Initialize map as normal
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+
+ //Create ui elements
+ createDropPin();
+ createSelectLocationButton();
+ createClearSelectionButton();
+
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap map) {
+ //Store for later
+ mapboxMap = map;
+
+ //Add user marker
+ mapboxMap.getMarkerViewManager().addMarkerViewAdapter(new PulseMarkerViewAdapter(LocationPickerActivity.this));
+ userMarker = createCustomUserMarker(new LatLng(0, 0));
+
+ //Fix the focal point to the center of the map
+ PointF focalPoint = new PointF((mapView.getX() + mapView.getWidth() / 2), (mapView.getY() + mapView.getHeight() / 2));
+ mapboxMap.getUiSettings().setFocalPoint(focalPoint);
+
+ //Track camera updates to animate the user location views
+ trackUserLocationView(userMarker);
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+
+ //Check permissions
+ if (arePermissionsGranted()) {
+ mapView.getMapAsync(new OnMapReadyCallback() {
+
+ @Override
+ public void onMapReady(final MapboxMap mapboxMap) {
+ //Get the user's location
+ final LocationServices locationServices = LocationServices.getLocationServices(getApplicationContext());
+
+ Location location = locationServices.getLastLocation();
+ if (location != null) {
+ zoomInOn(location);
+ userMarker.setPosition(new LatLng(location));
+ } else {
+ final ProgressDialog loadingDialog = ProgressDialog.show(LocationPickerActivity.this, "Loading", "Getting user location", false);
+ locationServices.addLocationListener(new LocationListener() {
+ @Override
+ public void onLocationChanged(@Nullable Location location) {
+ //Move the camera to the user
+ if (location != null) {
+ zoomInOn(location);
+ userMarker.setPosition(new LatLng(location));
+ locationServices.removeLocationListener(this);
+ loadingDialog.hide();
+ }
+ }
+ });
+ }
+
+ locationServices.toggleGPS(true);
+ }
+ });
+ }
+ }
+
+ private void zoomInOn(Location location) {
+ //Move the camera to the user
+ if (location != null) {
+ mapboxMap.setCameraPosition(new CameraPosition.Builder()
+ .target(new LatLng(location))
+ .zoom(16)
+ .bearing(0)
+ .tilt(0)
+ .build());
+ }
+ }
+
+
+ /**
+ * Tracks the camera to animate the marker when overlapping with the picker.
+ * Makes sure the marker actually points to the user's position by tracking it.
+ */
+ private void trackUserLocationView(final MarkerView markerView) {
+ final float circleDiameterSize = getResources().getDimension(R.dimen.circle_size);
+
+ //Track camera changes to check for overlap
+ mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
+
+ private Animation pulseAnimation;
+
+ @Override
+ public void onCameraChange(CameraPosition position) {
+ if (markerView == null) {
+ return;
+ }
+
+ //Make drop pin visible, if it wasn't already
+ showDropPin();
+
+ //Get the distance from the tip of the location picker to the MarkerView
+ double distance = getLocationPickerLocation().distanceTo(markerView.getPosition());
+
+ //If closeby, animate, otherwise, stop animation
+ View view = mapboxMap.getMarkerViewManager().getView(markerView);
+ if (view != null) {
+ View backgroundView = view.findViewById(R.id.background_imageview);
+ if (pulseAnimation == null && distance < 0.5f * circleDiameterSize) {
+ pulseAnimation = AnimationUtils.loadAnimation(LocationPickerActivity.this, R.anim.pulse);
+ pulseAnimation.setRepeatCount(Animation.INFINITE);
+ pulseAnimation.setRepeatMode(Animation.RESTART);
+ backgroundView.startAnimation(pulseAnimation);
+ } else if (pulseAnimation != null && distance >= 0.5f * circleDiameterSize) {
+ backgroundView.clearAnimation();
+ pulseAnimation = null;
+ }
+ }
+ }
+ });
+
+ //Track location updates to move the user marker
+ LocationServices.getLocationServices(getApplicationContext()).addLocationListener(new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ if (location != null && markerView != null) {
+ markerView.setPosition(new LatLng(location));
+ }
+ }
+ });
+ }
+
+ private MarkerView createCustomUserMarker(LatLng markerPosition) {
+ return mapboxMap.addMarker(new PulseMarkerViewOptions()
+ .icon(IconFactory.getInstance(getApplicationContext()).fromResource(R.drawable.ic_my_location_24dp))
+ .position(markerPosition)
+ );
+ }
+
+ private void createClearSelectionButton() {
+ clearDisplayViewButton = (ImageButton) findViewById(R.id.clearDisplayViewButton);
+ clearDisplayViewButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ removeAddressPin();
+ hide(clearDisplayViewButton);
+ showDropPin();
+ }
+ });
+ }
+
+ private void createSelectLocationButton() {
+ Button selectLocationButton = (Button) findViewById(R.id.selectLocationButton);
+ //noinspection ConstantConditions
+ selectLocationButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.i(TAG, "Location Selected!");
+ if (mapboxMap != null) {
+ //Control button's state
+ clearDisplayViewButton.setVisibility(View.VISIBLE);
+ dropPinView.setVisibility(View.INVISIBLE);
+
+ //Get position for the drop pin
+ LatLng position = getLocationPickerLocation();
+
+ //Show the address pin (result)
+ showAddressPin(position);
+
+ //Get the address for that location and update the marker
+ geocode(position, new GeocodeCallbacks() {
+ @Override
+ public void onResult(String result) {
+ updateAddressPin(result);
+ }
+
+ @Override
+ public void onFailure(Throwable failure) {
+ showFeedbackMessage("Could not retrieve address: " + failure.getMessage());
+ }
+ });
+ }
+ }
+ }
+ );
+ }
+
+ private void createDropPin() {
+ float density = getResources().getDisplayMetrics().density;
+
+ dropPinView = new ImageView(this);
+ dropPinView.setImageResource(R.drawable.ic_droppin_24dp);
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
+ params.bottomMargin = (int) (12 * density);
+ dropPinView.setLayoutParams(params);
+
+ mapView.addView(dropPinView);
+ }
+
+ private void showDropPin() {
+ if (dropPinView != null && dropPinView.getVisibility() != View.VISIBLE) {
+ dropPinView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void hide(View view) {
+ if (view != null) {
+ view.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private void setupActionBar() {
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ }
+
+ /**
+ * Get address for the given location
+ */
+ private void geocode(LatLng point, final GeocodeCallbacks callbacks) {
+ try {
+ //Create Geocoding client
+ MapboxGeocoding client = new MapboxGeocoding.Builder()
+ .setAccessToken(getString(R.string.mapbox_access_token))
+ .setCoordinates(Position.fromCoordinates(point.getLongitude(), point.getLatitude()))
+ .setType(GeocodingCriteria.TYPE_ADDRESS)
+ .build();
+
+ //Place the request
+ client.enqueueCall(new Callback<GeocodingResponse>() {
+ @Override
+ public void onResponse(Call<GeocodingResponse> call, Response<GeocodingResponse> response) {
+
+ List<GeocodingFeature> results = response.body().getFeatures();
+ String address = null;
+ if (results.size() > 0) {
+ GeocodingFeature feature = results.get(0);
+ address = feature.getAddress() + " " + feature.getText();
+ Log.i(TAG, "address " + address);
+ } else {
+ showFeedbackMessage("No results for search.");
+ }
+
+ callbacks.onResult(address);
+ }
+
+ @Override
+ public void onFailure(Call<GeocodingResponse> call, Throwable t) {
+ Log.e(TAG, "Geocoding Failure: " + t.getMessage());
+ callbacks.onFailure(t);
+ }
+ });
+ } catch (ServicesException e) {
+ Log.e(TAG, "Error geocoding: " + e.toString());
+ callbacks.onFailure(e);
+ }
+ }
+
+ private LatLng getLocationPickerLocation() {
+ return mapboxMap.getProjection().fromScreenLocation(
+ new PointF(dropPinView.getLeft() + (dropPinView.getWidth() / 2), dropPinView.getBottom())
+ );
+ }
+
+ private Marker showAddressPin(LatLng position) {
+ if (addressPin != null) {
+ //Remove previous pin
+ removeAddressPin();
+ }
+
+ //Create new one
+ addressPin = mapboxMap.addMarker(new MarkerViewOptions().title("Loading address...").position(position));
+ mapboxMap.selectMarker(addressPin);
+ return addressPin;
+ }
+
+ private void removeAddressPin() {
+ if (mapboxMap != null && addressPin != null) {
+ mapboxMap.removeMarker(addressPin);
+ }
+ }
+
+ private void updateAddressPin(@Nullable String address) {
+ if (addressPin != null) {
+ addressPin.setTitle(address == null ? "No address found" : address);
+ }
+ }
+
+ private void showFeedbackMessage(String message) {
+ Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
+ }
+
+ private boolean arePermissionsGranted() {
+ if (Build.VERSION.SDK_INT >= 23 &&
+ checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
+ checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ Log.i(TAG, "Requesting permissions");
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERMISSIONS);
+ return false;
+ }
+ Log.i(TAG, "Permissions already granted");
+ return true;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @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();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ /**
+ * Custom MarkerViewAdapter for the pulsing marker
+ */
+ private static class PulseMarkerViewAdapter extends MapboxMap.MarkerViewAdapter<PulseMarkerView> {
+
+ private LayoutInflater inflater;
+
+ public PulseMarkerViewAdapter(@NonNull Context context) {
+ super(context);
+ this.inflater = LayoutInflater.from(context);
+ }
+
+ @Nullable
+ @Override
+ public View getView(@NonNull PulseMarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
+ ViewHolder viewHolder;
+ if (convertView == null) {
+ viewHolder = new ViewHolder();
+ convertView = inflater.inflate(R.layout.view_pulse_marker, parent, false);
+ viewHolder.foregroundImageView = (ImageView) convertView.findViewById(R.id.foreground_imageView);
+ viewHolder.backgroundImageView = (ImageView) convertView.findViewById(R.id.background_imageview);
+ convertView.setTag(viewHolder);
+ }
+ return convertView;
+ }
+
+ private static class ViewHolder {
+ ImageView foregroundImageView;
+ ImageView backgroundImageView;
+ }
+ }
+
+ private interface GeocodeCallbacks {
+ void onResult(String result);
+
+ void onFailure(Throwable failure);
+ }
+}
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 fde46b1cab..8819168478 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,6 +12,7 @@ import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
+
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.Style;
@@ -26,10 +27,11 @@ import com.mapbox.mapboxsdk.offline.OfflineRegionError;
import com.mapbox.mapboxsdk.offline.OfflineRegionStatus;
import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
import com.mapbox.mapboxsdk.testapp.model.other.OfflineDownloadRegionDialog;
import com.mapbox.mapboxsdk.testapp.model.other.OfflineListRegionsDialog;
+
import org.json.JSONObject;
+
import java.util.ArrayList;
public class OfflineActivity extends AppCompatActivity
@@ -74,7 +76,7 @@ public class OfflineActivity extends AppCompatActivity
// Set up map
mMapView = (MapView) findViewById(R.id.mapView);
- mMapView.setStyleUrl(Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION));
+ mMapView.setStyleUrl(Style.MAPBOX_STREETS);
mMapView.onCreate(savedInstanceState);
mMapView.getMapAsync(new OnMapReadyCallback() {
@Override
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
new file mode 100644
index 0000000000..4e0f5f8c41
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java
@@ -0,0 +1,366 @@
+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.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+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.style.layers.FillLayer;
+import com.mapbox.mapboxsdk.style.layers.Function;
+import com.mapbox.mapboxsdk.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.layers.LineLayer;
+import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
+import com.mapbox.mapboxsdk.style.layers.Property;
+import com.mapbox.mapboxsdk.style.layers.PropertyValue;
+import com.mapbox.mapboxsdk.style.layers.RasterLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.style.sources.RasterSource;
+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 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 static com.mapbox.mapboxsdk.style.layers.Filter.*;
+import static com.mapbox.mapboxsdk.style.layers.Function.*;
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+/**
+ * Sample Activity to show a typical location picker use case
+ */
+public class RuntimeStyleActivity extends AppCompatActivity {
+ private static final String TAG = RuntimeStyleActivity.class.getSimpleName();
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_runtime_style);
+
+ setupActionBar();
+
+ //Initialize map as normal
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+
+
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap map) {
+ //Store for later
+ mapboxMap = map;
+
+ //Center and Zoom (Amsterdam, zoomed to streets)
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14));
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_runtime_style, menu);
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @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();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ case R.id.action_water_color:
+ setWaterColor();
+ return true;
+ case R.id.action_background_opacity:
+ setBackgroundOpacity();
+ return true;
+ case R.id.action_road_avoid_edges:
+ setRoadSymbolPlacement();
+ return true;
+ case R.id.action_layer_visibility:
+ setLayerInvisible();
+ return true;
+ case R.id.action_remove_layer:
+ removeBuildings();
+ return true;
+ case R.id.action_add_parks_layer:
+ addParksLayer();
+ return true;
+ case R.id.action_add_terrain_layer:
+ addTerrainLayer();
+ return true;
+ case R.id.action_add_satellite_layer:
+ addSatelliteLayer();
+ return true;
+ case R.id.action_update_water_color_on_zoom:
+ updateWaterColorOnZoom();
+ return true;
+ case R.id.action_add_custom_tiles:
+ addCustomTileSource();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void setLayerInvisible() {
+ String[] roadLayers = new String[]{"water"};
+ for (String roadLayer : roadLayers) {
+ Layer layer = mapboxMap.getLayer(roadLayer);
+ if (layer != null) {
+ layer.setProperties(visibility(VISIBLE));
+ }
+ }
+ }
+
+ private void setRoadSymbolPlacement() {
+ //Zoom so that the labels are visible first
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(14), new DefaultCallback() {
+ @Override
+ public void onFinish() {
+ String[] roadLayers = new String[]{"road-label-small", "road-label-medium", "road-label-large"};
+ for (String roadLayer : roadLayers) {
+ Layer layer = mapboxMap.getLayer(roadLayer);
+ if (layer != null) {
+ layer.setProperties(symbolPlacement(SYMBOL_PLACEMENT_POINT));
+ }
+ }
+ }
+ });
+ }
+
+ private void setBackgroundOpacity() {
+ Layer background = mapboxMap.getLayer("background");
+ if (background != null) {
+ background.setProperties(backgroundOpacity(0.2f));
+ }
+ }
+
+ private void setWaterColor() {
+ Layer water = mapboxMap.getLayer("water");
+ if (water != null) {
+ water.setProperties(
+ visibility(VISIBLE),
+ fillColor(Color.RED)
+ );
+ } else {
+ Toast.makeText(RuntimeStyleActivity.this, "No water layer in this style", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void removeBuildings() {
+ //Zoom to see buildings first
+ try {
+ mapboxMap.removeLayer("building");
+ } catch (NoSuchLayerException e) {
+ Toast.makeText(RuntimeStyleActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void addParksLayer() {
+ //Add a source
+ Source source;
+ try {
+ source = new GeoJsonSource("amsterdam-spots", readRawResource(R.raw.amsterdam));
+ } catch (IOException e) {
+ Toast.makeText(RuntimeStyleActivity.this, "Couldn't add source: " + e.getMessage(), Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ mapboxMap.addSource(source);
+
+ FillLayer layer = new FillLayer("parksLayer", "amsterdam-spots");
+ layer.setProperties(
+ fillColor(Color.RED),
+ fillOutlineColor(Color.BLUE),
+ fillOpacity(0.3f),
+ fillAntialias(true)
+ );
+
+ //Only show me parks
+ layer.setFilter(eq("type", "park"));
+
+ mapboxMap.addLayer(layer, "building");
+ //layer.setPaintProperty(fillColor(Color.RED)); //XXX But not after the object is attached
+
+ //Or get the object later and set it. It's all good.
+ mapboxMap.getLayer("parksLayer").setProperties(fillColor(Color.RED));
+
+ //You can get a typed layer, if you're sure it's of that type. Use with care
+ layer = mapboxMap.getLayerAs("parksLayer");
+ //And get some properties
+ PropertyValue<Boolean> fillAntialias = layer.getFillAntialias();
+ Log.d(TAG, "Fill anti alias: " + fillAntialias.getValue());
+ layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP));
+ PropertyValue<String> fillTranslateAnchor = layer.getFillTranslateAnchor();
+ Log.d(TAG, "Fill translate anchor: " + fillTranslateAnchor.getValue());
+ PropertyValue<String> visibility = layer.getVisibility();
+ Log.d(TAG, "Visibility: " + visibility.getValue());
+
+ //Get a good look at it all
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(12));
+ }
+
+ private void addTerrainLayer() {
+ //Add a source
+ Source source = new VectorSource("my-terrain-source", "mapbox://mapbox.mapbox-terrain-v2");
+ mapboxMap.addSource(source);
+
+ LineLayer layer = new LineLayer("terrainLayer", "my-terrain-source");
+ layer.setSourceLayer("contour");
+ layer.setProperties(
+ lineJoin(Property.LINE_JOIN_ROUND),
+ lineCap(Property.LINE_CAP_ROUND),
+ lineColor(Color.RED),
+ lineWidth(20f)
+ );
+
+ mapboxMap.addLayer(layer);
+
+ //Need to get a fresh handle
+ layer = mapboxMap.getLayerAs("terrainLayer");
+
+ //Make sure it's also applied after the fact
+ layer.setMinZoom(10);
+ layer.setMaxZoom(15);
+
+ layer = (LineLayer) mapboxMap.getLayer("terrainLayer");
+ Toast.makeText(this, String.format("Set min/max zoom to %s - %s", layer.getMinZoom(), layer.getMaxZoom()), Toast.LENGTH_SHORT).show();
+ }
+
+ private void addSatelliteLayer() {
+ //Add a source
+ Source source = new RasterSource("my-raster-source", "mapbox://mapbox.satellite").withTileSize(512);
+ mapboxMap.addSource(source);
+
+ //Add a layer
+ mapboxMap.addLayer(new RasterLayer("satellite-layer", "my-raster-source"));
+ }
+
+ private void updateWaterColorOnZoom() {
+ FillLayer layer = mapboxMap.getLayerAs("water");
+ if (layer == null) {
+ return;
+ }
+
+ //Set a zoom function to update the color of the water
+ layer.setProperties(fillColor(zoom(0.8f,
+ stop(1, fillColor(Color.GREEN)),
+ stop(4, fillColor(Color.BLUE)),
+ stop(12, fillColor(Color.RED)),
+ stop(20, fillColor(Color.BLACK))
+ )));
+
+ //do some animations to show it off properly
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(1), 1500);
+
+ PropertyValue<String> fillColor = layer.getFillColor();
+ Function<String> function = fillColor.getFunction();
+ Log.d(TAG, "Fill color base: " + function.getBase());
+ Log.d(TAG, "Fill color #stops: " + function.getStops().length);
+ }
+
+ 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 n;
+ while ((n = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, n);
+ }
+ } finally {
+ is.close();
+ }
+
+ return writer.toString();
+ }
+
+ private void setupActionBar() {
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ }
+
+ 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"));
+ mapboxMap.addSource(source);
+
+ //Add a layer
+ mapboxMap.addLayer(
+ new FillLayer("custom-tile-layers", "custom-tile-source")
+ .withSourceLayer("water")
+ );
+ }
+
+ private static class DefaultCallback implements MapboxMap.CancelableCallback {
+
+ @Override
+ public void onCancel() {
+ //noop
+ }
+
+ @Override
+ public void onFinish() {
+ //noop
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java
new file mode 100644
index 0000000000..6f04852ca1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java
@@ -0,0 +1,69 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+
+/**
+ * Test activity for unit test execution
+ */
+public class RuntimeStyleTestActivity extends AppCompatActivity {
+ private static final String TAG = RuntimeStyleTestActivity.class.getSimpleName();
+
+ public MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_runtime_style);
+
+ //Initialize map as normal
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ RuntimeStyleTestActivity.this.mapboxMap = mapboxMap;
+ }
+ });
+ }
+
+ public MapboxMap getMapboxMap() {
+ return mapboxMap;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @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/style/RuntimeStyleTimingTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java
new file mode 100644
index 0000000000..9463cfcf08
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java
@@ -0,0 +1,87 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.CircleLayer;
+import com.mapbox.mapboxsdk.style.sources.VectorSource;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+/**
+ * Test activity for unit test execution
+ */
+public class RuntimeStyleTimingTestActivity extends AppCompatActivity {
+ private static final String TAG = RuntimeStyleTimingTestActivity.class.getSimpleName();
+
+ public MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_runtime_style);
+
+ //Initialize map as normal
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ RuntimeStyleTimingTestActivity.this.mapboxMap = mapboxMap;
+ VectorSource museums = new VectorSource("museums_source", "mapbox://mapbox.2opop9hr");
+ mapboxMap.addSource(museums);
+
+ CircleLayer museumsLayer = new CircleLayer("museums", "museums_source");
+ museumsLayer.setSourceLayer("museum-cusco");
+ museumsLayer.setProperties(
+ visibility(VISIBLE),
+ circleRadius(8f),
+ circleColor(Color.argb(1, 55, 148, 179))
+ );
+
+ mapboxMap.addLayer(museumsLayer);
+ }
+ });
+ }
+
+ public MapboxMap getMapboxMap() {
+ return mapboxMap;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @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/userlocation/MyLocationDrawableActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
index a4a283907e..d630ee6d21 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
@@ -1,9 +1,14 @@
package com.mapbox.mapboxsdk.testapp.activity.userlocation;
+import android.Manifest;
+import android.content.pm.PackageManager;
import android.graphics.Color;
import android.location.Location;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
@@ -11,6 +16,7 @@ import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.Style;
@@ -22,10 +28,11 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
public class MyLocationDrawableActivity extends AppCompatActivity implements LocationListener {
+ private static final int PERMISSIONS_LOCATION = 0;
+
private MapView mapView;
private MapboxMap mapboxMap;
private Location location;
@@ -45,20 +52,13 @@ public class MyLocationDrawableActivity extends AppCompatActivity implements Loc
}
findViewById(R.id.progress).setVisibility(View.GONE);
- location = LocationServices.getLocationServices(this).getLastLocation();
MapboxMapOptions mapboxMapOptions = new MapboxMapOptions();
mapboxMapOptions.accessToken(getString(R.string.mapbox_access_token));
- mapboxMapOptions.styleUrl(Style.getMapboxStreetsUrl(AppConstant.STYLE_VERSION));
- mapboxMapOptions.locationEnabled(true);
- mapboxMapOptions.camera(new CameraPosition.Builder()
- .target(location != null ? new LatLng(location) : new LatLng(0, 0))
- .zoom(11)
- .tilt(25)
- .build());
-
- mapboxMapOptions.myLocationForegroundDrawables(ContextCompat.getDrawable(this, R.drawable.ic_chelsea),
- ContextCompat.getDrawable(this, R.drawable.ic_chelsea));
+ mapboxMapOptions.styleUrl(Style.MAPBOX_STREETS);
+
+ // configure MyLocationView drawables
+ mapboxMapOptions.myLocationForegroundDrawable(ContextCompat.getDrawable(this, R.drawable.ic_chelsea));
mapboxMapOptions.myLocationBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.ic_arsenal));
mapboxMapOptions.myLocationForegroundTintColor(Color.GREEN);
mapboxMapOptions.myLocationBackgroundTintColor(Color.YELLOW);
@@ -78,17 +78,53 @@ public class MyLocationDrawableActivity extends AppCompatActivity implements Loc
@Override
public void onMapReady(MapboxMap map) {
mapboxMap = map;
+ toggleGps(true);
}
});
+ }
+
+ public void toggleGps(boolean enableGps) {
+ if (enableGps) {
+ if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) ||
+ (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_LOCATION);
+ } else {
+ enableLocation(true);
+ }
+ } else {
+ enableLocation(false);
+ }
+ }
- LocationServices.getLocationServices(this).addLocationListener(this);
+ private void enableLocation(boolean enabled) {
+ if (enabled) {
+ mapboxMap.setMyLocationEnabled(true);
+ Location location = mapboxMap.getMyLocation();
+ if (location != null) {
+ onLocationChanged(location);
+ }else{
+ LocationServices.getLocationServices(this).addLocationListener(this);
+ }
+ } else {
+ mapboxMap.setMyLocationEnabled(false);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+ switch (requestCode) {
+ case PERMISSIONS_LOCATION: {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ enableLocation(true);
+ }
+ }
+ }
}
@Override
public void onLocationChanged(Location location) {
- if (mapboxMap != null && firstRun) {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 10));
- firstRun = false;
+ if (mapboxMap != null) {
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 14));
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
index fcd61e70b4..273b5134c3 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
@@ -1,10 +1,15 @@
package com.mapbox.mapboxsdk.testapp.activity.userlocation;
+import android.Manifest;
+import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
@@ -33,6 +38,7 @@ public class MyLocationTrackingModeActivity extends AppCompatActivity implements
private MapboxMap mMapboxMap;
private Spinner mLocationSpinner, mBearingSpinner;
private Location mLocation;
+ private static final int PERMISSIONS_LOCATION = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -57,8 +63,9 @@ public class MyLocationTrackingModeActivity extends AppCompatActivity implements
mMapboxMap = mapboxMap;
// disable dismissal when a gesture occurs
- mMapboxMap.getTrackingSettings().setDismissLocationTrackingOnGesture(false);
- mMapboxMap.getTrackingSettings().setDismissBearingTrackingOnGesture(false);
+ TrackingSettings trackingSettings = mapboxMap.getTrackingSettings();
+ trackingSettings.setDismissLocationTrackingOnGesture(false);
+ trackingSettings.setDismissBearingTrackingOnGesture(false);
mapboxMap.setOnMyLocationChangeListener(MyLocationTrackingModeActivity.this);
@@ -96,13 +103,46 @@ public class MyLocationTrackingModeActivity extends AppCompatActivity implements
}
});
+ toggleGps(!mapboxMap.isMyLocationEnabled());
+ }
+ });
+ }
+
+ @UiThread
+ public void toggleGps(boolean enableGps) {
+ if (enableGps) {
+ if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) ||
+ (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_LOCATION);
+ } else {
+ enableLocation(true);
+ }
+ } else {
+ enableLocation(false);
+ }
+ }
- mLocation = LocationServices.getLocationServices(MyLocationTrackingModeActivity.this).getLastLocation();
- if(mLocation!=null){
- setInitialPosition(new LatLng(mLocation));
+ private void enableLocation(boolean enabled) {
+ if (enabled) {
+ mMapboxMap.setMyLocationEnabled(true);
+ Location location = mMapboxMap.getMyLocation();
+ if (location != null) {
+ setInitialPosition(new LatLng(location));
+ }
+ } else {
+ mMapboxMap.setMyLocationEnabled(false);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+ switch (requestCode) {
+ case PERMISSIONS_LOCATION: {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ enableLocation(true);
}
}
- });
+ }
}
private void setInitialPosition(LatLng latLng){
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java
index c4ef4a8d13..a1df434ebc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java
@@ -1,8 +1,6 @@
package com.mapbox.mapboxsdk.testapp.model.annotations;
import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.PointF;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,9 +25,9 @@ public class CountryMarkerViewOptions extends BaseMarkerViewOptions<CountryMarke
flat(in.readByte() != 0);
anchor(in.readFloat(), in.readFloat());
infoWindowAnchor(in.readFloat(), in.readFloat());
- selectAnimatorResource(in.readInt());
- deselectAnimatorResource(in.readInt());
- rotation(in.readInt());
+ rotation(in.readFloat());
+ visible(in.readByte() != 0);
+ alpha(in.readFloat());
if (in.readByte() != 0) {
// this means we have an icon
String iconId = in.readString();
@@ -61,9 +59,9 @@ public class CountryMarkerViewOptions extends BaseMarkerViewOptions<CountryMarke
out.writeFloat(getAnchorV());
out.writeFloat(getInfoWindowAnchorU());
out.writeFloat(getInfoWindowAnchorV());
- out.writeInt(getSelectAnimRes());
- out.writeInt(getDeselectAnimRes());
- out.writeInt(getRotation());
+ out.writeFloat(getRotation());
+ out.writeByte((byte) (isVisible() ? 1 : 0));
+ out.writeFloat(getAlpha());
Icon icon = getIcon();
out.writeByte((byte) (icon != null ? 1 : 0));
if (icon != null) {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerView.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerView.java
new file mode 100644
index 0000000000..48867ea5eb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerView.java
@@ -0,0 +1,11 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+
+public class PulseMarkerView extends MarkerView {
+
+ public PulseMarkerView(BaseMarkerViewOptions baseMarkerViewOptions) {
+ super(baseMarkerViewOptions);
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java
new file mode 100644
index 0000000000..70ff6a22e2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java
@@ -0,0 +1,79 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+public class PulseMarkerViewOptions extends BaseMarkerViewOptions<PulseMarkerView, PulseMarkerViewOptions> {
+
+ public PulseMarkerViewOptions() {
+ }
+
+ protected PulseMarkerViewOptions(Parcel in) {
+ position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
+ snippet(in.readString());
+ title(in.readString());
+ flat(in.readByte() != 0);
+ anchor(in.readFloat(), in.readFloat());
+ selected = in.readByte() != 0;
+ rotation(in.readFloat());
+ if (in.readByte() != 0) {
+ // this means we have an icon
+ String iconId = in.readString();
+ Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ Icon icon = IconFactory.recreate(iconId, iconBitmap);
+ icon(icon);
+ }
+ }
+
+ @Override
+ public PulseMarkerViewOptions getThis() {
+ return this;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(getPosition(), flags);
+ out.writeString(getSnippet());
+ out.writeString(getTitle());
+ out.writeByte((byte) (isFlat() ? 1 : 0));
+ out.writeFloat(getAnchorU());
+ out.writeFloat(getAnchorV());
+ out.writeFloat(getInfoWindowAnchorU());
+ out.writeFloat(getInfoWindowAnchorV());
+ out.writeByte((byte) (selected ? 1 : 0));
+ out.writeFloat(getRotation());
+ Icon icon = getIcon();
+ out.writeByte((byte) (icon != null ? 1 : 0));
+ if (icon != null) {
+ out.writeString(getIcon().getId());
+ out.writeParcelable(getIcon().getBitmap(), flags);
+ }
+ }
+
+ @Override
+ public PulseMarkerView getMarker() {
+ return new PulseMarkerView(this);
+ }
+
+ public static final Parcelable.Creator<CountryMarkerViewOptions> CREATOR
+ = new Parcelable.Creator<CountryMarkerViewOptions>() {
+ public CountryMarkerViewOptions createFromParcel(Parcel in) {
+ return new CountryMarkerViewOptions(in);
+ }
+
+ public CountryMarkerViewOptions[] newArray(int size) {
+ return new CountryMarkerViewOptions[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerView.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerView.java
new file mode 100644
index 0000000000..c0a589cb57
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerView.java
@@ -0,0 +1,18 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+
+public class TextMarkerView extends MarkerView {
+
+ private String text;
+
+ public TextMarkerView(BaseMarkerViewOptions baseMarkerViewOptions, String text) {
+ super(baseMarkerViewOptions);
+ this.text = text;
+ }
+
+ public String getText() {
+ return text;
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerViewOptions.java
new file mode 100644
index 0000000000..a8622e9790
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerViewOptions.java
@@ -0,0 +1,92 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+public class TextMarkerViewOptions extends BaseMarkerViewOptions<TextMarkerView, TextMarkerViewOptions> {
+
+ private String text;
+
+ public TextMarkerViewOptions() {
+ }
+
+ protected TextMarkerViewOptions(Parcel in) {
+ position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
+ snippet(in.readString());
+ title(in.readString());
+ flat(in.readByte() != 0);
+ anchor(in.readFloat(), in.readFloat());
+ infoWindowAnchor(in.readFloat(), in.readFloat());
+ rotation(in.readFloat());
+ visible(in.readByte() != 0);
+ alpha(in.readFloat());
+ if (in.readByte() != 0) {
+ // this means we have an icon
+ String iconId = in.readString();
+ Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ Icon icon = IconFactory.recreate(iconId, iconBitmap);
+ icon(icon);
+ }
+ text(in.readString());
+ }
+
+ @Override
+ public TextMarkerViewOptions getThis() {
+ return this;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(getPosition(), flags);
+ out.writeString(getSnippet());
+ out.writeString(getTitle());
+ out.writeByte((byte) (isFlat() ? 1 : 0));
+ out.writeFloat(getAnchorU());
+ out.writeFloat(getAnchorV());
+ out.writeFloat(getInfoWindowAnchorU());
+ out.writeFloat(getInfoWindowAnchorV());
+ out.writeFloat(getRotation());
+ out.writeByte((byte) (isVisible() ? 1 : 0));
+ out.writeFloat(alpha);
+ Icon icon = getIcon();
+ out.writeByte((byte) (icon != null ? 1 : 0));
+ if (icon != null) {
+ out.writeString(getIcon().getId());
+ out.writeParcelable(getIcon().getBitmap(), flags);
+ }
+ out.writeString(text);
+ }
+
+ @Override
+ public TextMarkerView getMarker() {
+ return new TextMarkerView(this, text);
+ }
+
+ public TextMarkerViewOptions text(String text) {
+ this.text = text;
+ return getThis();
+ }
+
+ public static final Parcelable.Creator<CountryMarkerViewOptions> CREATOR
+ = new Parcelable.Creator<CountryMarkerViewOptions>() {
+ public CountryMarkerViewOptions createFromParcel(Parcel in) {
+ return new CountryMarkerViewOptions(in);
+ }
+
+ public CountryMarkerViewOptions[] newArray(int size) {
+ return new CountryMarkerViewOptions[size];
+ }
+ };
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/anim/pulse.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/anim/pulse.xml
new file mode 100644
index 0000000000..40bc57ab68
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/anim/pulse.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/decelerate_interpolator">
+
+ <scale
+ android:duration="1000"
+ android:fromXScale="1"
+ android:fromYScale="1"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:repeatCount="infinite"
+ android:repeatMode="restart"
+ android:toXScale="1.8"
+ android:toYScale="1.8"/>
+
+ <set android:startOffset="200">
+ <alpha
+ android:duration="800"
+ android:fromAlpha="1.0"
+ android:repeatCount="infinite"
+ android:repeatMode="restart"
+ android:toAlpha="0.0"/>
+ </set>
+</set> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/ic_drawer.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/ic_drawer.png
new file mode 100644
index 0000000000..c59f601ca3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/ic_drawer.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/icon.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/icon.png
index c9d3f3d2d0..ac2ea61c73 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/icon.png
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/ic_drawer.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/ic_drawer.png
new file mode 100644
index 0000000000..1ed2c56ee4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/ic_drawer.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/icon.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/icon.png
index 235a854d71..99eed7146c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/icon.png
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/ic_drawer.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/ic_drawer.png
new file mode 100644
index 0000000000..a5fa74def4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/ic_drawer.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/icon.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/icon.png
index c9d3f3d2d0..9b084daf91 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/icon.png
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/icon.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_drawer.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_drawer.png
new file mode 100644
index 0000000000..9c4685d6e0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_drawer.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/icon.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/icon.png
index 8667ce6c44..6fa714b47d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/icon.png
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/icon.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_car_top.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_car_top.png
new file mode 100644
index 0000000000..ca7590137b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_car_top.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_taxi_top.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_taxi_top.png
new file mode 100644
index 0000000000..09f84fd9cb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_taxi_top.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/icon.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/icon.png
index df0ca2e83d..77289b5d0a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/icon.png
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/icon.png
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml
new file mode 100644
index 0000000000..006b2ce5a8
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml
@@ -0,0 +1,12 @@
+<?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> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_clear_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_clear_24dp.xml
new file mode 100644
index 0000000000..1e2d044bee
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_clear_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="#FFFFFF"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_car_black_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_car_black_24dp.xml
new file mode 100644
index 0000000000..6d6337c3ab
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_car_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="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zM17.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11L5,11z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_run_black_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_run_black_24dp.xml
new file mode 100644
index 0000000000..dfa43f020e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_run_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="32dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.49,5.48c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM9.89,19.38l1,-4.4 2.1,2v6h2v-7.5l-2.1,-2 0.6,-3c1.3,1.5 3.3,2.5 5.5,2.5v-2c-1.9,0 -3.5,-1 -4.3,-2.4l-1,-1.6c-0.4,-0.6 -1,-1 -1.7,-1 -0.3,0 -0.5,0.1 -0.8,0.1l-5.2,2.2v4.7h2v-3.4l1.8,-0.7 -1.6,8.1 -4.9,-1 -0.4,2 7,1.4z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_print_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_print_24dp.xml
new file mode 100644
index 0000000000..7a9bc00287
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_print_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="#FFFFFF"
+ android:pathData="M19,8L5,8c-1.66,0 -3,1.34 -3,3v6h4v4h12v-4h4v-6c0,-1.66 -1.34,-3 -3,-3zM16,19L8,19v-5h8v5zM19,12c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,3L6,3v4h12L18,3z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml
index 8c025d999c..b9bfa701a8 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml
@@ -1,8 +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"
android:layout_height="match_parent"
- xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
@@ -16,7 +16,10 @@
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_below="@id/toolbar"
+ app:center_latitude="51.502615"
+ app:center_longitude="4.972326"
app:style_url="@string/style_light"
- android:layout_below="@id/toolbar" />
+ app:zoom="6" />
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_infowindow_adapter_dynamic.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_infowindow_adapter_dynamic.xml
new file mode 100644
index 0000000000..ccced6bbff
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_infowindow_adapter_dynamic.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout 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">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:style_url="@string/style_mapbox_streets"
+ app:center_latitude="47.798202"
+ app:center_longitude="7.573781"
+ app:zoom="4" />
+
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_picker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_picker.xml
new file mode 100644
index 0000000000..f9fe77bfec
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_picker.xml
@@ -0,0 +1,41 @@
+<?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.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/toolbar"/>
+
+ <ImageButton
+ android:id="@+id/clearDisplayViewButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_clear_24dp"
+ android:background="@color/accent"
+ android:visibility="gone"
+ android:layout_marginLeft="@dimen/full_button_margin"
+ android:layout_above="@+id/selectLocationButton"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"/>
+
+ <Button
+ android:id="@+id/selectLocationButton"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/full_button_margin"
+ android:background="@color/primary"
+ android:text="@string/navigation_select_location_button_text"
+ android:textColor="@android:color/white"
+ android:layout_alignParentBottom="true"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view_scale.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view_scale.xml
new file mode 100644
index 0000000000..c59f41539c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view_scale.xml
@@ -0,0 +1,60 @@
+<?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.support.v7.widget.Toolbar
+ android:id="@id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:paddingBottom="8dp"
+ android:textSize="20sp"
+ android:textColor="#FFFFFF"
+ android:text="Scaling in the View Marker API" />
+
+ <TextView
+ android:id="@+id/textview_factor"
+ android:layout_alignBottom="@+id/seekbar_factor"
+ android:layout_below="@id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Scale: 1" />
+
+ <SeekBar
+ android:id="@+id/seekbar_factor"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title"
+ android:layout_marginLeft="56dp"
+ android:progress="0" />
+
+ </RelativeLayout>
+
+
+ </android.support.v7.widget.Toolbar>
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/toolbar"
+ app:center_latitude="38.907192"
+ app:center_longitude="-77.036871"
+ app:style_url="@string/style_mapbox_streets"
+ app:zoom="12" />
+
+</RelativeLayout>
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
new file mode 100644
index 0000000000..2bf08fbdc1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml
@@ -0,0 +1,33 @@
+<?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> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml
index 9378900dc3..86a97017b9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml
@@ -1,8 +1,9 @@
<?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">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
@@ -15,6 +16,13 @@
android:id="@+id/pressForMarkerMapView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ app:center_latitude="45.1855569"
+ app:center_longitude="5.7215506"
+ app:zoom="11"
+ app:style_url="@string/style_mapbox_streets"
+ app:logo_gravity="top|end"
+ app:attribution_gravity="top|end"
+ app:logo_margin_right="10dp"
android:layout_below="@id/toolbar"/>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_print.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_print.xml
new file mode 100644
index 0000000000..10e312e8c6
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_print.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout 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">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+ </android.support.v7.widget.Toolbar>
+
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.design.widget.CoordinatorLayout
+ 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" />
+
+ <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_print_24dp"
+ app:backgroundTint="@color/accent"/>
+
+ </android.support.design.widget.CoordinatorLayout>
+
+ </FrameLayout>
+
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_runtime_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_runtime_style.xml
new file mode 100644
index 0000000000..4c0d067c14
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_runtime_style.xml
@@ -0,0 +1,19 @@
+<?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.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/toolbar"/>
+
+</RelativeLayout>
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 b9701b53e0..ac69780592 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
@@ -29,8 +29,7 @@
android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1"
- app:style_url="@string/style_emerald" />
+ android:layout_weight="1"/>
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_surfaceview_mediacontrols.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_surfaceview_mediacontrols.xml
new file mode 100644
index 0000000000..15394f8f33
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_surfaceview_mediacontrols.xml
@@ -0,0 +1,31 @@
+<?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">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/toolbar"
+ app:style_url="@string/style_light" />
+
+ <FrameLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="128dp"
+ android:layout_alignParentBottom="true"
+ android:paddingBottom="48dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp" />
+
+</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
new file mode 100644
index 0000000000..9bb5560bb2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml
@@ -0,0 +1,30 @@
+<?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">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/viewpager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/toolbar">
+
+ <android.support.v4.view.PagerTabStrip
+ android:id="@+id/viewpager_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingBottom="4dp"
+ android:paddingTop="4dp" />
+
+ </android.support.v4.view.ViewPager>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/drawer_navigation_drawer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/drawer_navigation_drawer.xml
new file mode 100644
index 0000000000..e04a162d49
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/drawer_navigation_drawer.xml
@@ -0,0 +1,7 @@
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/navigation_drawer_width"
+ android:layout_height="match_parent"
+ android:background="#cccc"
+ android:choiceMode="singleChoice"
+ android:divider="@android:color/transparent"
+ android:dividerHeight="0dp" />
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 08caf1df66..98050c061f 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
@@ -14,6 +14,8 @@
android:textColor="@android:color/white"
android:layout_height="wrap_content"
android:textStyle="bold"
- android:layout_centerInParent="true" />
+ android:layout_alignBottom="@id/imageView"
+ android:layout_centerHorizontal="true"
+ android:padding="2dp"/>
</RelativeLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_pulse_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_pulse_marker.xml
new file mode 100644
index 0000000000..e1ac50b440
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_pulse_marker.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="48dp"
+ android:layout_height="48dp">
+
+ <ImageView
+ android:id="@+id/foreground_imageView"
+ android:layout_width="@dimen/circle_size"
+ android:layout_height="@dimen/circle_size"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_circle"/>
+
+ <ImageView
+ android:id="@+id/background_imageview"
+ android:layout_width="@dimen/circle_size"
+ android:layout_height="@dimen/circle_size"
+ android:layout_gravity="center"
+ android:alpha="0.5"
+ android:src="@drawable/ic_circle"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml
index 583b760d7c..adca8d2e00 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml
@@ -8,6 +8,12 @@
android:id="@+id/action_toggle_concurrent_infowindow"
app:showAsAction="never"
android:checkable="true"/>
+ <item
+ android:title="@string/menuitem_title_deselect_markers_on_tap"
+ android:id="@+id/action_toggle_deselect_markers_on_tap"
+ app:showAsAction="never"
+ android:checkable="true"
+ android:checked="true"/>
</group>
</menu> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polygon.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polygon.xml
new file mode 100644
index 0000000000..62d4c83594
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polygon.xml
@@ -0,0 +1,20 @@
+<?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">
+ <item
+ android:id="@+id/action_id_alpha"
+ android:title="@string/action_alpha_polygon"
+ mapbox:showAsAction="never" />
+ <item
+ android:id="@+id/action_id_visible"
+ android:title="@string/action_visibility_polygon"
+ mapbox:showAsAction="never" />
+ <item
+ android:id="@+id/action_id_points"
+ android:title="@string/action_points_polygon"
+ mapbox:showAsAction="never" />
+ <item
+ android:id="@+id/action_id_color"
+ android:title="@string/action_color_polygon"
+ mapbox:showAsAction="never" />
+</menu> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polyline.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polyline.xml
index 3590d0c68b..7c324fc9dc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polyline.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polyline.xml
@@ -6,4 +6,20 @@
android:icon="@drawable/ic_delete_24dp"
android:title="@string/action_remove_polylines"
mapbox:showAsAction="ifRoom" />
+ <item
+ android:id="@+id/action_id_width"
+ android:title="@string/action_width_polyline"
+ mapbox:showAsAction="never" />
+ <item
+ android:id="@+id/action_id_alpha"
+ android:title="@string/action_alpha_polygon"
+ mapbox:showAsAction="never" />
+ <item
+ android:id="@+id/action_id_color"
+ android:title="@string/action_color_polygon"
+ mapbox:showAsAction="never" />
+ <item
+ android:id="@+id/action_id_visible"
+ android:title="@string/action_visibility_polygon"
+ mapbox:showAsAction="never" />
</menu> \ No newline at end of file
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
new file mode 100644
index 0000000000..189a33708f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml
@@ -0,0 +1,45 @@
+<?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">
+
+ <item
+ android:id="@+id/action_water_color"
+ android:title="Color the water"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_background_opacity"
+ android:title="Set background opacity"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_road_avoid_edges"
+ android:title="Set road symbol placement to Point"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_layer_visibility"
+ android:title="Set layer visibility to false"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_add_parks_layer"
+ android:title="Add a parks layer"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_remove_layer"
+ android:title="Remove buildings layer"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_add_terrain_layer"
+ android:title="Add a terrain layer"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_add_satellite_layer"
+ android:title="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"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_add_custom_tiles"
+ android:title="Custom tiles"
+ mapbox:showAsAction="never"/>
+</menu> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/amsterdam.geojson b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/amsterdam.geojson
new file mode 100644
index 0000000000..2629a7c3c4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/amsterdam.geojson
@@ -0,0 +1,2283 @@
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Westerpark",
+ "type": "park",
+ "description": "The \"Westerpark\" is a public urban park in Amsterdam, Netherlands. The former borough of Westerpark is named after the park, as is the current neighborhood. The verdant space of the former Westergasfabriek (gasworks) along Haarlemmerweg has become a place for cultural avant-garde businesses and events. The park is stretched along the railway, offering a biotope area to experience nature in the city. In addition Westerpark is home to one of the Netherlands’ oldest volkstuin (Gardenpark)."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.88093376159668,
+ 52.38560608655206
+ ],
+ [
+ 4.881706237792968,
+ 52.3864966440161
+ ],
+ [
+ 4.870891571044922,
+ 52.388696767789725
+ ],
+ [
+ 4.864625930786133,
+ 52.38906344442449
+ ],
+ [
+ 4.85072135925293,
+ 52.389220590621235
+ ],
+ [
+ 4.846086502075195,
+ 52.38864438516467
+ ],
+ [
+ 4.84522819519043,
+ 52.38607756038855
+ ],
+ [
+ 4.845314025878906,
+ 52.38560608655206
+ ],
+ [
+ 4.84745979309082,
+ 52.38560608655206
+ ],
+ [
+ 4.848232269287109,
+ 52.38518699447024
+ ],
+ [
+ 4.88093376159668,
+ 52.38560608655206
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "vondelpark",
+ "type": "park",
+ "description": "Vondelpark has opened its gates since 1885 and is Amsterdam's busiest park, with 10 Million visitors per year, situated at the south-west corner of the canal ring. It is very popular in summer for both tourists and locals, and all year round as a training area for runners, with many bootcamps taking place all over the park."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.881491661071777,
+ 52.36194735288177
+ ],
+ [
+ 4.882135391235352,
+ 52.361711487760196
+ ],
+ [
+ 4.882307052612305,
+ 52.361475621379526
+ ],
+ [
+ 4.875826835632324,
+ 52.35966727063089
+ ],
+ [
+ 4.875226020812988,
+ 52.35846166234964
+ ],
+ [
+ 4.866771697998047,
+ 52.356207610808546
+ ],
+ [
+ 4.867458343505859,
+ 52.355159175569305
+ ],
+ [
+ 4.86668586730957,
+ 52.35497569684526
+ ],
+ [
+ 4.864239692687988,
+ 52.35563097450493
+ ],
+ [
+ 4.861965179443359,
+ 52.35578823969753
+ ],
+ [
+ 4.858918190002441,
+ 52.35437283281734
+ ],
+ [
+ 4.857029914855957,
+ 52.35468737159704
+ ],
+ [
+ 4.855892658233642,
+ 52.354634948622525
+ ],
+ [
+ 4.855034351348877,
+ 52.356391084418235
+ ],
+ [
+ 4.875226020812988,
+ 52.36126596131745
+ ],
+ [
+ 4.876556396484375,
+ 52.360453519180375
+ ],
+ [
+ 4.881491661071777,
+ 52.36194735288177
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Jordaan",
+ "type": "neighborhood",
+ "description": "The Jordan was originally a working-class neighbourhood, and has now become a more upscale neighborhood. It is home to many art galleries, particularly for modern art, and is also dotted with speciality shops and restaurants. Markets are held regularly at Noordermarkt, the Westerstraat and Lindengracht."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.888465404510498,
+ 52.38053742479665
+ ],
+ [
+ 4.883208274841309,
+ 52.374865596670936
+ ],
+ [
+ 4.882457256317139,
+ 52.36667749309006
+ ],
+ [
+ 4.882757663726807,
+ 52.36619270976844
+ ],
+ [
+ 4.879302978515624,
+ 52.36490866337324
+ ],
+ [
+ 4.874324798583984,
+ 52.37186565170666
+ ],
+ [
+ 4.8818135261535645,
+ 52.38427021667093
+ ],
+ [
+ 4.888465404510498,
+ 52.38053742479665
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Prinseneiland",
+ "type": "neighborhood",
+ "description": "Between 1610 and 1615 Prinseneiland was built as an extension of the harbor. Until the end of the 19th century this was an area with many wharfs, little industries and warehouses, related to the shipping trades. After the second World war the desolated area was discovered by many artists, who established their homes and studios in the vacant buildings. During the second half of the 20th century the old warehouses were transformed into apartments one after another, and new apartments were built. Nevertheless a lot of the atmosphere of the glorious past is still present in the old buildings and wooden drawbridges."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.883251190185547,
+ 52.38618233166844
+ ],
+ [
+ 4.889817237854004,
+ 52.38264616355127
+ ],
+ [
+ 4.896254539489746,
+ 52.38356297507495
+ ],
+ [
+ 4.891490936279297,
+ 52.390425359543386
+ ],
+ [
+ 4.884967803955078,
+ 52.39068726147953
+ ],
+ [
+ 4.883251190185547,
+ 52.38618233166844
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Sarphatipark",
+ "type": "park",
+ "description": "Sarphatipark is a small park in the popular De Pijp neighbourhood. It was openend in late 19th century, and named after Samuel Sarphati."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.894580841064453,
+ 52.35337022551748
+ ],
+ [
+ 4.899033308029174,
+ 52.354267986060016
+ ],
+ [
+ 4.89815354347229,
+ 52.35544094498385
+ ],
+ [
+ 4.893786907196045,
+ 52.35446457352601
+ ],
+ [
+ 4.894580841064453,
+ 52.35337022551748
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Museumsplein",
+ "type": "area",
+ "description": "Museumplein is a large open space which hosts different events throughout the year. Along the edges of the open square, some of the Dutch capitals most important art museums, such as Stedelijk Museum, Van Gogh Museum and Rijksmuseum."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.880322217941284,
+ 52.35625347928239
+ ],
+ [
+ 4.881459474563598,
+ 52.35610932106146
+ ],
+ [
+ 4.882693290710448,
+ 52.356921843071525
+ ],
+ [
+ 4.883508682250977,
+ 52.357996446011384
+ ],
+ [
+ 4.884324073791504,
+ 52.35783263627572
+ ],
+ [
+ 4.884721040725708,
+ 52.35903498560687
+ ],
+ [
+ 4.886341094970703,
+ 52.35875651523955
+ ],
+ [
+ 4.886770248413086,
+ 52.36003418836164
+ ],
+ [
+ 4.884881973266602,
+ 52.36113492327348
+ ],
+ [
+ 4.884538650512695,
+ 52.36066318309746
+ ],
+ [
+ 4.883229732513428,
+ 52.36028971855292
+ ],
+ [
+ 4.883841276168823,
+ 52.35953622784582
+ ],
+ [
+ 4.882038831710815,
+ 52.35897929167382
+ ],
+ [
+ 4.882339239120483,
+ 52.35861236518361
+ ],
+ [
+ 4.880794286727905,
+ 52.35790471263422
+ ],
+ [
+ 4.880847930908203,
+ 52.357780217032044
+ ],
+ [
+ 4.879699945449829,
+ 52.35691529053445
+ ],
+ [
+ 4.880322217941284,
+ 52.35625347928239
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Marineterrein",
+ "type": "area",
+ "description": "The Marineterrein exists already for more than 350 years. The area lies on the island known as ‘Kattenburg’, in close vicinity to Centraal Station and Amsterdam’s Maritime Museum. During the Golden Age the VOC used this area to build large warships. After years of use through the Dutch marines, the area has now been opened for the public and workspaces are filled by tech startups."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.914064407348633,
+ 52.37083068892153
+ ],
+ [
+ 4.917154312133789,
+ 52.37187875234601
+ ],
+ [
+ 4.921102523803711,
+ 52.37444640263532
+ ],
+ [
+ 4.921188354492187,
+ 52.37481319763409
+ ],
+ [
+ 4.914150238037109,
+ 52.37586116655898
+ ],
+ [
+ 4.913034439086914,
+ 52.374917995645625
+ ],
+ [
+ 4.914493560791016,
+ 52.37339840013861
+ ],
+ [
+ 4.914064407348633,
+ 52.37083068892153
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "erasmuspark"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.849905967712402,
+ 52.37300539279099
+ ],
+ [
+ 4.852695465087891,
+ 52.37350320150736
+ ],
+ [
+ 4.855098724365234,
+ 52.37428920384616
+ ],
+ [
+ 4.854240417480469,
+ 52.3765423330248
+ ],
+ [
+ 4.848318099975586,
+ 52.37552057938607
+ ],
+ [
+ 4.849905967712402,
+ 52.37300539279099
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Sloterdijk",
+ "type": "area",
+ "description": "To protect the area around Sloten from the as-yet undrained IJ the Spaarndammerdijk was laid along the south bank of this inlet. In this vicinity at the same time, a dam on the Slochter (or Slooter) river was built, the Slooterdam. Trade grew in the vicinity, and in the 15th century a weigh house and a church were built. The area is nowadays best known as a large intersection of train lines and a business and industrial centre north-west of Amsterdam."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.8665571212768555,
+ 52.39312287505632
+ ],
+ [
+ 4.849648475646973,
+ 52.400638383557414
+ ],
+ [
+ 4.845571517944336,
+ 52.39681532315127
+ ],
+ [
+ 4.8445844650268555,
+ 52.39411803332277
+ ],
+ [
+ 4.844756126403809,
+ 52.38911582655221
+ ],
+ [
+ 4.85072135925293,
+ 52.38935154535783
+ ],
+ [
+ 4.871063232421875,
+ 52.389299163509826
+ ],
+ [
+ 4.8665571212768555,
+ 52.39312287505632
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Nine Streets (Negen Straatjes)",
+ "type": "poi",
+ "description": "De Negen Straatjes (Dutch for \"the nine little streets\") are nine side streets of the Prinsengracht, Keizersgracht, Herengracht and Singel in central Amsterdam which have been promoting themselves with that name since the 1990s. Together they form a sub-neighborhood within the larger western Grachtengordel (\"Canal Belt\"), one with many small and diverse shops and restaurants. The construction in this area goes back to the first half of the 17th century."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.887650012969971,
+ 52.368629674781644
+ ],
+ [
+ 4.8884546756744385,
+ 52.37242897568859
+ ],
+ [
+ 4.883047342300415,
+ 52.372664783594274
+ ],
+ [
+ 4.882628917694092,
+ 52.36839384533322
+ ],
+ [
+ 4.887650012969971,
+ 52.368629674781644
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Artis",
+ "type": "poi",
+ "description": "Artis, short for Natura Artis Magistra (Latin for \"Nature is the teacher of art and science\"), is a zoo in the centre of Amsterdam. It is the oldest zoo in the Netherlands and one of the oldest zoos of mainland Europe. Artis Royal Zoo is not just a zoo, it also contains an aquarium and a planetarium. Artis also has an arboretum and a fairly large art collection. A part of the art collection is on display in the Aquarium building of the zoo. Artis contains 27 monumental buildings, most of which are used as enclosures for the animals, making Artis a unique cultural heritage of the 19th century."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.911768436431885,
+ 52.36642855096776
+ ],
+ [
+ 4.913313388824463,
+ 52.36802699702209
+ ],
+ [
+ 4.916939735412598,
+ 52.36679541255308
+ ],
+ [
+ 4.9175190925598145,
+ 52.3673456992188
+ ],
+ [
+ 4.921274185180664,
+ 52.366048583971256
+ ],
+ [
+ 4.921660423278808,
+ 52.36551138367574
+ ],
+ [
+ 4.918656349182129,
+ 52.36400456750192
+ ],
+ [
+ 4.911768436431885,
+ 52.36642855096776
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Oosterpark",
+ "type": "park",
+ "description": "Oosterpark was the first large park opened by the municipality of Amsterdam in 1891. The park was designed as an English garden by Dutch landscape architect Leonard Anthony Springer."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.915802478790282,
+ 52.36062387118878
+ ],
+ [
+ 4.9173688888549805,
+ 52.35804231262857
+ ],
+ [
+ 4.925351142883301,
+ 52.36002108420944
+ ],
+ [
+ 4.923892021179199,
+ 52.36192114570822
+ ],
+ [
+ 4.92213249206543,
+ 52.36150182881734
+ ],
+ [
+ 4.92161750793457,
+ 52.36223563076494
+ ],
+ [
+ 4.919493198394775,
+ 52.36159355472725
+ ],
+ [
+ 4.91987943649292,
+ 52.36101698870163
+ ],
+ [
+ 4.918956756591797,
+ 52.360741806809884
+ ],
+ [
+ 4.918656349182129,
+ 52.36097767710775
+ ],
+ [
+ 4.917240142822266,
+ 52.36063697516221
+ ],
+ [
+ 4.916982650756836,
+ 52.360899053815025
+ ],
+ [
+ 4.915802478790282,
+ 52.36062387118878
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Park Frankendael",
+ "type": "park",
+ "description": "As Amsterdam rapidly grew in the sixteen and the beginning of eighteen century, the real estate in the city became so expensive, that rich people who wanted to enjoy a bigger property had to move further from the capital. The Park Frankendael (7 acres) in East Amsterdam, was originally one of these wealthy estates. The entrance to the park with an old ornamented gate is at the Middenweg, less than one mile (1300m) from the Tropenmuseum, driving out of the city. The beautiful old land house Frankendael (built in 1659) is visible from the street – it is one of the few of these estates remaining in Amsterdam"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.925265312194824,
+ 52.35007391180776
+ ],
+ [
+ 4.9283552169799805,
+ 52.352079253210675
+ ],
+ [
+ 4.930479526519775,
+ 52.3535995821349
+ ],
+ [
+ 4.93483543395996,
+ 52.35080790353051
+ ],
+ [
+ 4.929170608520508,
+ 52.34772767795072
+ ],
+ [
+ 4.925265312194824,
+ 52.35007391180776
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Stedelijk Museum",
+ "type": "poi",
+ "description": "Colloquially known as the Stedelijk, it is a museum for modern art, contemporary art, and design. The 19th century building was designed by Adriaan Willem Weissman and the 21st century wing with the current entrance was designed by Benthem Crouwel Architects. The collection comprises art from the early 20th century up to the 21st century. "
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.879399538040161,
+ 52.357400175655954
+ ],
+ [
+ 4.88067626953125,
+ 52.3577933218488
+ ],
+ [
+ 4.880236387252808,
+ 52.35841579616774
+ ],
+ [
+ 4.8789381980896,
+ 52.35808817919812
+ ],
+ [
+ 4.879399538040161,
+ 52.357400175655954
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Rembrandtpark",
+ "type": "park",
+ "description": "Rembrandtpark is a hidden gem west of the 'famous' Vondelpark. It is often not known to tourists and expats, but loved by locals for the outdoor gym and kids' playgrounds. It's great to combine both, Vondelpark and Rembrandtpark on a run or walk."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.845314025878906,
+ 52.369179938598464
+ ],
+ [
+ 4.84522819519043,
+ 52.36763394187947
+ ],
+ [
+ 4.843039512634277,
+ 52.36755533043126
+ ],
+ [
+ 4.8429107666015625,
+ 52.36490866337324
+ ],
+ [
+ 4.8442840576171875,
+ 52.36456799173892
+ ],
+ [
+ 4.84419822692871,
+ 52.36323148534417
+ ],
+ [
+ 4.843082427978516,
+ 52.36302183361385
+ ],
+ [
+ 4.843254089355469,
+ 52.36019143788499
+ ],
+ [
+ 4.844799041748047,
+ 52.36027006243683
+ ],
+ [
+ 4.8451852798461905,
+ 52.35924793235075
+ ],
+ [
+ 4.843854904174805,
+ 52.35888100809126
+ ],
+ [
+ 4.844112396240234,
+ 52.35822577862119
+ ],
+ [
+ 4.848747253417969,
+ 52.35825198798652
+ ],
+ [
+ 4.8487043380737305,
+ 52.36079422254044
+ ],
+ [
+ 4.849519729614258,
+ 52.361292168879636
+ ],
+ [
+ 4.849262237548828,
+ 52.363572167284175
+ ],
+ [
+ 4.850249290466309,
+ 52.3637031981001
+ ],
+ [
+ 4.850249290466309,
+ 52.36540656334422
+ ],
+ [
+ 4.8484039306640625,
+ 52.369546777334904
+ ],
+ [
+ 4.845314025878906,
+ 52.369179938598464
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Sloterpark",
+ "type": "park",
+ "description": "Amsterdam's largest park is Sloter Park with 91 hectares. It was created in the 1950s using excavated soil of the former Sloterdijkermeer Polder."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.823555946350098,
+ 52.3639652585661
+ ],
+ [
+ 4.826860427856445,
+ 52.36414869996741
+ ],
+ [
+ 4.826817512512207,
+ 52.36585204803552
+ ],
+ [
+ 4.830508232116699,
+ 52.370280445668364
+ ],
+ [
+ 4.826602935791016,
+ 52.373031593389626
+ ],
+ [
+ 4.822740554809569,
+ 52.37360800262741
+ ],
+ [
+ 4.821324348449707,
+ 52.37235037277361
+ ],
+ [
+ 4.819135665893555,
+ 52.371695343041914
+ ],
+ [
+ 4.81201171875,
+ 52.37187875234601
+ ],
+ [
+ 4.807548522949219,
+ 52.372402774732464
+ ],
+ [
+ 4.805660247802734,
+ 52.37046386084771
+ ],
+ [
+ 4.80926513671875,
+ 52.36349354860812
+ ],
+ [
+ 4.8105525970458975,
+ 52.36328389812132
+ ],
+ [
+ 4.8113250732421875,
+ 52.36150182881734
+ ],
+ [
+ 4.807033538818359,
+ 52.360899053815025
+ ],
+ [
+ 4.809608459472655,
+ 52.35654834690599
+ ],
+ [
+ 4.815659523010254,
+ 52.3577540073869
+ ],
+ [
+ 4.818663597106934,
+ 52.357963684112846
+ ],
+ [
+ 4.818620681762695,
+ 52.359483810623004
+ ],
+ [
+ 4.823555946350098,
+ 52.3639652585661
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amsterdamse Bos",
+ "type": "park",
+ "description": "Every year almost 4.5 million people visit the Amsterdamse Bos, which has a size of 1,000 hectares and is approximately three times the size of New York's Central Park. The park was designed as a landscape park and is great for running, walking and cycling."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.849991798400879,
+ 52.33090717211467
+ ],
+ [
+ 4.849863052368164,
+ 52.331746382485676
+ ],
+ [
+ 4.853982925415039,
+ 52.33187750766789
+ ],
+ [
+ 4.853467941284179,
+ 52.332323330379836
+ ],
+ [
+ 4.848232269287109,
+ 52.33258557693488
+ ],
+ [
+ 4.848318099975586,
+ 52.33321496232317
+ ],
+ [
+ 4.8319244384765625,
+ 52.33145790571652
+ ],
+ [
+ 4.8181915283203125,
+ 52.330828495326095
+ ],
+ [
+ 4.820079803466797,
+ 52.32327487205222
+ ],
+ [
+ 4.816474914550781,
+ 52.31299147898779
+ ],
+ [
+ 4.813899993896484,
+ 52.30879348896445
+ ],
+ [
+ 4.809093475341796,
+ 52.30585465906291
+ ],
+ [
+ 4.824028015136719,
+ 52.29000260620264
+ ],
+ [
+ 4.832954406738281,
+ 52.29441235610253
+ ],
+ [
+ 4.8427391052246085,
+ 52.29220753602784
+ ],
+ [
+ 4.844627380371094,
+ 52.30060626328963
+ ],
+ [
+ 4.842395782470703,
+ 52.30396530825102
+ ],
+ [
+ 4.842395782470703,
+ 52.30407027430016
+ ],
+ [
+ 4.839649200439453,
+ 52.30504119845803
+ ],
+ [
+ 4.841108322143555,
+ 52.30732409839935
+ ],
+ [
+ 4.840335845947266,
+ 52.31708413595253
+ ],
+ [
+ 4.848575592041015,
+ 52.317189070898415
+ ],
+ [
+ 4.8558712005615225,
+ 52.31645452105213
+ ],
+ [
+ 4.8566436767578125,
+ 52.323484712336324
+ ],
+ [
+ 4.856557846069336,
+ 52.32757639896581
+ ],
+ [
+ 4.855785369873047,
+ 52.33085472093785
+ ],
+ [
+ 4.849991798400879,
+ 52.33090717211467
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amstelpark",
+ "type": "park",
+ "description": "The Amstelpark is a park in Amsterdam-Zuid. The park includes a labyrinth, a café, a restaurant, two galleries, an orangery, petting zoo and a mini-golf course. The Amstelpark was built and opened for the 1972 Floriade gardening exhibition. The park offers the Amstel train which runs through the Rosarium, the rhododendron valley and the Riekermolen. The park lost about 30 percent of its larger trees due to disease. The rhododendron valley contains about 139 species of rhododendrons, blooming between April and May. At the Great Pond in the park is the Japanese Garden. At the time of the celebration of the 400-year relationship between the Netherlands and Japan in 2001, the gardens were renovated."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.892907142639159,
+ 52.32445521070514
+ ],
+ [
+ 4.891705513000488,
+ 52.325110940795255
+ ],
+ [
+ 4.890632629394531,
+ 52.32513716979672
+ ],
+ [
+ 4.89041805267334,
+ 52.3336869954871
+ ],
+ [
+ 4.892778396606445,
+ 52.33373944330546
+ ],
+ [
+ 4.894108772277832,
+ 52.333949233956965
+ ],
+ [
+ 4.897370338439941,
+ 52.33255935234935
+ ],
+ [
+ 4.897799491882323,
+ 52.33085472093785
+ ],
+ [
+ 4.897327423095702,
+ 52.32907134391899
+ ],
+ [
+ 4.895954132080078,
+ 52.32734035040776
+ ],
+ [
+ 4.894537925720215,
+ 52.32458635750065
+ ],
+ [
+ 4.892907142639159,
+ 52.32445521070514
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Martin Luther Kingpark",
+ "type": "park",
+ "description": "Martin Luther Kingpark is part of the Rivierenbuurt and has been renamed from Amstelpark, after the new Amstelpark in Buitenveldert was created. The park hosts the famous theater festival Parade every year in summer since the 1990s. "
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.906554222106933,
+ 52.33813172737753
+ ],
+ [
+ 4.906167984008789,
+ 52.33685997655858
+ ],
+ [
+ 4.90389347076416,
+ 52.33743685775091
+ ],
+ [
+ 4.901747703552246,
+ 52.33821039117558
+ ],
+ [
+ 4.901790618896484,
+ 52.3400982803501
+ ],
+ [
+ 4.90689754486084,
+ 52.34004584007248
+ ],
+ [
+ 4.905717372894287,
+ 52.33836771835187
+ ],
+ [
+ 4.906554222106933,
+ 52.33813172737753
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Beatrixpark",
+ "type": "park",
+ "description": "Beatrixpark, named after Queen Beatrix is located in the borough of Amsterdam-Zuid."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.87818717956543,
+ 52.34435884510934
+ ],
+ [
+ 4.88093376159668,
+ 52.34582700615622
+ ],
+ [
+ 4.882993698120117,
+ 52.34456858538671
+ ],
+ [
+ 4.8854827880859375,
+ 52.34435884510934
+ ],
+ [
+ 4.884710311889648,
+ 52.33984918847747
+ ],
+ [
+ 4.882392883300781,
+ 52.33995406943698
+ ],
+ [
+ 4.879045486450195,
+ 52.33984918847747
+ ],
+ [
+ 4.878787994384765,
+ 52.340688229188224
+ ],
+ [
+ 4.881620407104492,
+ 52.34074066870404
+ ],
+ [
+ 4.881706237792968,
+ 52.341789445960536
+ ],
+ [
+ 4.878444671630859,
+ 52.341789445960536
+ ],
+ [
+ 4.87818717956543,
+ 52.34435884510934
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Park de Schinkeleilanden",
+ "type": "Park",
+ "description": "This park was built between 2005 and 2010 and is popular for the neighborhoods surrounding the Schinkel waterway. It's allowed to BBQ here, and it's a popular spot for running and hiking."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.851171970367432,
+ 52.34405734171741
+ ],
+ [
+ 4.851021766662597,
+ 52.34514536601911
+ ],
+ [
+ 4.851686954498291,
+ 52.34628579648493
+ ],
+ [
+ 4.851021766662597,
+ 52.3465610683968
+ ],
+ [
+ 4.848833084106445,
+ 52.342785761313266
+ ],
+ [
+ 4.849659204483032,
+ 52.340747223639156
+ ],
+ [
+ 4.852405786514282,
+ 52.34110774357341
+ ],
+ [
+ 4.852041006088257,
+ 52.34196642466951
+ ],
+ [
+ 4.852041006088257,
+ 52.341979534175316
+ ],
+ [
+ 4.851665496826172,
+ 52.34280870252078
+ ],
+ [
+ 4.851171970367432,
+ 52.34405734171741
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Schiphol",
+ "type": "area",
+ "description": "Schiphol is the main international airport of the Netherlands. Schiphol Airport is an important European airport, ranking as Europe's fifth busiest and the world's fourteenth busiest by total passenger traffic in 2015. It also ranks as the world's fifth busiest by international passenger traffic. The entire airport is below sea level; the lowest point sits at 3.4 m (11 ft) below sea level."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.729099273681641,
+ 52.28811257899827
+ ],
+ [
+ 4.734764099121094,
+ 52.28559241729168
+ ],
+ [
+ 4.762744903564453,
+ 52.292522517043615
+ ],
+ [
+ 4.776134490966797,
+ 52.284857343123655
+ ],
+ [
+ 4.7907257080078125,
+ 52.293992398835414
+ ],
+ [
+ 4.793128967285156,
+ 52.30081621106509
+ ],
+ [
+ 4.803943634033203,
+ 52.30564473517634
+ ],
+ [
+ 4.810981750488281,
+ 52.311942018805624
+ ],
+ [
+ 4.796905517578125,
+ 52.319707434957024
+ ],
+ [
+ 4.788494110107422,
+ 52.320966563244205
+ ],
+ [
+ 4.779567718505859,
+ 52.32442898129939
+ ],
+ [
+ 4.77081298828125,
+ 52.3191827875965
+ ],
+ [
+ 4.756736755371094,
+ 52.31886799619451
+ ],
+ [
+ 4.750041961669922,
+ 52.310367781878
+ ],
+ [
+ 4.7454071044921875,
+ 52.30281066528705
+ ],
+ [
+ 4.7426605224609375,
+ 52.29798183210937
+ ],
+ [
+ 4.729099273681641,
+ 52.28811257899827
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Noorderpark",
+ "type": "park",
+ "description": "The Noorderpark is a park just 5 minutes north of the ferry that crosses the IJ canal behind Amsterdam Central Station. It came to exist in 2014 after combining Florapark and Volewijkspark."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.921703338623047,
+ 52.39046464493288
+ ],
+ [
+ 4.921939373016357,
+ 52.389652739777624
+ ],
+ [
+ 4.92436408996582,
+ 52.39068726147955
+ ],
+ [
+ 4.924213886260986,
+ 52.390922971893374
+ ],
+ [
+ 4.92460012435913,
+ 52.39105392157937
+ ],
+ [
+ 4.923999309539795,
+ 52.39326691251008
+ ],
+ [
+ 4.924793243408203,
+ 52.39486438729677
+ ],
+ [
+ 4.922282695770264,
+ 52.39698553494043
+ ],
+ [
+ 4.919922351837158,
+ 52.39847813327716
+ ],
+ [
+ 4.917948246002197,
+ 52.397705654476155
+ ],
+ [
+ 4.918398857116699,
+ 52.39618684316538
+ ],
+ [
+ 4.917948246002197,
+ 52.39527029380819
+ ],
+ [
+ 4.917197227478027,
+ 52.39200984251752
+ ],
+ [
+ 4.9161458015441895,
+ 52.39068726147955
+ ],
+ [
+ 4.918420314788818,
+ 52.389901551009025
+ ],
+ [
+ 4.920244216918945,
+ 52.39115868104846
+ ],
+ [
+ 4.921402931213379,
+ 52.39157771643836
+ ],
+ [
+ 4.922153949737548,
+ 52.39121106068977
+ ],
+ [
+ 4.921703338623047,
+ 52.39046464493288
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Vliegenbos",
+ "type": "park",
+ "description": "The Vliegenbos is the oldest city forest in Amsterdam. Here you've got plenty of space to run or cycle through a dense forest. It also hosts a camping ground, and is an amazingly well located starting point for excursions to the fishing villages along the IJsselmeer. "
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.925222396850586,
+ 52.389403927143704
+ ],
+ [
+ 4.925136566162109,
+ 52.39003250372539
+ ],
+ [
+ 4.926724433898926,
+ 52.39105392157937
+ ],
+ [
+ 4.933032989501953,
+ 52.391525337232174
+ ],
+ [
+ 4.93311882019043,
+ 52.390739641680284
+ ],
+ [
+ 4.938998222351074,
+ 52.38969202585476
+ ],
+ [
+ 4.9376678466796875,
+ 52.38686333892666
+ ],
+ [
+ 4.927024841308593,
+ 52.38906344442449
+ ],
+ [
+ 4.926466941833496,
+ 52.38948249970591
+ ],
+ [
+ 4.9253082275390625,
+ 52.38945630886739
+ ],
+ [
+ 4.925222396850586,
+ 52.389403927143704
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Flevopark",
+ "type": "park",
+ "description": "Flevopark is the jewel in the East of Amsterdam. Its offerings include an outdoor swimming pool, several lakes, restaurants. It's awesome for a bbq in summer or a foggy walk or run on a winter morning."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.948225021362305,
+ 52.36493486877479
+ ],
+ [
+ 4.9527740478515625,
+ 52.36517071668903
+ ],
+ [
+ 4.954404830932617,
+ 52.36517071668903
+ ],
+ [
+ 4.955005645751953,
+ 52.36459419734253
+ ],
+ [
+ 4.952559471130371,
+ 52.36215700971062
+ ],
+ [
+ 4.952859878540038,
+ 52.35953622784585
+ ],
+ [
+ 4.952731132507324,
+ 52.35809473156134
+ ],
+ [
+ 4.944963455200195,
+ 52.35859270832139
+ ],
+ [
+ 4.946165084838867,
+ 52.362314251679365
+ ],
+ [
+ 4.947195053100586,
+ 52.362602527168704
+ ],
+ [
+ 4.947667121887207,
+ 52.36417490581972
+ ],
+ [
+ 4.948225021362305,
+ 52.36493486877479
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amsterdam Centraal",
+ "type": "station",
+ "description": "Amsterdam's Central Station."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.896576404571533,
+ 52.37985631995486
+ ],
+ [
+ 4.89715576171875,
+ 52.38066840529253
+ ],
+ [
+ 4.90389347076416,
+ 52.37820590695898
+ ],
+ [
+ 4.902949333190918,
+ 52.37738067732881
+ ],
+ [
+ 4.896576404571533,
+ 52.37985631995486
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amsterdam Amstel",
+ "type": "station",
+ "description": "Amsterdam Amstel is a trainstation in the South East, close to the river Amstel."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.916896820068359,
+ 52.3468101224592
+ ],
+ [
+ 4.917690753936768,
+ 52.34704606711881
+ ],
+ [
+ 4.918248653411864,
+ 52.34604984776767
+ ],
+ [
+ 4.9173903465271,
+ 52.34584011451739
+ ],
+ [
+ 4.916896820068359,
+ 52.3468101224592
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Ouderkerk",
+ "type": "poi",
+ "description": "Ouderkerk aan de Amstel is a picturesque village in the Dutch province of North Holland. It lies about 9 km south of Amsterdam. The town is a popular destination for Amsterdammers on the weekends. The town is the location of the Beth Haim of Ouderkerk aan de Amstel, the oldest Jewish cemetery in the Netherlands."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.922518730163574,
+ 52.29236502681571
+ ],
+ [
+ 4.922819137573242,
+ 52.28777131549237
+ ],
+ [
+ 4.90788459777832,
+ 52.29034385252062
+ ],
+ [
+ 4.892692565917969,
+ 52.29456983905114
+ ],
+ [
+ 4.898228645324707,
+ 52.29824428222637
+ ],
+ [
+ 4.902563095092773,
+ 52.299267822821434
+ ],
+ [
+ 4.908742904663086,
+ 52.29879542240944
+ ],
+ [
+ 4.916167259216309,
+ 52.30149853446092
+ ],
+ [
+ 4.920544624328613,
+ 52.29666955819423
+ ],
+ [
+ 4.922518730163574,
+ 52.29236502681571
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "NDSM terrein",
+ "type": "poi",
+ "description": "The former NDSM Amsterdam ship wharf is a stunning hangout. Just 10 minutes by free ferry and a large area is there to explore. Restaurants, bars, terraces, skatepark, new and old architecture, all with the amazing view on the IJ-waters. Many of the old buildings that were in use for the making of large ships are still there, housing creative enterprises."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.890182018280029,
+ 52.401711923144106
+ ],
+ [
+ 4.894495010375977,
+ 52.40307344797437
+ ],
+ [
+ 4.899129867553711,
+ 52.400939500955296
+ ],
+ [
+ 4.896554946899414,
+ 52.39881854337136
+ ],
+ [
+ 4.8909544944763175,
+ 52.39813772055684
+ ],
+ [
+ 4.891490936279297,
+ 52.400847856747404
+ ],
+ [
+ 4.890182018280029,
+ 52.401711923144106
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amsterdam Zuid",
+ "type": "station",
+ "description": "Station Amsterdam Zuid"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.8720502853393555,
+ 52.33950832364112
+ ],
+ [
+ 4.874711036682129,
+ 52.33969186657182
+ ],
+ [
+ 4.8749041557312,
+ 52.338459492279576
+ ],
+ [
+ 4.872071743011475,
+ 52.33835460777523
+ ],
+ [
+ 4.8720502853393555,
+ 52.33950832364112
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Het Twiske",
+ "type": "park",
+ "description": "Het Twiske recreational area lies to the north of Amsterdam between Zaanstad and Purmerend. It’s the ideal place for a day out on your bicycle, as Amsterdam’s bike paths connect directly to the paths in Het Twiske."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.888744354248047,
+ 52.43555429631541
+ ],
+ [
+ 4.900503158569336,
+ 52.43445541622349
+ ],
+ [
+ 4.910116195678711,
+ 52.43586825702301
+ ],
+ [
+ 4.910888671875,
+ 52.442199320554714
+ ],
+ [
+ 4.907197952270508,
+ 52.44852947442261
+ ],
+ [
+ 4.90565299987793,
+ 52.449941863962756
+ ],
+ [
+ 4.905910491943359,
+ 52.45135420821245
+ ],
+ [
+ 4.903764724731445,
+ 52.4549633266463
+ ],
+ [
+ 4.903507232666016,
+ 52.45846754991504
+ ],
+ [
+ 4.904794692993164,
+ 52.461866903001194
+ ],
+ [
+ 4.906854629516602,
+ 52.46489995032684
+ ],
+ [
+ 4.906940460205078,
+ 52.465736616263186
+ ],
+ [
+ 4.9031639099121085,
+ 52.4673576112622
+ ],
+ [
+ 4.896640777587891,
+ 52.468560246396606
+ ],
+ [
+ 4.895782470703125,
+ 52.46793278868704
+ ],
+ [
+ 4.892778396606445,
+ 52.465422868400594
+ ],
+ [
+ 4.890289306640625,
+ 52.46416785458775
+ ],
+ [
+ 4.882049560546875,
+ 52.46191919869101
+ ],
+ [
+ 4.876041412353516,
+ 52.45982732264483
+ ],
+ [
+ 4.87492561340332,
+ 52.45825835038316
+ ],
+ [
+ 4.873895645141601,
+ 52.45648011423114
+ ],
+ [
+ 4.873037338256836,
+ 52.454911022694276
+ ],
+ [
+ 4.873552322387695,
+ 52.452714200611055
+ ],
+ [
+ 4.874839782714844,
+ 52.44805866784458
+ ],
+ [
+ 4.879388809204102,
+ 52.44277482667677
+ ],
+ [
+ 4.883165359497069,
+ 52.43926935464697
+ ],
+ [
+ 4.887542724609374,
+ 52.43597291009513
+ ],
+ [
+ 4.888744354248047,
+ 52.43555429631541
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Diemerpark",
+ "type": "park"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.974660873413086,
+ 52.35694150067703
+ ],
+ [
+ 4.980497360229492,
+ 52.357989893633615
+ ],
+ [
+ 4.985218048095703,
+ 52.35827819733633
+ ],
+ [
+ 4.986248016357422,
+ 52.35720360124792
+ ],
+ [
+ 4.985218048095703,
+ 52.355971715048284
+ ],
+ [
+ 4.985218048095703,
+ 52.3552640202125
+ ],
+ [
+ 4.986376762390137,
+ 52.3545563140442
+ ],
+ [
+ 4.987921714782714,
+ 52.353901020450564
+ ],
+ [
+ 4.988865852355957,
+ 52.353901020450564
+ ],
+ [
+ 4.989681243896484,
+ 52.353219504806525
+ ],
+ [
+ 4.991183280944824,
+ 52.352642829515084
+ ],
+ [
+ 4.992728233337402,
+ 52.35169916280845
+ ],
+ [
+ 4.994831085205078,
+ 52.34996905485244
+ ],
+ [
+ 4.995818138122559,
+ 52.34991662628147
+ ],
+ [
+ 4.997320175170898,
+ 52.348763181988105
+ ],
+ [
+ 4.997320175170898,
+ 52.348081587122245
+ ],
+ [
+ 4.99852180480957,
+ 52.34658728467996
+ ],
+ [
+ 4.999551773071289,
+ 52.34535510256526
+ ],
+ [
+ 4.999337196350098,
+ 52.345197800248926
+ ],
+ [
+ 4.976506233215332,
+ 52.35573581802885
+ ],
+ [
+ 4.974660873413086,
+ 52.35694150067703
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amstelfeld",
+ "type": "poi"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.897080659866332,
+ 52.36253045847271
+ ],
+ [
+ 4.8978424072265625,
+ 52.36269425079362
+ ],
+ [
+ 4.89815354347229,
+ 52.362137354425165
+ ],
+ [
+ 4.896329641342163,
+ 52.36185562770631
+ ],
+ [
+ 4.896275997161865,
+ 52.36240597590272
+ ],
+ [
+ 4.897080659866332,
+ 52.36253045847271
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Albert Cuyp Markt",
+ "type": "poi"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.890514612197876,
+ 52.35491672137934
+ ],
+ [
+ 4.894967079162598,
+ 52.35580134510498
+ ],
+ [
+ 4.896436929702759,
+ 52.356168294935955
+ ],
+ [
+ 4.899580478668213,
+ 52.3570725511568
+ ],
+ [
+ 4.899430274963379,
+ 52.35731499351983
+ ],
+ [
+ 4.897252321243286,
+ 52.356587662440496
+ ],
+ [
+ 4.895503520965576,
+ 52.356076557763856
+ ],
+ [
+ 4.893561601638794,
+ 52.355683396297984
+ ],
+ [
+ 4.8904502391815186,
+ 52.35510020034824
+ ],
+ [
+ 4.890514612197876,
+ 52.35491672137934
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Noordermarkt",
+ "type": "poi"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.885987043380737,
+ 52.37978100483205
+ ],
+ [
+ 4.886265993118286,
+ 52.37907041669766
+ ],
+ [
+ 4.886485934257507,
+ 52.37900819875896
+ ],
+ [
+ 4.886732697486877,
+ 52.37907041669766
+ ],
+ [
+ 4.887698292732239,
+ 52.38007244089837
+ ],
+ [
+ 4.885987043380737,
+ 52.37978100483205
+ ]
+ ]
+ ]
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/attrs.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/attrs.xml
new file mode 100644
index 0000000000..2eb503b057
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/attrs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="ScrimInsetsView">
+ <attr name="appInsetForeground" format="reference|color" />
+ </declare-styleable>
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/dimens.xml
new file mode 100644
index 0000000000..035b9f3564
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/dimens.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="toolbar_padding_top">25dp</dimen>
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/styles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000000..3d61b9a36d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="AppTheme" parent="AppBaseTheme">
+
+ </style>r
+
+ <style name="AppTheme.ActionBar.Transparent" parent="AppTheme">
+ <item name="colorPrimary">@android:color/transparent</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+ <item name="android:windowTranslucentStatus">true</item>
+ </style>
+
+ <style name="AppTheme.ActionBar" parent="AppTheme">
+ <item name="colorPrimaryDark">@color/primary_dark</item>
+ <item name="colorPrimary">@color/primary</item>
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-w820dp/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000000..63fc816444
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
index 98ec90c6fd..b77a25b681 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <dimen name="circle_size">24dp</dimen>
<dimen name="fab_margin">16dp</dimen>
+ <dimen name="full_button_margin">8dp</dimen>
<dimen name="attr_margin">10dp</dimen>
<dimen name="coordinatebounds_margin">32dp</dimen>
<dimen name="map_padding_left">96dp</dimen>
@@ -9,4 +11,9 @@
<dimen name="toolbar_shadow">4dp</dimen>
<dimen name="locationview_background_drawable_padding">2dp</dimen>
<dimen name="locationview_padding_top">350dp</dimen>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="navigation_drawer_width">240dp</dimen>
+ <dimen name="toolbar_padding_top">0dp</dimen>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index a84035aa58..00c421bfef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -18,10 +18,12 @@
<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_view_marker_scale">Scaling in the View Marker API</string>
<!-- InfoWindow-->
- <string name="activity_info_window">Standard InfoWindow example</string>
- <string name="activity_infowindow_adapter">Custom InfoWindow Adapter</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>
<!-- Camera -->
<string name="activity_camera_animation_types">Animation Types</string>
@@ -34,11 +36,15 @@
<string name="activity_directions">Directions</string>
<string name="activity_geocoder">Geocoder</string>
+ <!-- Navigation -->
+ <string name="activity_location_picker">Location Picker</string>
+
<!-- Other -->
<string name="activity_double_map">Double Map Activity</string>
+ <string name="activity_back_to_map">Back to 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 customization</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_layer">Custom Layer</string>
@@ -46,6 +52,11 @@
<string name="activity_debug_mode">Debug Mode</string>
<string name="activity_offline">Offline Map</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_print">Print a map</string>
+ <string name="activity_surfaceview_overlay">SurfaceView MediaOverlay</string>
+ <string name="title_activity_navigation_drawer">Android SDK View integration</string>
<!-- Description -->
<string name="description_user_location_tracking">Tracks the location of the user</string>
@@ -77,9 +88,19 @@
<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_back_to_map">Restart map view after temporarily leaving to another activity</string>
<string name="description_view_marker">Use an Android SDK View as marker</string>
-
- <string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string>r
+ <string name="description_view_marker_scale">Scale a View Marker</string>
+ <string name="description_dynamic_info_window_adapter">Learn how to create a dynamic custom InfoWindow</string>
+ <string name="description_location_picker">Use a fixed Marker to select your location</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_print">Shows how to print a map</string>
+ <string name="description_navigation_drawer">Test animation of Android SDK View components</string>
+ <string name="description_surfaceview_mediacontrols">Test overlaying SurfaceView</string>
+
+ <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>
@@ -96,10 +117,18 @@
<string name="category_maplayout">Map Layout</string>
<string name="category_offline">Offline</string>
<string name="category_userlocation">User Location</string>
+ <string name="category_navigation">Navigation</string>
+ <string name="category_style">Styling</string>
<string name="action_visible_bounds_explanation">Center map around 2 markers</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_width_polyline">Change width</string>
+
<string name="button_camera_move">Move</string>
<string name="button_camera_ease">Ease</string>
<string name="button_camera_animate">Animate</string>
@@ -152,4 +181,21 @@
<string name="geocoder_instructions">Tap Map To Geocode Where Black Marker Is Currently Located</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="navigation_select_location_button_text">Select Location!</string>
+
+ <string name="title_section1">Different style</string>
+ <string name="title_section2">Show Snackbar</string>
+
+ <string name="navigation_drawer_open">Open navigation drawer</string>
+ <string name="navigation_drawer_close">Close navigation drawer</string>
+
+ <string name="action_example">Example action</string>
+
+ <string name="action_settings">Settings</string>
+
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml
index d01b9d313f..e358fcd5a0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml
@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <style name="AppTheme" parent="AppBaseTheme" />
-
<style name="AppBaseTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
@@ -10,4 +8,16 @@
<item name="android:windowBackground">@color/white</item>
</style>
+ <style name="AppTheme.ActionBar.Transparent" parent="AppTheme">
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="windowActionBarOverlay">true</item>
+ <item name="colorPrimary">@android:color/transparent</item>
+ </style>
+
+ <style name="AppTheme.ActionBar" parent="AppTheme">
+ <item name="windowActionBarOverlay">false</item>
+ </style>
+
+ <style name="AppTheme" parent="AppBaseTheme" />
+
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java
index 11ab8173fd..1398f40009 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java
@@ -32,7 +32,7 @@ public class InfoWindowTest {
@Test
public void testBoundMarker() {
MarkerOptions markerOptions = new MarkerOptions();
- Marker marker = markerOptions.getMarker();
+ Marker marker = markerOptions.position(new LatLng()).getMarker();
InfoWindow infoWindow = new InfoWindow(mMapView, mMapboxMap).setBoundMarker(marker);
assertEquals("marker should match", marker, infoWindow.getBoundMarker());
}
@@ -53,7 +53,7 @@ public class InfoWindowTest {
when(projection.toScreenLocation(latLng)).thenReturn(new PointF(0, 0));
InfoWindow infoWindow = new InfoWindow(mMapView, mMapboxMap);
- infoWindow.open(mMapView, new MarkerOptions().getMarker(), latLng, 0, 0);
+ infoWindow.open(mMapView, new MarkerOptions().position(new LatLng()).getMarker(), latLng, 0, 0);
assertEquals("infowindow should not be visible", true, infoWindow.isVisible());
}
@@ -65,7 +65,7 @@ public class InfoWindowTest {
when(projection.toScreenLocation(latLng)).thenReturn(new PointF(0, 0));
InfoWindow infoWindow = new InfoWindow(mMapView, mMapboxMap);
- infoWindow.open(mMapView, new MarkerOptions().getMarker(), latLng, 0, 0);
+ infoWindow.open(mMapView, new MarkerOptions().position(new LatLng()).getMarker(), latLng, 0, 0);
infoWindow.close();
assertEquals("infowindow should not be visible", false, infoWindow.isVisible());
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
index a55672e86b..c4c294fb98 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
@@ -3,6 +3,7 @@ package com.mapbox.mapboxsdk.annotations;
import android.graphics.Bitmap;
import android.os.Parcelable;
+import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.utils.MockParcel;
@@ -23,10 +24,15 @@ public class MarkerTest {
@Test
public void testMarker() {
- MarkerOptions markerOptions = new MarkerOptions();
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
assertNotNull("marker should not be null", markerOptions.getMarker());
}
+ @Test(expected = InvalidMarkerPositionException.class)
+ public void testInvalidMarker(){
+ new MarkerOptions().getMarker();
+ }
+
@Test
public void testPosition() {
MarkerOptions markerOptions = new MarkerOptions().position(new LatLng(10, 12));
@@ -37,7 +43,7 @@ public class MarkerTest {
@Test
public void testTitle() {
- MarkerOptions markerOptions = new MarkerOptions().title("Mapbox");
+ MarkerOptions markerOptions = new MarkerOptions().title("Mapbox").position(new LatLng());
Marker marker = markerOptions.getMarker();
assertEquals(marker.getTitle(), "Mapbox");
assertEquals(markerOptions.getTitle(), "Mapbox");
@@ -45,7 +51,7 @@ public class MarkerTest {
@Test
public void testSnippet() {
- MarkerOptions markerOptions = new MarkerOptions().snippet("Mapbox");
+ MarkerOptions markerOptions = new MarkerOptions().snippet("Mapbox").position(new LatLng());
Marker marker = markerOptions.getMarker();
assertEquals(marker.getSnippet(), "Mapbox");
}
@@ -62,7 +68,7 @@ public class MarkerTest {
public void testIcon() {
Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_4444);
Icon icon = IconFactory.recreate("test", bitmap);
- MarkerOptions markerOptions = new MarkerOptions().icon(icon);
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()).icon(icon);
Marker marker = markerOptions.getMarker();
assertEquals("Icon should match", icon, marker.getIcon());
assertEquals("Icon should match", icon, markerOptions.getIcon());
@@ -70,7 +76,7 @@ public class MarkerTest {
@Test
public void testHashCode() {
- Marker marker = new MarkerOptions().getMarker();
+ Marker marker = new MarkerOptions().position(new LatLng()).getMarker();
assertEquals("hash code should match", marker.hashCode(), 0);
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
index ed8e4ff323..e6c30f4aac 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
@@ -1,10 +1,9 @@
package com.mapbox.mapboxsdk.annotations;
-import android.graphics.Bitmap;
import android.os.Parcelable;
+import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.utils.MockParcel;
import org.junit.Test;
@@ -25,10 +24,15 @@ public class MarkerViewTest {
@Test
public void testMarker() {
- MarkerViewOptions markerOptions = new MarkerViewOptions();
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng());
assertNotNull("marker should not be null", markerOptions.getMarker());
}
+ @Test(expected = InvalidMarkerPositionException.class)
+ public void testInvalidMarker(){
+ new MarkerViewOptions().getMarker();
+ }
+
@Test
public void testPosition() {
MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng(10, 12));
@@ -39,46 +43,36 @@ public class MarkerViewTest {
@Test
public void testSnippet() {
- MarkerViewOptions markerOptions = new MarkerViewOptions().snippet("Mapbox");
+ MarkerViewOptions markerOptions = new MarkerViewOptions().snippet("Mapbox").position(new LatLng());
MarkerView marker = markerOptions.getMarker();
assertEquals(marker.getSnippet(), "Mapbox");
}
@Test
public void testTitle() {
- MarkerViewOptions markerOptions = new MarkerViewOptions().title("Mapbox");
+ MarkerViewOptions markerOptions = new MarkerViewOptions().title("Mapbox").position(new LatLng());
MarkerView marker = markerOptions.getMarker();
assertEquals(marker.getTitle(), "Mapbox");
assertEquals(markerOptions.getTitle(), "Mapbox");
}
@Test
- public void testIcon() {
- Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_4444);
- Icon icon = IconFactory.recreate("test", bitmap);
- MarkerViewOptions markerOptions = new MarkerViewOptions().icon(icon);
- MarkerView marker = markerOptions.getMarker();
- assertEquals("Icon should match", icon, marker.getIcon());
- assertEquals("Icon should match", icon, markerOptions.getIcon());
- }
-
- @Test
public void testFlat() {
- MarkerViewOptions markerOptions = new MarkerViewOptions().flat(true);
+ MarkerViewOptions markerOptions = new MarkerViewOptions().flat(true).position(new LatLng());
MarkerView marker = markerOptions.getMarker();
assertTrue("flat should be true", marker.isFlat());
}
@Test
public void testFlatDefault() {
- assertFalse("default value of flat should be false", new MarkerViewOptions().getMarker().isFlat());
+ assertFalse("default value of flat should be false", new MarkerViewOptions().position(new LatLng()).getMarker().isFlat());
}
@Test
public void testAnchor() {
float anchorU = 1;
float anchorV = 1;
- MarkerViewOptions markerOptions = new MarkerViewOptions().anchor(anchorU, anchorV);
+ MarkerViewOptions markerOptions = new MarkerViewOptions().anchor(anchorU, anchorV).position(new LatLng());
MarkerView marker = markerOptions.getMarker();
assertEquals("anchorU should match ", anchorU, marker.getAnchorU(), 0);
assertEquals("anchorU should match ", anchorV, marker.getAnchorV(), 0);
@@ -86,7 +80,7 @@ public class MarkerViewTest {
@Test
public void testAnchorDefault() {
- MarkerView marker = new MarkerViewOptions().getMarker();
+ MarkerView marker = new MarkerViewOptions().position(new LatLng()).getMarker();
assertEquals("anchorU should match ", 0.5, marker.getAnchorU(), 0);
assertEquals("anchorU should match ", 1, marker.getAnchorV(), 0);
}
@@ -95,7 +89,7 @@ public class MarkerViewTest {
public void testInfoWindowAnchor() {
float anchorU = 1;
float anchorV = 1;
- MarkerViewOptions markerOptions = new MarkerViewOptions().infoWindowAnchor(anchorU, anchorV);
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng()).infoWindowAnchor(anchorU, anchorV);
MarkerView marker = markerOptions.getMarker();
assertEquals("anchorU should match ", 1, marker.getInfoWindowAnchorU(), 0);
assertEquals("anchorU should match ", 1, marker.getInfoWindowAnchorV(), 0);
@@ -103,31 +97,15 @@ public class MarkerViewTest {
@Test
public void testInfoWindowAnchorDefault() {
- MarkerView marker = new MarkerViewOptions().getMarker();
+ MarkerView marker = new MarkerViewOptions().position(new LatLng()).getMarker();
assertEquals("anchorU should match ", 0.5, marker.getInfoWindowAnchorU(), 0);
assertEquals("anchorU should match ", 0, marker.getInfoWindowAnchorV(), 0);
}
@Test
- public void testSelectAnimRes() {
- int animatorRes = R.animator.rotate_360;
- MarkerViewOptions markerOptions = new MarkerViewOptions().selectAnimatorResource(animatorRes);
- MarkerView marker = markerOptions.getMarker();
- assertEquals("select animator resource should match ", animatorRes, marker.getSelectAnimRes(), 0);
- }
-
- @Test
- public void testDeselectAnimRes() {
- int animatorRes = R.animator.rotate_360;
- MarkerViewOptions markerOptions = new MarkerViewOptions().deselectAnimatorResource(animatorRes);
- MarkerView marker = markerOptions.getMarker();
- assertEquals("deselect animator resource should match ", animatorRes, marker.getDeselectAnimRes(), 0);
- }
-
- @Test
public void testRotation() {
int rotation = 90;
- MarkerViewOptions markerOptions = new MarkerViewOptions().rotation(rotation);
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng()).rotation(rotation);
MarkerView marker = markerOptions.getMarker();
assertEquals("rotation should match ", rotation, marker.getRotation(), 0);
}
@@ -135,27 +113,26 @@ public class MarkerViewTest {
@Test
public void testVisible() {
boolean visible = false;
- MarkerViewOptions markerOptions = new MarkerViewOptions().visible(visible);
+ MarkerViewOptions markerOptions = new MarkerViewOptions().visible(visible).position(new LatLng());
MarkerView marker = markerOptions.getMarker();
assertEquals("visible should match ", visible, marker.isVisible());
}
@Test
public void testVisibleDefault() {
- assertTrue(new MarkerViewOptions().getMarker().isVisible());
+ assertTrue(new MarkerViewOptions().position(new LatLng()).getMarker().isVisible());
}
@Test
public void testBuilder() {
MarkerView marker = new MarkerViewOptions().title("title").snippet("snippet").position(new LatLng(10, 12)).getMarker();
assertEquals(marker.getSnippet(), "snippet");
-
assertEquals(marker.getPosition(), new LatLng(10, 12));
}
@Test
public void testHashCode() {
- MarkerView marker = new MarkerViewOptions().getMarker();
+ MarkerView marker = new MarkerViewOptions().position(new LatLng()).getMarker();
assertEquals("hash code should match", marker.hashCode(), 0);
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
index a8cca4c3c8..4775bb39a4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
@@ -3,10 +3,10 @@ package com.mapbox.mapboxsdk.camera;
import android.content.res.TypedArray;
import android.os.Parcelable;
+import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.MathConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.utils.MathUtils;
import com.mapbox.mapboxsdk.utils.MockParcel;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/constants/StyleVersionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/constants/StyleVersionTest.java
index 078b4184ca..3d9b2a9364 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/constants/StyleVersionTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/constants/StyleVersionTest.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.constants;
+
import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
import org.junit.Test;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
index b667940a6f..b2cc1e9046 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
@@ -8,6 +8,9 @@ import com.mapbox.mapboxsdk.utils.MockParcel;
import org.junit.Before;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -106,6 +109,24 @@ public class LatLngBoundsTest {
}
@Test
+ public void testIncludes() {
+ List<LatLng> points = new ArrayList<>();
+ points.add(LAT_LNG_NULL_ISLAND);
+ points.add(LAT_LNG_NOT_NULL_ISLAND);
+
+ LatLngBounds latLngBounds1 = new LatLngBounds.Builder()
+ .includes(points)
+ .build();
+
+ LatLngBounds latLngBounds2 = new LatLngBounds.Builder()
+ .include(LAT_LNG_NULL_ISLAND)
+ .include(LAT_LNG_NOT_NULL_ISLAND)
+ .build();
+
+ assertEquals("LatLngBounds should match", latLngBounds1, latLngBounds2);
+ }
+
+ @Test
public void testNoIncluding() {
assertFalse("LatLng should not be included", mLatLngBounds.contains(new LatLng(3, 1)));
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
index b69ba4e3f8..33fd77521d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
@@ -13,6 +13,7 @@ import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
@@ -572,16 +573,21 @@ public class MapboxMapTest {
@Test
public void testAddMarker() {
- MarkerOptions markerOptions = new MarkerOptions();
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
Marker marker = mMapboxMap.addMarker(markerOptions);
assertTrue("Marker should be contained", mMapboxMap.getMarkers().contains(marker));
}
+ @Test(expected = InvalidMarkerPositionException.class)
+ public void testAddMarkerInvalidPosition(){
+ new MarkerOptions().getMarker();
+ }
+
@Test
public void testAddMarkers() {
List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions1 = new MarkerOptions().title("a");
- MarkerOptions markerOptions2 = new MarkerOptions().title("b");
+ MarkerOptions markerOptions1 = new MarkerOptions().position(new LatLng()).title("a");
+ MarkerOptions markerOptions2 = new MarkerOptions().position(new LatLng()).title("b");
markerList.add(markerOptions1);
markerList.add(markerOptions2);
mMapboxMap.addMarkers(markerList);
@@ -600,7 +606,7 @@ public class MapboxMapTest {
@Test
public void testAddMarkersSingleMarker() {
List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions = new MarkerOptions().title("a");
+ MarkerOptions markerOptions = new MarkerOptions().title("a").position(new LatLng());
markerList.add(markerOptions);
mMapboxMap.addMarkers(markerList);
assertEquals("Markers size should be 1", 1, mMapboxMap.getMarkers().size());
@@ -701,7 +707,7 @@ public class MapboxMapTest {
@Test
public void testRemoveMarker() {
- MarkerOptions markerOptions = new MarkerOptions();
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
Marker marker = mMapboxMap.addMarker(markerOptions);
mMapboxMap.removeMarker(marker);
assertTrue("Markers should be empty", mMapboxMap.getMarkers().isEmpty());
@@ -725,7 +731,7 @@ public class MapboxMapTest {
@Test
public void testRemoveAnnotation() {
- MarkerOptions markerOptions = new MarkerOptions();
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
Marker marker = mMapboxMap.addMarker(markerOptions);
mMapboxMap.removeAnnotation(marker);
assertTrue("Annotations should be empty", mMapboxMap.getAnnotations().isEmpty());
@@ -733,7 +739,7 @@ public class MapboxMapTest {
@Test
public void testRemoveAnnotationById() {
- MarkerOptions markerOptions = new MarkerOptions();
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
mMapboxMap.addMarker(markerOptions);
// id will always be 0 in unit tests
mMapboxMap.removeAnnotation(0);
@@ -743,8 +749,8 @@ public class MapboxMapTest {
@Test
public void testRemoveAnnotations() {
List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions1 = new MarkerOptions().title("a");
- MarkerOptions markerOptions2 = new MarkerOptions().title("b");
+ MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng());
+ MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng());
markerList.add(markerOptions1);
markerList.add(markerOptions2);
mMapboxMap.addMarkers(markerList);
@@ -755,8 +761,8 @@ public class MapboxMapTest {
@Test
public void testClear() {
List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions1 = new MarkerOptions().title("a");
- MarkerOptions markerOptions2 = new MarkerOptions().title("b");
+ MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng());
+ MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng());
markerList.add(markerOptions1);
markerList.add(markerOptions2);
mMapboxMap.addMarkers(markerList);
@@ -767,12 +773,12 @@ public class MapboxMapTest {
@Test
public void testRemoveAnnotationsByList() {
List<BaseMarkerOptions> markerList = new ArrayList<>();
- MarkerOptions markerOptions1 = new MarkerOptions().title("a");
- MarkerOptions markerOptions2 = new MarkerOptions().title("b");
+ MarkerOptions markerOptions1 = new MarkerOptions().title("a").position(new LatLng());
+ MarkerOptions markerOptions2 = new MarkerOptions().title("b").position(new LatLng());
markerList.add(markerOptions1);
markerList.add(markerOptions2);
List<Marker> markers = mMapboxMap.addMarkers(markerList);
- Marker marker = mMapboxMap.addMarker(new MarkerOptions().title("c"));
+ Marker marker = mMapboxMap.addMarker(new MarkerOptions().position(new LatLng()).title("c"));
mMapboxMap.removeAnnotations(markers);
assertTrue("Annotations should not be empty", mMapboxMap.getAnnotations().size() == 1);
assertTrue("Marker should be contained", mMapboxMap.getAnnotations().contains(marker));
@@ -780,7 +786,7 @@ public class MapboxMapTest {
@Test
public void testGetAnnotationById() {
- MarkerOptions markerOptions = new MarkerOptions();
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
Marker initialMarker = mMapboxMap.addMarker(markerOptions);
Marker retrievedMarker = (Marker) mMapboxMap.getAnnotation(0);
assertEquals("Markers should match", initialMarker, retrievedMarker);
@@ -814,7 +820,7 @@ public class MapboxMapTest {
@Test
public void testSelectMarker() {
mMapboxMap.setOnMarkerClickListener(mOnMarkerClickListener);
- MarkerOptions markerOptions = new MarkerOptions();
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
Marker marker = mMapboxMap.addMarker(markerOptions);
when(mOnMarkerClickListener.onMarkerClick(marker)).thenReturn(true);
mMapboxMap.selectMarker(marker);
@@ -824,7 +830,7 @@ public class MapboxMapTest {
@Test
public void testDeselectMarker() {
mMapboxMap.setOnMarkerClickListener(mOnMarkerClickListener);
- MarkerOptions markerOptions = new MarkerOptions();
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
Marker marker = mMapboxMap.addMarker(markerOptions);
when(mOnMarkerClickListener.onMarkerClick(marker)).thenReturn(true);
mMapboxMap.selectMarker(marker);
@@ -835,7 +841,7 @@ public class MapboxMapTest {
@Test
public void testDeselectMarkers() {
mMapboxMap.setOnMarkerClickListener(mOnMarkerClickListener);
- MarkerOptions markerOptions = new MarkerOptions();
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
Marker marker1 = mMapboxMap.addMarker(markerOptions);
Marker marker2 = mMapboxMap.addMarker(markerOptions);
when(mOnMarkerClickListener.onMarkerClick(marker1)).thenReturn(true);
@@ -853,7 +859,7 @@ public class MapboxMapTest {
@Test
public void testOnMarkerClick() {
mMapboxMap.setOnMarkerClickListener(mOnMarkerClickListener);
- Marker marker = new MarkerOptions().getMarker();
+ Marker marker = new MarkerOptions().position(new LatLng()).getMarker();
when(mOnMarkerClickListener.onMarkerClick(marker)).thenReturn(true);
mMapboxMap.selectMarker(marker);
verify(mOnMarkerClickListener, times(1)).onMarkerClick(marker);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/StyleInitializerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/StyleInitializerTest.java
deleted file mode 100644
index 71d61a3d4b..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/StyleInitializerTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import android.content.Context;
-import android.content.res.Resources;
-
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.model.constants.AppConstant;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Locale;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class StyleInitializerTest {
-
- @InjectMocks
- Context context = mock(Context.class);
-
- @InjectMocks
- Resources resources = mock(Resources.class);
-
- MapView.StyleInitializer initializer;
-
- @Before
- public void beforeTest() {
- MockitoAnnotations.initMocks(this);
- when(context.getResources()).thenReturn(resources);
- when(resources.getInteger(R.integer.style_version)).thenReturn(AppConstant.STYLE_VERSION);
- initializer = new MapView.StyleInitializer(context);
- }
-
- @Test
- public void testSanity() {
- assertNotNull("initializer should not be null", initializer);
- }
-
- @Test
- public void testDefaultStyle() {
- assertTrue(initializer.isDefaultStyle());
- assertEquals(String.format(Locale.US, "mapbox://styles/mapbox/streets-v%d", AppConstant.STYLE_VERSION), "mapbox://styles/mapbox/streets-v9");
- }
-
- @Test
- public void testUpdateStyle() {
- String customStyle = "test";
- initializer.setStyle(customStyle);
- assertFalse(initializer.isDefaultStyle());
- assertEquals(customStyle, initializer.getStyle());
- }
-
- @Test
- public void testUpdateStyleNull() {
- String customStyle = null;
- initializer.setStyle(customStyle);
- assertTrue(initializer.isDefaultStyle());
- assertEquals(String.format(Locale.US, "mapbox://styles/mapbox/streets-v%d", AppConstant.STYLE_VERSION), "mapbox://styles/mapbox/streets-v9");
- }
-
- @Test
- public void testOverrideDefaultStyle() {
- String customStyle = "test";
- initializer.setStyle(customStyle, true);
- assertTrue(initializer.isDefaultStyle());
- assertEquals(customStyle, initializer.getStyle());
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
index a03eb6acae..fd70308931 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
@@ -16,6 +16,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class MyLocationViewSettingsTest {
@@ -41,6 +42,9 @@ public class MyLocationViewSettingsTest {
public void testForegroundDrawables() {
Drawable foregroundDrawable = mock(Drawable.class);
Drawable foregroundBearingDrawable = mock(Drawable.class);
+ Drawable.ConstantState constantState = mock(Drawable.ConstantState.class);
+ when(foregroundDrawable.getConstantState()).thenReturn(constantState);
+ when(constantState.newDrawable()).thenReturn(foregroundDrawable);
locationViewSettings.setForegroundDrawable(foregroundDrawable, foregroundBearingDrawable);
assertEquals("foreground should match", foregroundDrawable, locationViewSettings.getForegroundDrawable());
assertEquals("foreground bearing should match", foregroundBearingDrawable, locationViewSettings.getForegroundBearingDrawable());
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
new file mode 100644
index 0000000000..3e312d3b0a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
@@ -0,0 +1,90 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static com.mapbox.mapboxsdk.style.layers.Filter.*;
+
+/**
+ * Tests for Filter
+ */
+public class FilterTest {
+
+ @Test
+ public void testAll() {
+ assertArrayEquals(all().toArray(), new Object[]{"all"});
+ assertArrayEquals(
+ all(eq("key", 2), neq("key", 3)).toArray(),
+ new Object[]{"all", new Object[]{"==", "key", 2}, new Object[]{"!=", "key", 3}}
+ );
+ }
+
+ @Test
+ public void testAny() {
+ assertArrayEquals(any().toArray(), new Object[]{"any"});
+ assertArrayEquals(
+ any(eq("key", 2), neq("key", 3)).toArray(),
+ new Object[]{"any", new Object[]{"==", "key", 2}, new Object[]{"!=", "key", 3}}
+ );
+ }
+
+ @Test
+ public void testNone() {
+ assertArrayEquals(none().toArray(), new Object[]{"none"});
+ assertArrayEquals(
+ none(eq("key", 2), neq("key", 3)).toArray(),
+ new Object[]{"none", new Object[]{"==", "key", 2}, new Object[]{"!=", "key", 3}}
+ );
+ }
+
+ @Test
+ public void testHas() {
+ assertArrayEquals(has("key").toArray(), new Object[]{"has", "key"});
+ }
+
+ @Test
+ public void testHasNot() {
+ assertArrayEquals(notHas("key").toArray(), new Object[]{"!has", "key"});
+ }
+
+ @Test
+ public void testEq() {
+ assertArrayEquals(eq("key", 1).toArray(), new Object[]{"==", "key", 1});
+
+ }
+
+ @Test
+ public void testNeq() {
+ assertArrayEquals(neq("key", 1).toArray(), new Object[]{"!=", "key", 1});
+ }
+
+ @Test
+ public void testGt() {
+ assertArrayEquals(gt("key", 1).toArray(), new Object[]{">", "key", 1});
+ }
+
+ @Test
+ public void testGte() {
+ assertArrayEquals(gte("key", 1).toArray(), new Object[]{">=", "key", 1});
+ }
+
+ @Test
+ public void testLt() {
+ assertArrayEquals(lt("key", 1).toArray(), new Object[]{"<", "key", 1});
+ }
+
+ @Test
+ public void testLte() {
+ assertArrayEquals(lte("key", 1).toArray(), new Object[]{"<=", "key", 1});
+ }
+
+ @Test
+ public void testIn() {
+ assertArrayEquals(in("key", 1, 2, "Aap").toArray(), new Object[]{"in", "key", 1, 2, "Aap"});
+ }
+
+ @Test
+ public void testNotIn() {
+ assertArrayEquals(notIn("key", 1, 2, "Noot").toArray(), new Object[]{"!in", "key", 1, 2, "Noot"});
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
new file mode 100644
index 0000000000..1106009ea8
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
@@ -0,0 +1,29 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+import org.junit.Test;
+
+import static com.mapbox.mapboxsdk.style.layers.Function.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests Function
+ */
+public class FunctionTest {
+
+ @Test
+ public void testZoomFunction() {
+ Function zoomF = zoom(
+ stop(1f, lineBlur(1f)),
+ stop(10f, lineBlur(20f))
+ );
+
+ assertNotNull(zoomF.toValueObject());
+ assertArrayEquals(
+ new Object[]{new Object[]{1f, 1f}, new Object[]{10f, 20f}},
+ (Object[]) zoomF.toValueObject().get("stops")
+ );
+ }
+
+}
diff --git a/platform/android/bitrise.yml b/platform/android/bitrise.yml
index 3c06a04675..5eb5cfa508 100644
--- a/platform/android/bitrise.yml
+++ b/platform/android/bitrise.yml
@@ -75,7 +75,7 @@ workflows:
signing.secretKeyRingFile=secring.gpg" >> platform/android/MapboxGLAndroidSDK/gradle.properties
export BUILDTYPE=Release
- make apackage -j4
+ make apackage
cd platform/android
./gradlew uploadArchives
diff --git a/platform/android/build.gradle b/platform/android/build.gradle
index 23aef9d1e6..c99fc67a61 100644
--- a/platform/android/build.gradle
+++ b/platform/android/build.gradle
@@ -7,7 +7,7 @@ buildscript {
maven { url 'https://jitpack.io' }
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.1.0'
+ classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'com.github.JakeWharton:sdk-manager-plugin:220bf7a88a7072df3ed16dc8466fb144f2817070'
// NOTE: Do not place your application dependencies here; they belong
diff --git a/platform/android/platform.gyp b/platform/android/platform.gyp
index 964492a21e..f8e8b33b08 100644
--- a/platform/android/platform.gyp
+++ b/platform/android/platform.gyp
@@ -1,6 +1,7 @@
{
'variables': {
'loop_lib': 'android',
+ 'OS': 'android',
'headless_lib': 'none',
'coverage': 0,
},
@@ -26,11 +27,23 @@
'sources': [
'src/native_map_view.cpp',
'src/jni.cpp',
+ 'src/java_types.cpp',
'src/attach_env.cpp',
'src/log_android.cpp',
'src/http_file_source.cpp',
'src/asset_file_source.cpp',
- '../default/thread.cpp',
+ 'src/thread.cpp',
+ 'src/style/value.cpp',
+ 'src/style/sources/sources.cpp',
+ 'src/style/layers/layers.cpp',
+ 'src/style/layers/layer.cpp',
+ 'src/style/layers/background_layer.cpp',
+ 'src/style/layers/circle_layer.cpp',
+ 'src/style/layers/fill_layer.cpp',
+ 'src/style/layers/line_layer.cpp',
+ 'src/style/layers/raster_layer.cpp',
+ 'src/style/layers/symbol_layer.cpp',
+ 'src/style/layers/custom_layer.cpp',
'../default/string_stdlib.cpp',
'../default/image.cpp',
'../default/png_reader.cpp',
@@ -49,6 +62,7 @@
'cflags_cc': [
'<@(boost_cflags)',
+ '<@(geojson_cflags)',
'<@(rapidjson_cflags)',
'<@(nunicode_cflags)',
'<@(sqlite_cflags)',
@@ -78,6 +92,7 @@
'<@(libpng_ldflags)',
'<@(libjpeg-turbo_static_libs)',
'<@(libjpeg-turbo_ldflags)',
+ '<@(geojson_static_libs)'
],
},
},
diff --git a/platform/android/scripts/configure.sh b/platform/android/scripts/configure.sh
index 2bbc134597..1c26108e11 100644
--- a/platform/android/scripts/configure.sh
+++ b/platform/android/scripts/configure.sh
@@ -9,8 +9,11 @@ SQLITE_VERSION=3.9.1
ZLIB_VERSION=system
NUNICODE_VERSION=1.6
LIBZIP_VERSION=0.11.2
-GEOMETRY_VERSION=0.5.0
-GEOJSONVT_VERSION=4.1.2
+GEOMETRY_VERSION=0.8.0
+GEOJSON_VERSION=0.1.4
+GEOJSONVT_VERSION=6.1.2
+SUPERCLUSTER_VERSION=0.2.0
+KDBUSH_VERSION=0.1.1
VARIANT_VERSION=1.1.0
RAPIDJSON_VERSION=1.0.2
JNI_HPP_VERSION=2.0.0
diff --git a/platform/android/scripts/debug.sh b/platform/android/scripts/debug.sh
index ade9ab2d6f..efed96969a 100755
--- a/platform/android/scripts/debug.sh
+++ b/platform/android/scripts/debug.sh
@@ -11,7 +11,7 @@ export PATH="${MASON_DIR}:${PATH}"
export MASON_ANDROID_ABI=x86
export MASON_ANDROID_ARCH=x86
export MASON_ANDROID_PLATFORM=9
-export MASON_NDK_PACKAGE_VERSION=${MASON_ANDROID_ARCH}-${MASON_ANDROID_PLATFORM}-r10e
+export MASON_NDK_PACKAGE_VERSION=${MASON_ANDROID_ARCH}-${MASON_ANDROID_PLATFORM}-r12b
if [[ $1 == '--prepare' ]]; then
mkdir -p ~/.android/debugging/{vendor,system}_lib
diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js
new file mode 100644
index 0000000000..0abc4969be
--- /dev/null
+++ b/platform/android/scripts/generate-style-code.js
@@ -0,0 +1,206 @@
+'use strict';
+
+const fs = require('fs');
+const ejs = require('ejs');
+const spec = require('mapbox-gl-style-spec').latest;
+const _ = require('lodash');
+
+global.iff = function (condition, val) {
+ return condition() ? val : "";
+}
+
+
+global.camelize = function (str) {
+ return str.replace(/(?:^|-)(.)/g, function (_, x) {
+ return x.toUpperCase();
+ });
+}
+
+global.camelizeWithLeadingLowercase = function (str) {
+ return str.replace(/-(.)/g, function (_, x) {
+ return x.toUpperCase();
+ });
+}
+
+global.snakeCaseUpper = function snakeCaseUpper(str) {
+ return str.replace(/-/g, "_").toUpperCase();
+}
+
+global.propertyType = function propertyType(property) {
+ switch (property.type) {
+ case 'boolean':
+ return 'Boolean';
+ case 'number':
+ return 'Float';
+ case 'string':
+ return 'String';
+ case 'enum':
+ return 'String';
+ case 'color':
+ return 'String';
+ case 'array':
+ return `${propertyType({type:property.value})}[]`;
+ default:
+ throw new Error(`unknown type for ${property.name}`);
+ }
+}
+
+global.propertyJNIType = function propertyJNIType(property) {
+ switch (property.type) {
+ case 'boolean':
+ return 'jboolean';
+ case 'jfloat':
+ return 'Float';
+ case 'String':
+ return 'String';
+ case 'enum':
+ return 'String';
+ case 'color':
+ return 'String';
+ case 'array':
+ return `jarray<${propertyType({type:property.value})}[]>`;
+ default:
+ return 'jobject*';
+ }
+}
+
+global.propertyNativeType = function (property) {
+ if (/-translate-anchor$/.test(property.name)) {
+ return 'TranslateAnchorType';
+ }
+ if (/-(rotation|pitch)-alignment$/.test(property.name)) {
+ return 'AlignmentType';
+ }
+ switch (property.type) {
+ case 'boolean':
+ return 'bool';
+ case 'number':
+ return 'float';
+ case 'string':
+ return 'std::string';
+ case 'enum':
+ return `${camelize(property.name)}Type`;
+ case 'color':
+ return `Color`;
+ case 'array':
+ if (property.length) {
+ return `std::array<${propertyType({type: property.value})}, ${property.length}>`;
+ } else {
+ return `std::vector<${propertyType({type: property.value})}>`;
+ }
+ default: throw new Error(`unknown type for ${property.name}`)
+ }
+}
+
+global.propertyTypeAnnotation = function propertyTypeAnnotation(property) {
+ switch (property.type) {
+ case 'enum':
+ return `@Property.${snakeCaseUpper(property.name)}`;
+ default:
+ return "";
+ }
+};
+
+global.defaultValueJava = function(property) {
+ if(property.name.endsWith("-pattern")) {
+ return '"pedestrian-polygon"';
+ }
+ if(property.name.endsWith("-font")) {
+ return 'new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}';
+ }
+ switch (property.type) {
+ case 'boolean':
+ return 'true';
+ case 'number':
+ return '0.3f';
+ case 'string':
+ return '"' + property['default'] + '"';
+ case 'enum':
+ return snakeCaseUpper(property.name) + "_" + snakeCaseUpper(property.values[0]);
+ case 'color':
+ return '"rgba(0, 0, 0, 1)"';
+ case 'array':
+ switch (property.value) {
+ case 'string':
+ return '[' + property['default'] + "]";
+ case 'number':
+ var result ='new Float[]{';
+ for (var i = 0; i < property.length; i++) {
+ result += "0f";
+ if (i +1 != property.length) {
+ result += ",";
+ }
+ }
+ return result + "}";
+ }
+ default: throw new Error(`unknown type for ${property.name}`)
+ }
+}
+
+//Process Layers
+const layers = spec.layer.type.values.map((type) => {
+ const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
+ if (name !== 'visibility') {
+ spec[`layout_${type}`][name].name = name;
+ memo.push(spec[`layout_${type}`][name]);
+ }
+ return memo;
+ }, []);
+
+ const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
+ spec[`paint_${type}`][name].name = name;
+ memo.push(spec[`paint_${type}`][name]);
+ return memo;
+ }, []);
+
+ return {
+ type: type,
+ layoutProperties: layoutProperties,
+ paintProperties: paintProperties,
+ properties: layoutProperties.concat(paintProperties)
+ };
+});
+
+const layerHpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.hpp.ejs', 'utf8'), {strict: true});
+const layerCpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.cpp.ejs', 'utf8'), {strict: true});
+const layerJava = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs', 'utf8'), {strict: true});
+const layerJavaUnitTests = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/layer.junit.ejs', 'utf8'), {strict: true});
+
+for (const layer of layers) {
+ fs.writeFileSync(`platform/android/src/style/layers/${layer.type}_layer.hpp`, layerHpp(layer));
+ fs.writeFileSync(`platform/android/src/style/layers/${layer.type}_layer.cpp`, layerCpp(layer));
+ fs.writeFileSync(`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/${camelize(layer.type)}Layer.java`, layerJava(layer));
+ fs.writeFileSync(`platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/style/${camelize(layer.type)}LayerTest.java`, layerJavaUnitTests(layer));
+}
+
+//Process all layer properties
+const layoutProperties = _(layers).map('layoutProperties').flatten().value();
+const paintProperties = _(layers).map('paintProperties').flatten().value();
+
+const propertiesTemplate = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs', 'utf8'), {strict: true});
+fs.writeFileSync(
+ `platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java`,
+ propertiesTemplate({layoutProperties: layoutProperties, paintProperties: paintProperties})
+);
+
+//Create types for the enum properties
+const enumProperties = _(layoutProperties).union(paintProperties).filter({'type': 'enum'}).value();
+const enumPropertyJavaTemplate = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property.java.ejs', 'utf8'), {strict: true});
+fs.writeFileSync(
+ `platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java`,
+ enumPropertyJavaTemplate({properties: enumProperties})
+);
+
+//De-dup types before generating cpp headers
+const enumPropertiesDeDup = _(enumProperties).uniq(global.propertyNativeType).value();
+const enumPropertyHppTypeStringValueTemplate = ejs.compile(fs.readFileSync('platform/android/src/style/conversion/types_string_values.hpp.ejs', 'utf8'), {strict: true});
+fs.writeFileSync(
+ `platform/android/src/style/conversion/types_string_values.hpp`,
+ enumPropertyHppTypeStringValueTemplate({properties: enumPropertiesDeDup})
+);
+
+const enumPropertyHppTypeTemplate = ejs.compile(fs.readFileSync('platform/android/src/style/conversion/types.hpp.ejs', 'utf8'), {strict: true});
+fs.writeFileSync(
+ `platform/android/src/style/conversion/types.hpp`,
+ enumPropertyHppTypeTemplate({properties: enumPropertiesDeDup})
+);
diff --git a/platform/android/src/asset_file_source.cpp b/platform/android/src/asset_file_source.cpp
index 2babe7db41..ec4925a02e 100644
--- a/platform/android/src/asset_file_source.cpp
+++ b/platform/android/src/asset_file_source.cpp
@@ -100,7 +100,7 @@ private:
AssetFileSource::AssetFileSource(const std::string& root)
: thread(std::make_unique<util::Thread<Impl>>(
- util::ThreadContext{"AssetFileSource"},
+ util::ThreadContext{"AssetFileSource", util::ThreadPriority::Low},
root)) {
}
diff --git a/platform/android/src/async_task.cpp b/platform/android/src/async_task.cpp
index 6a9263baff..7b78eadb0c 100644
--- a/platform/android/src/async_task.cpp
+++ b/platform/android/src/async_task.cpp
@@ -1,9 +1,9 @@
#include "run_loop_impl.hpp"
#include <mbgl/util/async_task.hpp>
-#include <mbgl/util/atomic.hpp>
#include <mbgl/util/run_loop.hpp>
+#include <atomic>
#include <functional>
namespace mbgl {
@@ -45,7 +45,7 @@ private:
// TODO: Use std::atomic_flag if we ever drop
// support for ARMv5
- util::Atomic<bool> queued;
+ std::atomic<bool> queued;
std::function<void()> task;
};
diff --git a/platform/android/src/conversion/constant.hpp b/platform/android/src/conversion/constant.hpp
new file mode 100644
index 0000000000..9a570d3717
--- /dev/null
+++ b/platform/android/src/conversion/constant.hpp
@@ -0,0 +1,95 @@
+#pragma once
+
+#include "conversion.hpp"
+
+#include <mbgl/util/optional.hpp>
+#include <jni/jni.hpp>
+
+#include <string>
+#include <array>
+#include <vector>
+#include <sstream>
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+template <>
+struct Converter<jni::jobject*, bool> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const bool& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Boolean")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Z)V");
+ return {&jni::NewObject(env, *javaClass, *constructor, (jboolean) value)};
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, float> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const float& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Float")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(F)V");
+ return {&jni::NewObject(env, *javaClass, *constructor, (jfloat) value)};
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, std::string> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::string& value) const {
+ return {jni::Make<jni::String>(env, value).Get()};
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, Color> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const Color& value) const {
+ std::stringstream sstream;
+ sstream << "rgba(" << value.r << ", " << value.g << ", " << value.b << ", " << value.a << ")";
+ std::string result = sstream.str();
+ return convert<jni::jobject*, std::string>(env, result);
+ }
+};
+
+template <std::size_t N>
+struct Converter<jni::jobject*, std::array<float, N>> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::array<float, N>& value) const {
+ std::vector<float> v;
+ for (const float& id : value) {
+ v.push_back(id);
+ }
+ return convert<jni::jobject*, std::vector<float>>(env, v);
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, std::vector<std::string>> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::vector<std::string>& value) const {
+ static jni::jclass* stringCass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/String")).release();
+ jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, value.size(), *stringCass);
+
+ for(size_t i = 0; i < value.size(); i = i + 1) {
+ Result<jni::jobject*> converted = convert<jni::jobject*, std::string>(env, value.at(i));
+ jni::SetObjectArrayElement(env, jarray, i, *converted);
+ }
+
+ return &jarray;
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, std::vector<float>> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::vector<float>& value) const {
+ static jni::jclass* floatClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Float")).release();
+ jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, value.size(), *floatClass);
+
+ for(size_t i = 0; i < value.size(); i = i + 1) {
+ Result<jni::jobject*> converted = convert<jni::jobject*, float>(env, value.at(i));
+ jni::SetObjectArrayElement(env, jarray, i, *converted);
+ }
+
+ return &jarray;
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/platform/android/src/conversion/conversion.hpp b/platform/android/src/conversion/conversion.hpp
new file mode 100644
index 0000000000..ea8a31bcf2
--- /dev/null
+++ b/platform/android/src/conversion/conversion.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <mbgl/util/variant.hpp>
+
+#include <jni/jni.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+struct Error { std::string message; };
+
+template <class T>
+class Result : private variant<T, Error> {
+public:
+ using variant<T, Error>::variant;
+
+ explicit operator bool() const {
+ return this->template is<T>();
+ }
+
+ T& operator*() {
+ assert(this->template is<T>());
+ return this->template get<T>();
+ }
+
+ const T& operator*() const {
+ assert(this->template is<T>());
+ return this->template get<T>();
+ }
+
+ const Error& error() const {
+ assert(this->template is<Error>());
+ return this->template get<Error>();
+ }
+};
+
+template <class T, class V>
+struct Converter;
+
+template <class T, typename V, class...Args>
+Result<T> convert(jni::JNIEnv& env, const V& value, Args&&...args) {
+ return Converter<T, V>()(env, value, std::forward<Args>(args)...);
+}
+
+} // namespace conversion
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/java_types.cpp b/platform/android/src/java_types.cpp
new file mode 100644
index 0000000000..6383426387
--- /dev/null
+++ b/platform/android/src/java_types.cpp
@@ -0,0 +1,37 @@
+#include "java_types.hpp"
+
+namespace mbgl {
+namespace android {
+namespace java {
+
+ jni::jclass* ObjectArray::jclass;
+
+ jni::jclass* String::jclass;
+
+ jni::jclass* Boolean::jclass;
+ jni::jmethodID* Boolean::booleanValueMethodId;
+
+ jni::jclass* Number::jclass;
+ jni::jmethodID* Number::floatValueMethodId;
+
+ jni::jclass* Map::jclass;
+ jni::jmethodID* Map::getMethodId;
+
+ void registerNatives(JNIEnv& env) {
+ ObjectArray::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "[Ljava/lang/Object;")).release();
+
+ String::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/String")).release();
+
+ Boolean::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Boolean")).release();
+ Boolean::booleanValueMethodId = &jni::GetMethodID(env, *Boolean::jclass, "booleanValue", "()Z");
+
+ Number::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Number")).release();
+ Number::floatValueMethodId = &jni::GetMethodID(env, *Number::jclass, "floatValue", "()F");
+
+ Map::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Map")).release();
+ Map::getMethodId = &jni::GetMethodID(env, *Map::jclass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+ }
+
+}
+}
+}
diff --git a/platform/android/src/java_types.hpp b/platform/android/src/java_types.hpp
new file mode 100644
index 0000000000..2e2c5fc2d6
--- /dev/null
+++ b/platform/android/src/java_types.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+namespace java {
+
+ struct ObjectArray {
+ static jni::jclass* jclass;
+ };
+
+ struct String {
+ static jni::jclass* jclass;
+ };
+
+ struct Boolean {
+ static jni::jclass* jclass;
+ static jni::jmethodID* booleanValueMethodId;
+ };
+
+ struct Number {
+ static jni::jclass* jclass;
+ static jni::jmethodID* floatValueMethodId;
+ };
+
+ struct Map {
+ static jni::jclass* jclass;
+ static jni::jmethodID* getMethodId;
+ };
+
+ void registerNatives(JNIEnv&);
+}
+}
+} \ No newline at end of file
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index e79689d7ad..5907a0ff9d 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -9,12 +9,15 @@
#include <sys/system_properties.h>
#include "jni.hpp"
+#include "java_types.hpp"
#include "native_map_view.hpp"
+#include "style/layers/layers.hpp"
+#include "style/sources/sources.hpp"
#include <mbgl/map/map.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/annotation/annotation.hpp>
-#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/style/layer.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/platform/event.hpp>
#include <mbgl/platform/log.hpp>
@@ -42,6 +45,7 @@ std::string androidRelease;
jni::jmethodID* onInvalidateId = nullptr;
jni::jmethodID* onMapChangedId = nullptr;
jni::jmethodID* onFpsChangedId = nullptr;
+jni::jmethodID* onSnapshotReadyId = nullptr;
jni::jclass* latLngClass = nullptr;
jni::jmethodID* latLngConstructorId = nullptr;
@@ -98,13 +102,6 @@ jni::jfieldID* rectFTopId = nullptr;
jni::jfieldID* rectFRightId = nullptr;
jni::jfieldID* rectFBottomId = nullptr;
-jni::jclass* customLayerClass = nullptr;
-jni::jfieldID* customLayerIdId = nullptr;
-jni::jfieldID* customLayerContextId = nullptr;
-jni::jfieldID* customLayerInitializeFunctionId = nullptr;
-jni::jfieldID* customLayerRenderFunctionId = nullptr;
-jni::jfieldID* customLayerDeinitializeFunctionId = nullptr;
-
// Offline declarations start
jni::jfieldID* offlineManagerClassPtrId = nullptr;
@@ -478,15 +475,10 @@ void nativeMoveBy(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdoubl
nativeMapView->getMap().moveBy(center, mbgl::Milliseconds(duration));
}
-void nativeSetLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* latLng,
- jlong duration) {
+void nativeSetLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble latitude, jdouble longitude, jlong duration) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetLatLng");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
-
- jdouble latitude = jni::GetField<jdouble>(*env, latLng, *latLngLatitudeId);
- jdouble longitude = jni::GetField<jdouble>(*env, latLng, *latLngLongitudeId);
-
nativeMapView->getMap().setLatLng(mbgl::LatLng(latitude, longitude), nativeMapView->getInsets(), mbgl::Duration(duration));
}
@@ -660,29 +652,16 @@ void nativeResetNorth(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
nativeMapView->getMap().resetNorth();
}
-void nativeUpdateMarker(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* marker) {
+void nativeUpdateMarker(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong markerId, jdouble lat, jdouble lon, jni::jstring* jid) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdateMarker");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
-
- jlong markerId = jni::GetField<jlong>(*env, marker, *markerIdId);
if (markerId == -1) {
return;
}
-
- jni::jobject* position = jni::GetField<jni::jobject*>(*env, marker, *markerPositionId);
- jni::jobject* icon = jni::GetField<jni::jobject*>(*env, marker, *markerIconId);
-
- jni::jstring* jid = reinterpret_cast<jni::jstring*>(jni::GetField<jni::jobject*>(*env, icon, *iconIdId));
std::string iconId = std_string_from_jstring(env, jid);
-
- jdouble latitude = jni::GetField<jdouble>(*env, position, *latLngLatitudeId);
- jdouble longitude = jni::GetField<jdouble>(*env, position, *latLngLongitudeId);
-
- nativeMapView->getMap().updateAnnotation(markerId, mbgl::SymbolAnnotation {
- mbgl::Point<double>(longitude, latitude),
- iconId
- });
+ // Because Java only has int, not unsigned int, we need to bump the annotation id up to a long.
+ nativeMapView->getMap().updateAnnotation(markerId, mbgl::SymbolAnnotation { mbgl::Point<double>(lon, lat), iconId });
}
jni::jarray<jlong>* nativeAddMarkers(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jni::jobject>* jarray) {
@@ -724,7 +703,7 @@ static mbgl::Color toColor(jint color) {
float g = (color >> 8) & 0xFF;
float b = (color) & 0xFF;
float a = (color >> 24) & 0xFF;
- return {{ r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f }};
+ return { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
}
template <class Geometry>
@@ -734,10 +713,12 @@ Geometry toGeometry(JNIEnv *env, jni::jobject* jlist) {
reinterpret_cast<jni::jarray<jni::jobject>*>(jni::CallMethod<jni::jobject*>(*env, jlist, *listToArrayId));
NullCheck(*env, jarray);
+ std::size_t size = jni::GetArrayLength(*env, *jarray);
+
Geometry geometry;
- geometry.reserve(jni::GetArrayLength(*env, *jarray));
+ geometry.reserve(size);
- for (std::size_t i = 0; i < geometry.size(); i++) {
+ for (std::size_t i = 0; i < size; i++) {
jni::jobject* latLng = reinterpret_cast<jni::jobject*>(jni::GetObjectArrayElement(*env, *jarray, i));
NullCheck(*env, latLng);
@@ -770,9 +751,9 @@ jni::jarray<jlong>* nativeAddPolylines(JNIEnv *env, jni::jobject* obj, jlong nat
jni::jobject* points = jni::GetField<jni::jobject*>(*env, polyline, *polylinePointsId);
mbgl::LineAnnotation annotation { toGeometry<mbgl::LineString<double>>(env, points) };
- annotation.opacity = jni::GetField<jfloat>(*env, polyline, *polylineAlphaId);
- annotation.color = toColor(jni::GetField<jint>(*env, polyline, *polylineColorId));
- annotation.width = jni::GetField<jfloat>(*env, polyline, *polylineWidthId);
+ annotation.opacity = { jni::GetField<jfloat>(*env, polyline, *polylineAlphaId) };
+ annotation.color = { toColor(jni::GetField<jint>(*env, polyline, *polylineColorId)) };
+ annotation.width = { jni::GetField<jfloat>(*env, polyline, *polylineWidthId) };
ids.push_back(nativeMapView->getMap().addAnnotation(annotation));
jni::DeleteLocalRef(*env, polyline);
@@ -797,9 +778,9 @@ jni::jarray<jlong>* nativeAddPolygons(JNIEnv *env, jni::jobject* obj, jlong nati
jni::jobject* points = jni::GetField<jni::jobject*>(*env, polygon, *polygonPointsId);
mbgl::FillAnnotation annotation { mbgl::Polygon<double> { toGeometry<mbgl::LinearRing<double>>(env, points) } };
- annotation.opacity = jni::GetField<jfloat>(*env, polygon, *polygonAlphaId);
- annotation.outlineColor = toColor(jni::GetField<jint>(*env, polygon, *polygonStrokeColorId));
- annotation.color = toColor(jni::GetField<jint>(*env, polygon, *polygonFillColorId));
+ annotation.opacity = { jni::GetField<jfloat>(*env, polygon, *polygonAlphaId) };
+ annotation.outlineColor = { toColor(jni::GetField<jint>(*env, polygon, *polygonStrokeColorId)) };
+ annotation.color = { toColor(jni::GetField<jint>(*env, polygon, *polygonFillColorId)) };
ids.push_back(nativeMapView->getMap().addAnnotation(annotation));
jni::DeleteLocalRef(*env, polygon);
@@ -808,6 +789,42 @@ jni::jarray<jlong>* nativeAddPolygons(JNIEnv *env, jni::jobject* obj, jlong nati
return std_vector_uint_to_jobject(env, ids);
}
+jlong nativeUpdatePolygon(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong polygonId, jni::jobject* polygon) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdatePolygon");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ if (polygonId == -1) {
+ return polygonId;
+ }
+ jni::jobject* points = jni::GetField<jni::jobject*>(*env, polygon, *polygonPointsId);
+
+ mbgl::FillAnnotation annotation { mbgl::Polygon<double> { toGeometry<mbgl::LinearRing<double>>(env, points) } };
+ annotation.opacity = { jni::GetField<jfloat>(*env, polygon, *polygonAlphaId) };
+ annotation.outlineColor = { toColor(jni::GetField<jint>(*env, polygon, *polygonStrokeColorId)) };
+ annotation.color = { toColor(jni::GetField<jint>(*env, polygon, *polygonFillColorId)) };
+ //TODO replace remove add with updateAnnotation, https://github.com/mapbox/mapbox-gl-native/issues/5844
+ nativeMapView->getMap().removeAnnotation(polygonId);
+ return nativeMapView->getMap().addAnnotation(annotation);
+}
+
+jlong nativeUpdatePolyline(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong polylineId, jni::jobject* polyline) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdatePolyline");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ if (polylineId == -1) {
+ return polylineId;
+ }
+ jni::jobject* points = jni::GetField<jni::jobject*>(*env, polyline, *polylinePointsId);
+
+ mbgl::LineAnnotation annotation { toGeometry<mbgl::LineString<double>>(env, points) };
+ annotation.opacity = { jni::GetField<jfloat>(*env, polyline, *polylineAlphaId) };
+ annotation.color = { toColor(jni::GetField<jint>(*env, polyline, *polylineColorId)) };
+ annotation.width = { jni::GetField<jfloat>(*env, polyline, *polylineWidthId) };
+ //TODO replace remove add with updateAnnotation, https://github.com/mapbox/mapbox-gl-native/issues/5844
+ nativeMapView->getMap().removeAnnotation(polylineId);
+ return nativeMapView->getMap().addAnnotation(annotation);
+}
+
void nativeRemoveAnnotations(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jlong>* jarray) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveAnnotations");
assert(nativeMapViewPtr != 0);
@@ -902,7 +919,7 @@ void nativeSetVisibleCoordinateBounds(JNIEnv *env, jni::jobject* obj, jlong nati
if (duration > 0) {
animationOptions.duration.emplace(mbgl::Milliseconds(duration));
// equivalent to kCAMediaTimingFunctionDefault in iOS
- animationOptions.easing = mbgl::util::UnitBezier(0.25, 0.1, 0.25, 0.1);
+ animationOptions.easing.emplace(mbgl::util::UnitBezier { 0.25, 0.1, 0.25, 0.1 });
}
nativeMapView->getMap().easeTo(cameraOptions, animationOptions);
@@ -963,50 +980,34 @@ jdouble nativeGetMetersPerPixelAtLatitude(JNIEnv *env, jni::jobject* obj, jlong
return nativeMapView->getMap().getMetersPerPixelAtLatitude(lat, zoom);
}
-jni::jobject* nativeProjectedMetersForLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* latLng) {
+jni::jobject* nativeProjectedMetersForLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble latitude, jdouble longitude) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeProjectedMetersForLatLng");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
-
- jdouble latitude = jni::GetField<jdouble>(*env, latLng, *latLngLatitudeId);
- jdouble longitude = jni::GetField<jdouble>(*env, latLng, *latLngLongitudeId);
-
mbgl::ProjectedMeters projectedMeters = nativeMapView->getMap().projectedMetersForLatLng(mbgl::LatLng(latitude, longitude));
return &jni::NewObject(*env, *projectedMetersClass, *projectedMetersConstructorId, projectedMeters.northing, projectedMeters.easting);
}
-jni::jobject* nativeLatLngForProjectedMeters(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* projectedMeters) {
+jni::jobject* nativeLatLngForProjectedMeters(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble northing, jdouble easting) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeLatLngForProjectedMeters");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
-
- jdouble northing = jni::GetField<jdouble>(*env, projectedMeters, *projectedMetersNorthingId);
- jdouble easting = jni::GetField<jdouble>(*env, projectedMeters, *projectedMetersEastingId);
-
mbgl::LatLng latLng = nativeMapView->getMap().latLngForProjectedMeters(mbgl::ProjectedMeters(northing, easting));
return &jni::NewObject(*env, *latLngClass, *latLngConstructorId, latLng.latitude, latLng.longitude);
}
-jni::jobject* nativePixelForLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* latLng) {
+jni::jobject* nativePixelForLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble latitude, jdouble longitude) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativePixelForLatLng");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
-
- jdouble latitude = jni::GetField<jdouble>(*env, latLng, *latLngLatitudeId);
- jdouble longitude = jni::GetField<jdouble>(*env, latLng, *latLngLongitudeId);
-
mbgl::ScreenCoordinate pixel = nativeMapView->getMap().pixelForLatLng(mbgl::LatLng(latitude, longitude));
return &jni::NewObject(*env, *pointFClass, *pointFConstructorId, static_cast<jfloat>(pixel.x), static_cast<jfloat>(pixel.y));
}
-jni::jobject* nativeLatLngForPixel(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* pixel) {
+jni::jobject* nativeLatLngForPixel(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jfloat x, jfloat y) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeLatLngForPixel");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
-
- jfloat x = jni::GetField<jfloat>(*env, pixel, *pointFXId);
- jfloat y = jni::GetField<jfloat>(*env, pixel, *pointFYId);
-
mbgl::LatLng latLng = nativeMapView->getMap().latLngForPixel(mbgl::ScreenCoordinate(x, y));
return &jni::NewObject(*env, *latLngClass, *latLngConstructorId, latLng.latitude, latLng.longitude);
}
@@ -1018,14 +1019,11 @@ jdouble nativeGetTopOffsetPixelsForAnnotationSymbol(JNIEnv *env, jni::jobject* o
return nativeMapView->getMap().getTopOffsetPixelsForAnnotationIcon(std_string_from_jstring(env, symbolName));
}
-void nativeJumpTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jni::jobject* centerLatLng, jdouble pitch, jdouble zoom) {
+void nativeJumpTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jdouble latitude, jdouble longitude, jdouble pitch, jdouble zoom) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeJumpTo");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- jdouble latitude = jni::GetField<jdouble>(*env, centerLatLng, *latLngLatitudeId);
- jdouble longitude = jni::GetField<jdouble>(*env, centerLatLng, *latLngLongitudeId);
-
mbgl::CameraOptions options;
if (angle != -1) {
options.angle = angle;
@@ -1042,14 +1040,11 @@ void nativeJumpTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdoubl
nativeMapView->getMap().jumpTo(options);
}
-void nativeEaseTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jni::jobject* centerLatLng, jlong duration, jdouble pitch, jdouble zoom, jboolean easing) {
+void nativeEaseTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jdouble latitude, jdouble longitude, jlong duration, jdouble pitch, jdouble zoom, jboolean easing) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeEaseTo");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- jdouble latitude = jni::GetField<jdouble>(*env, centerLatLng, *latLngLatitudeId);
- jdouble longitude = jni::GetField<jdouble>(*env, centerLatLng, *latLngLongitudeId);
-
mbgl::CameraOptions cameraOptions;
if (angle != -1) {
cameraOptions.angle = angle;
@@ -1067,7 +1062,7 @@ void nativeEaseTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdoubl
if (!easing) {
// add a linear interpolator instead of easing
- animationOptions.easing = mbgl::util::UnitBezier(0, 0, 1, 1);
+ animationOptions.easing.emplace(mbgl::util::UnitBezier { 0, 0, 1, 1 });
}
nativeMapView->getMap().easeTo(cameraOptions, animationOptions);
@@ -1080,14 +1075,11 @@ void nativeSetContentPadding(JNIEnv *env, jni::jobject* obj,long nativeMapViewPt
nativeMapView->setInsets({top, left, bottom, right});
}
-void nativeFlyTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jni::jobject* centerLatLng, jlong duration, jdouble pitch, jdouble zoom) {
+void nativeFlyTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jdouble latitude, jdouble longitude, jlong duration, jdouble pitch, jdouble zoom) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeFlyTo");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- jdouble latitude = jni::GetField<jdouble>(*env, centerLatLng, *latLngLatitudeId);
- jdouble longitude = jni::GetField<jdouble>(*env, centerLatLng, *latLngLongitudeId);
-
mbgl::CameraOptions cameraOptions;
if (angle != -1) {
cameraOptions.angle = angle;
@@ -1106,24 +1098,86 @@ void nativeFlyTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble
nativeMapView->getMap().flyTo(cameraOptions, animationOptions);
}
-void nativeAddCustomLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* customLayer, jni::jstring* before) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddCustomLayer");
+jni::jobject* nativeGetLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* layerId) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLayer");
+
+ assert(env);
assert(nativeMapViewPtr != 0);
+
+ //Get the native map peer
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().addLayer(std::make_unique<mbgl::style::CustomLayer>(
- std_string_from_jstring(env, reinterpret_cast<jni::jstring*>(jni::GetField<jni::jobject*>(*env, customLayer, *customLayerIdId))),
- reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(jni::GetField<jlong>(*env, customLayer, *customLayerInitializeFunctionId)),
- reinterpret_cast<mbgl::style::CustomLayerRenderFunction>(jni::GetField<jlong>(*env, customLayer, *customLayerRenderFunctionId)),
- reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(jni::GetField<jlong>(*env, customLayer, *customLayerDeinitializeFunctionId)),
- reinterpret_cast<void*>(jni::GetField<jlong>(*env, customLayer, *customLayerContextId))),
- before ? mbgl::optional<std::string>(std_string_from_jstring(env, before)) : mbgl::optional<std::string>());
+
+ //Find the layer
+ mbgl::style::Layer* coreLayer = nativeMapView->getMap().getLayer(std_string_from_jstring(env, layerId));
+ if (!coreLayer) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "No layer found");
+ return jni::Object<Layer>();
+ }
+
+ //Create and return the layer's native peer
+ return createJavaLayerPeer(*env, nativeMapView->getMap(), *coreLayer);
}
-void nativeRemoveCustomLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) {
- mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveCustomLayer");
+void nativeAddLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong nativeLayerPtr, jni::jstring* before) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddLayer");
assert(nativeMapViewPtr != 0);
+ assert(nativeLayerPtr != 0);
+
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
- nativeMapView->getMap().removeLayer(std_string_from_jstring(env, id));
+ Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr);
+
+ nativeMapView->getMap().addLayer(
+ layer->releaseCoreLayer(),
+ before ? mbgl::optional<std::string>(std_string_from_jstring(env, before)) : mbgl::optional<std::string>()
+ );
+}
+
+void nativeRemoveLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveLayer");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ try {
+ nativeMapView->getMap().removeLayer(std_string_from_jstring(env, id));
+ } catch (const std::runtime_error& error) {
+ jni::ThrowNew(*env, jni::FindClass(*env, "com/mapbox/mapboxsdk/style/layers/NoSuchLayerException"), error.what());
+ }
+}
+
+void nativeAddSource(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id, jni::jobject* jsource) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddSource");
+ assert(nativeMapViewPtr != 0);
+ assert(id != nullptr);
+ assert(jsource != nullptr);
+
+ //Convert
+ mbgl::optional<std::unique_ptr<mbgl::style::Source>> source = convertToNativeSource(
+ *env,
+ jni::Object<jni::jobject>(jsource), jni::String(id)
+ );
+
+ //Add to map view
+ if (source) {
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().addSource(std::move(*source));
+ }
+}
+
+void nativeRemoveSource(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveSource");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ try {
+ nativeMapView->getMap().removeSource(std_string_from_jstring(env, id));
+ } catch (const std::runtime_error& error) {
+ jni::ThrowNew(*env, jni::FindClass(*env, "com/mapbox/mapboxsdk/style/layers/NoSuchSourceException"), error.what());
+ }
+}
+
+void nativeScheduleTakeSnapshot(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Error(mbgl::Event::JNI, "nativeRenderToOffscreen");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->scheduleTakeSnapshot();
}
// Offline calls begin
@@ -1183,12 +1237,15 @@ void listOfflineRegions(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourceP
std::size_t index = 0;
jni::jarray<jni::jobject>* jregions = &jni::NewObjectArray(*env2, regions->size(), *offlineRegionClass, NULL);
for (auto& region : *regions) {
+ // Create a new local reference frame (capacity 2 for the NewObject allocations below)
+ // to avoid a local reference table overflow (#5629)
+ jni::UniqueLocalFrame frame = jni::PushLocalFrame(*env2, 2);
+
// Build the Region object
jni::jobject* jregion = &jni::NewObject(*env2, *offlineRegionClass, *offlineRegionConstructorId);
jni::SetField<jni::jobject*>(*env2, jregion, *offlineRegionOfflineManagerId, obj);
jni::SetField<jlong>(*env2, jregion, *offlineRegionIdId, region.getID());
-
// Definition object
mbgl::OfflineTilePyramidRegionDefinition definition = region.getDefinition();
jni::jobject* jdefinition = &jni::NewObject(*env2, *offlineRegionDefinitionClass, *offlineRegionDefinitionConstructorId);
@@ -1198,7 +1255,7 @@ void listOfflineRegions(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourceP
jni::SetField<jdouble>(*env2, jdefinition, *offlineRegionDefinitionMaxZoomId, definition.maxZoom);
jni::SetField<jfloat>(*env2, jdefinition, *offlineRegionDefinitionPixelRatioId, definition.pixelRatio);
jni::SetField<jni::jobject*>(*env2, jregion, *offlineRegionDefinitionId, jdefinition);
-
+
// Metadata object
jni::jarray<jbyte>* metadata = metadata_from_native(env2, region.getMetadata());
jni::SetField<jni::jobject*>(*env2, jregion, *offlineRegionMetadataId, metadata);
@@ -1583,6 +1640,9 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
mbgl::android::RegisterNativeHTTPRequest(env);
+ java::registerNatives(env);
+ registerNativeLayers(env);
+
latLngClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/geometry/LatLng");
latLngClass = jni::NewGlobalRef(env, latLngClass).release();
latLngConstructorId = &jni::GetMethodID(env, *latLngClass, "<init>", "(DD)V");
@@ -1649,19 +1709,12 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
rectFTopId = &jni::GetFieldID(env, *rectFClass, "top", "F");
rectFBottomId = &jni::GetFieldID(env, *rectFClass, "bottom", "F");
- customLayerClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/layers/CustomLayer");
- customLayerClass = jni::NewGlobalRef(env, customLayerClass).release();
- customLayerIdId = &jni::GetFieldID(env, *customLayerClass, "mID", "Ljava/lang/String;");
- customLayerContextId = &jni::GetFieldID(env, *customLayerClass, "mContext", "J");
- customLayerInitializeFunctionId = &jni::GetFieldID(env, *customLayerClass, "mInitializeFunction", "J");
- customLayerRenderFunctionId = &jni::GetFieldID(env, *customLayerClass, "mRenderFunction", "J");
- customLayerDeinitializeFunctionId = &jni::GetFieldID(env, *customLayerClass, "mDeinitializeFunction", "J");
-
jni::jclass& nativeMapViewClass = jni::FindClass(env, "com/mapbox/mapboxsdk/maps/NativeMapView");
onInvalidateId = &jni::GetMethodID(env, nativeMapViewClass, "onInvalidate", "()V");
onMapChangedId = &jni::GetMethodID(env, nativeMapViewClass, "onMapChanged", "(I)V");
onFpsChangedId = &jni::GetMethodID(env, nativeMapViewClass, "onFpsChanged", "(D)V");
+ onSnapshotReadyId = &jni::GetMethodID(env, nativeMapViewClass, "onSnapshotReady","([B)V");
#define MAKE_NATIVE_METHOD(name, sig) jni::MakeNativeMethod<decltype(name), name>( #name, sig )
@@ -1691,7 +1744,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeCancelTransitions, "(J)V"),
MAKE_NATIVE_METHOD(nativeSetGestureInProgress, "(JZ)V"),
MAKE_NATIVE_METHOD(nativeMoveBy, "(JDDJ)V"),
- MAKE_NATIVE_METHOD(nativeSetLatLng, "(JLcom/mapbox/mapboxsdk/geometry/LatLng;J)V"),
+ MAKE_NATIVE_METHOD(nativeSetLatLng, "(JDDJ)V"),
MAKE_NATIVE_METHOD(nativeGetLatLng, "(J)Lcom/mapbox/mapboxsdk/geometry/LatLng;"),
MAKE_NATIVE_METHOD(nativeResetPosition, "(J)V"),
MAKE_NATIVE_METHOD(nativeGetCameraValues, "(J)[D"),
@@ -1715,7 +1768,9 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeAddMarkers, "(J[Lcom/mapbox/mapboxsdk/annotations/Marker;)[J"),
MAKE_NATIVE_METHOD(nativeAddPolylines, "(J[Lcom/mapbox/mapboxsdk/annotations/Polyline;)[J"),
MAKE_NATIVE_METHOD(nativeAddPolygons, "(J[Lcom/mapbox/mapboxsdk/annotations/Polygon;)[J"),
- MAKE_NATIVE_METHOD(nativeUpdateMarker, "(JLcom/mapbox/mapboxsdk/annotations/Marker;)V"),
+ MAKE_NATIVE_METHOD(nativeUpdateMarker, "(JJDDLjava/lang/String;)V"),
+ MAKE_NATIVE_METHOD(nativeUpdatePolygon, "(JJLcom/mapbox/mapboxsdk/annotations/Polygon;)J"),
+ MAKE_NATIVE_METHOD(nativeUpdatePolyline, "(JJLcom/mapbox/mapboxsdk/annotations/Polyline;)J"),
MAKE_NATIVE_METHOD(nativeRemoveAnnotations, "(J[J)V"),
MAKE_NATIVE_METHOD(nativeGetAnnotationsInBounds, "(JLcom/mapbox/mapboxsdk/geometry/LatLngBounds;)[J"),
MAKE_NATIVE_METHOD(nativeAddAnnotationIcon, "(JLjava/lang/String;IIF[B)V"),
@@ -1727,17 +1782,21 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeIsFullyLoaded, "(J)Z"),
MAKE_NATIVE_METHOD(nativeSetReachability, "(JZ)V"),
MAKE_NATIVE_METHOD(nativeGetMetersPerPixelAtLatitude, "(JDD)D"),
- MAKE_NATIVE_METHOD(nativeProjectedMetersForLatLng, "(JLcom/mapbox/mapboxsdk/geometry/LatLng;)Lcom/mapbox/mapboxsdk/geometry/ProjectedMeters;"),
- MAKE_NATIVE_METHOD(nativeLatLngForProjectedMeters, "(JLcom/mapbox/mapboxsdk/geometry/ProjectedMeters;)Lcom/mapbox/mapboxsdk/geometry/LatLng;"),
- MAKE_NATIVE_METHOD(nativePixelForLatLng, "(JLcom/mapbox/mapboxsdk/geometry/LatLng;)Landroid/graphics/PointF;"),
- MAKE_NATIVE_METHOD(nativeLatLngForPixel, "(JLandroid/graphics/PointF;)Lcom/mapbox/mapboxsdk/geometry/LatLng;"),
+ MAKE_NATIVE_METHOD(nativeProjectedMetersForLatLng, "(JDD)Lcom/mapbox/mapboxsdk/geometry/ProjectedMeters;"),
+ MAKE_NATIVE_METHOD(nativeLatLngForProjectedMeters, "(JDD)Lcom/mapbox/mapboxsdk/geometry/LatLng;"),
+ MAKE_NATIVE_METHOD(nativePixelForLatLng, "(JDD)Landroid/graphics/PointF;"),
+ MAKE_NATIVE_METHOD(nativeLatLngForPixel, "(JFF)Lcom/mapbox/mapboxsdk/geometry/LatLng;"),
MAKE_NATIVE_METHOD(nativeGetTopOffsetPixelsForAnnotationSymbol, "(JLjava/lang/String;)D"),
- MAKE_NATIVE_METHOD(nativeJumpTo, "(JDLcom/mapbox/mapboxsdk/geometry/LatLng;DD)V"),
- MAKE_NATIVE_METHOD(nativeEaseTo, "(JDLcom/mapbox/mapboxsdk/geometry/LatLng;JDDZ)V"),
- MAKE_NATIVE_METHOD(nativeFlyTo, "(JDLcom/mapbox/mapboxsdk/geometry/LatLng;JDD)V"),
- MAKE_NATIVE_METHOD(nativeAddCustomLayer, "(JLcom/mapbox/mapboxsdk/layers/CustomLayer;Ljava/lang/String;)V"),
- MAKE_NATIVE_METHOD(nativeRemoveCustomLayer, "(JLjava/lang/String;)V"),
- MAKE_NATIVE_METHOD(nativeSetContentPadding, "(JDDDD)V")
+ MAKE_NATIVE_METHOD(nativeJumpTo, "(JDDDDD)V"),
+ MAKE_NATIVE_METHOD(nativeEaseTo, "(JDDDJDDZ)V"),
+ MAKE_NATIVE_METHOD(nativeFlyTo, "(JDDDJDD)V"),
+ MAKE_NATIVE_METHOD(nativeGetLayer, "(JLjava/lang/String;)Lcom/mapbox/mapboxsdk/style/layers/Layer;"),
+ MAKE_NATIVE_METHOD(nativeAddLayer, "(JJLjava/lang/String;)V"),
+ MAKE_NATIVE_METHOD(nativeRemoveLayer, "(JLjava/lang/String;)V"),
+ MAKE_NATIVE_METHOD(nativeAddSource, "(JLjava/lang/String;Lcom/mapbox/mapboxsdk/style/sources/Source;)V"),
+ MAKE_NATIVE_METHOD(nativeRemoveSource, "(JLjava/lang/String;)V"),
+ MAKE_NATIVE_METHOD(nativeSetContentPadding, "(JDDDD)V"),
+ MAKE_NATIVE_METHOD(nativeScheduleTakeSnapshot, "(J)V")
);
// Offline begin
diff --git a/platform/android/src/jni.hpp b/platform/android/src/jni.hpp
index 57f84f0ccc..0810ee656d 100644
--- a/platform/android/src/jni.hpp
+++ b/platform/android/src/jni.hpp
@@ -19,6 +19,7 @@ extern std::string androidRelease;
extern jmethodID onInvalidateId;
extern jmethodID onMapChangedId;
extern jmethodID onFpsChangedId;
+extern jmethodID onSnapshotReadyId;
extern bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName);
extern void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach);
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 0b849976eb..578e5d0033 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -194,6 +194,35 @@ void NativeMapView::render() {
map->render();
+ if(snapshot){
+ snapshot = false;
+
+ // take snapshot
+ const unsigned int w = fbWidth;
+ const unsigned int h = fbHeight;
+ mbgl::PremultipliedImage image { w, h };
+ MBGL_CHECK_ERROR(glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
+ const size_t stride = image.stride();
+ auto tmp = std::make_unique<uint8_t[]>(stride);
+ uint8_t *rgba = image.data.get();
+ for (int i = 0, j = h - 1; i < j; i++, j--) {
+ std::memcpy(tmp.get(), rgba + i * stride, stride);
+ std::memcpy(rgba + i * stride, rgba + j * stride, stride);
+ std::memcpy(rgba + j * stride, tmp.get(), stride);
+ }
+
+ // encode and convert to jbytes
+ std::string string = encodePNG(image);
+ jbyteArray arr = env->NewByteArray(string.length());
+ env->SetByteArrayRegion(arr,0,string.length(),(jbyte*)string.c_str());
+
+ // invoke Mapview#OnSnapshotReady
+ env->CallVoidMethod(obj, onSnapshotReadyId, arr);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ }
+ }
+
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
if (!eglSwapBuffers(display, surface)) {
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
@@ -436,6 +465,10 @@ void NativeMapView::destroySurface() {
}
}
+void NativeMapView::scheduleTakeSnapshot() {
+ snapshot = true;
+}
+
// Speed
/*
typedef enum {
diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp
index 40cb012b0b..9b5af76dfe 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -49,6 +49,8 @@ public:
mbgl::EdgeInsets getInsets() { return insets;}
void setInsets(mbgl::EdgeInsets insets_);
+ void scheduleTakeSnapshot();
+
private:
EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);
@@ -79,6 +81,7 @@ private:
bool firstTime = false;
bool fpsEnabled = false;
bool sizeChanged = false;
+ bool snapshot = false;
double fps = 0.0;
int width = 0;
diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp
index 7a28a6139c..1e5fc9b4ba 100644
--- a/platform/android/src/run_loop.cpp
+++ b/platform/android/src/run_loop.cpp
@@ -1,13 +1,16 @@
#include "run_loop_impl.hpp"
+#include <mbgl/platform/platform.hpp>
#include <mbgl/util/thread_context.hpp>
#include <mbgl/util/thread_local.hpp>
+#include <mbgl/util/timer.hpp>
#include <android/looper.h>
#include <algorithm>
#include <cassert>
#include <functional>
+#include <memory>
#include <stdexcept>
#include <vector>
@@ -36,9 +39,15 @@ int looperCallbackDefault(int fd, int, void* data) {
int buffer[1];
while (read(fd, buffer, sizeof(buffer)) > 0) {}
- auto runLoop = reinterpret_cast<RunLoop*>(data);
+ auto runLoopImpl = reinterpret_cast<RunLoop::Impl*>(data);
+ auto runLoop = runLoopImpl->runLoop;
+
runLoop->runOnce();
+ if (!runLoopImpl->running) {
+ ALooper_wake(runLoopImpl->loop);
+ }
+
return 1;
}
@@ -47,9 +56,27 @@ int looperCallbackDefault(int fd, int, void* data) {
namespace mbgl {
namespace util {
-RunLoop::Impl::Impl(RunLoop* runLoop, RunLoop::Type type) {
+// This is needed only for the RunLoop living on the main thread because of
+// how we implement timers. We sleep on `ALooper_pollAll` until the next
+// timeout, but on the main thread `ALooper_pollAll` is called by the activity
+// automatically, thus we cannot set the timeout. Instead we wake the loop
+// with an external file descriptor event coming from this thread.
+class Alarm {
+public:
+ Alarm(RunLoop::Impl* loop_) : loop(loop_) {}
+
+ void set(const Milliseconds& timeout) {
+ alarm.start(timeout, mbgl::Duration::zero(), [this]() { loop->wake(); });
+ }
+
+private:
+ Timer alarm;
+ RunLoop::Impl* loop;
+};
+
+RunLoop::Impl::Impl(RunLoop* runLoop_, RunLoop::Type type) : runLoop(runLoop_) {
using namespace mbgl::android;
- detach = attach_jni_thread(theJVM, &env, "");
+ detach = attach_jni_thread(theJVM, &env, platform::getCurrentThreadName());
loop = ALooper_prepare(0);
assert(loop);
@@ -73,7 +100,9 @@ RunLoop::Impl::Impl(RunLoop* runLoop, RunLoop::Type type) {
break;
case Type::Default:
ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK,
- ALOOPER_EVENT_INPUT, looperCallbackDefault, runLoop);
+ ALOOPER_EVENT_INPUT, looperCallbackDefault, this);
+ alarm = std::make_unique<Thread<Alarm>>(ThreadContext{"Alarm"}, this);
+ running = true;
break;
}
@@ -83,6 +112,8 @@ RunLoop::Impl::Impl(RunLoop* runLoop, RunLoop::Type type) {
}
RunLoop::Impl::~Impl() {
+ alarm.reset();
+
if (ALooper_removeFd(loop, fds[PIPE_OUT]) != 1) {
throw std::runtime_error("Failed to remove file descriptor from Looper.");
}
@@ -117,10 +148,16 @@ void RunLoop::Impl::addRunnable(Runnable* runnable) {
void RunLoop::Impl::removeRunnable(Runnable* runnable) {
std::lock_guard<std::recursive_mutex> lock(mtx);
- if (runnable->iter != runnables.end()) {
- runnables.erase(runnable->iter);
- runnable->iter = runnables.end();
+ if (runnable->iter == runnables.end()) {
+ return;
+ }
+
+ if (nextRunnable == runnable->iter) {
+ ++nextRunnable;
}
+
+ runnables.erase(runnable->iter);
+ runnable->iter = runnables.end();
}
void RunLoop::Impl::initRunnable(Runnable* runnable) {
@@ -135,8 +172,8 @@ Milliseconds RunLoop::Impl::processRunnables() {
// O(N) but in the render thread where we get tons
// of messages, the size of the list is usually 1~2.
- for (auto iter = runnables.begin(); iter != runnables.end();) {
- Runnable* runnable = *(iter++);
+ for (nextRunnable = runnables.begin(); nextRunnable != runnables.end();) {
+ Runnable* runnable = *(nextRunnable++);
auto const dueTime = runnable->dueTime();
if (dueTime <= now) {
@@ -148,9 +185,14 @@ Milliseconds RunLoop::Impl::processRunnables() {
if (runnables.empty() || nextDue == TimePoint::max()) {
return Milliseconds(-1);
- } else {
- return std::chrono::duration_cast<Milliseconds>(nextDue - now);
}
+
+ auto timeout = std::chrono::duration_cast<Milliseconds>(nextDue - now);
+ if (alarm) {
+ alarm->invoke(&Alarm::set, timeout);
+ }
+
+ return timeout;
}
RunLoop* RunLoop::Get() {
@@ -197,8 +239,10 @@ void RunLoop::runOnce() {
}
void RunLoop::stop() {
- impl->running = false;
- impl->wake();
+ invoke([&] {
+ impl->running = false;
+ impl->wake();
+ });
}
void RunLoop::addWatch(int, Event, std::function<void(int, Event)>&&) {
diff --git a/platform/android/src/run_loop_impl.hpp b/platform/android/src/run_loop_impl.hpp
index d855728b60..a3efa92a83 100644
--- a/platform/android/src/run_loop_impl.hpp
+++ b/platform/android/src/run_loop_impl.hpp
@@ -2,10 +2,11 @@
#include "jni.hpp"
-#include <mbgl/util/atomic.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/thread.hpp>
+#include <atomic>
#include <list>
#include <memory>
#include <mutex>
@@ -15,6 +16,8 @@ struct ALooper;
namespace mbgl {
namespace util {
+class Alarm;
+
class RunLoop::Impl {
public:
class Runnable {
@@ -38,6 +41,10 @@ public:
Milliseconds processRunnables();
+ ALooper* loop = nullptr;
+ RunLoop* runLoop = nullptr;
+ std::atomic<bool> running;
+
private:
friend RunLoop;
@@ -46,11 +53,11 @@ private:
JNIEnv *env = nullptr;
bool detach = false;
- ALooper* loop = nullptr;
- util::Atomic<bool> running;
+ std::unique_ptr<Thread<Alarm>> alarm;
std::recursive_mutex mtx;
std::list<Runnable*> runnables;
+ std::list<Runnable*>::iterator nextRunnable;
};
} // namespace util
diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp
new file mode 100644
index 0000000000..0ccb704c0a
--- /dev/null
+++ b/platform/android/src/style/android_conversion.hpp
@@ -0,0 +1,91 @@
+#pragma once
+
+#include "value.hpp"
+
+#include <mbgl/platform/log.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+inline bool isUndefined(const mbgl::android::Value& value) {
+ return value.isNull();
+}
+
+inline bool isArray(const mbgl::android::Value& value) {
+ return value.isArray();
+}
+
+inline bool isObject(const mbgl::android::Value& value) {
+ return value.isObject();
+}
+
+inline std::size_t arrayLength(const mbgl::android::Value& value) {
+ return value.getLength();;
+}
+
+inline mbgl::android::Value arrayMember(const mbgl::android::Value& value, std::size_t i) {
+ return value.get(i);
+}
+
+inline optional<mbgl::android::Value> objectMember(const mbgl::android::Value& value, const char* key) {
+ mbgl::android::Value member = value.get(key);
+
+ if (!member.isNull()) {
+ return member;
+ } else {
+ return {};
+ }
+}
+
+template <class Fn>
+optional<Error> eachMember(const mbgl::android::Value&, Fn&&) {
+ //TODO
+ mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented");
+ return {};
+}
+
+inline optional<bool> toBool(const mbgl::android::Value& value) {
+ if (value.isBool()) {
+ return value.toBool();
+ } else {
+ return {};
+ }
+}
+
+inline optional<float> toNumber(const mbgl::android::Value& value) {
+ if (value.isNumber()) {
+ return value.toNumber();
+ } else {
+ return {};
+ }
+}
+
+inline optional<std::string> toString(const mbgl::android::Value& value) {
+ if (value.isString()) {
+ return value.toString();
+ } else {
+ return {};
+ }
+}
+
+inline optional<Value> toValue(const mbgl::android::Value& value) {
+ if (value.isBool()) {
+ return { value.toBool() };
+ } else if (value.isString()) {
+ return { value.toString() };
+ } else if (value.isNumber()) {
+ return { value.toNumber() };
+ } else {
+ return {};
+ }
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/platform/android/src/style/android_geojson.hpp b/platform/android/src/style/android_geojson.hpp
new file mode 100644
index 0000000000..b2e0ca0a4a
--- /dev/null
+++ b/platform/android/src/style/android_geojson.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "value.hpp"
+
+#include <mapbox/geojson.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/util/rapidjson.hpp>
+#include <mbgl/platform/log.hpp>
+#include <jni/jni.hpp>
+
+#include <sstream>
+#include <string>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+Result<GeoJSON> convertGeoJSON(const mbgl::android::Value& value) {
+
+ //Value should be a string wrapped in an object
+ mbgl::android::Value jsonValue = value.get("data");
+ if(value.isNull()) {
+ return Error { "no json data found" };
+ }
+ std::string jsonString = value.get("data").toString();
+
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> d;
+ d.Parse(jsonString.c_str());
+
+ if (d.HasParseError()) {
+ std::stringstream message;
+ message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError());
+ return Error { message.str() };
+ }
+
+ conversion::Result<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d);
+ if (!geoJSON) {
+ return Error { geoJSON.error().message };
+ }
+
+ return geoJSON;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/conversion/function.hpp b/platform/android/src/style/conversion/function.hpp
new file mode 100644
index 0000000000..ad09ce02d2
--- /dev/null
+++ b/platform/android/src/style/conversion/function.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <mbgl/style/property_value.hpp>
+#include "../../conversion/conversion.hpp"
+#include "../../conversion/constant.hpp"
+#include "types.hpp"
+#include "function.hpp"
+
+#include <jni/jni.hpp>
+
+#include <tuple>
+#include <vector>
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+template <class T>
+inline jni::jobject* toFunctionStopJavaArray(jni::JNIEnv& env, std::vector<std::pair<float, T>> value) {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/Function$Stop")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
+
+ jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, value.size(), *javaClass);
+
+ for(size_t i = 0; i < value.size(); i = i + 1) {
+ jni::SetObjectArrayElement(env, jarray, i, &jni::NewObject(env, *javaClass, *constructor, value[i].first, *convert<jni::jobject*, T>(env, value[i].second)));
+ }
+
+ return &jarray;
+}
+
+template <class T>
+struct Converter<jni::jobject*, mbgl::style::Function<T>> {
+
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::Function<T>& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/Function")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "([Lcom/mapbox/mapboxsdk/style/layers/Function$Stop;)V");
+ static jni::jmethodID* withBase = &jni::GetMethodID(env, *javaClass, "withBase", "(F)Lcom/mapbox/mapboxsdk/style/layers/Function;");
+
+ //Create object
+ jni::jobject* jfunction = &jni::NewObject(env, *javaClass, *constructor, *toFunctionStopJavaArray(env, value.getStops()));
+
+ //Set base
+ jni::CallMethod<jni::jobject*>(env, jfunction, *withBase, value.getBase());
+
+ return {jfunction};
+ }
+};
+
+} // namespace conversion
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/conversion/property_value.hpp b/platform/android/src/style/conversion/property_value.hpp
new file mode 100644
index 0000000000..4121192f3f
--- /dev/null
+++ b/platform/android/src/style/conversion/property_value.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <mbgl/style/property_value.hpp>
+#include "../../conversion/conversion.hpp"
+#include "../../conversion/constant.hpp"
+#include "types.hpp"
+#include "function.hpp"
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+template <class T>
+struct Converter<jni::jobject*, mbgl::style::PropertyValue<T>> {
+
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::PropertyValue<T>& value) const {
+
+ if(value.isUndefined()) {
+ //Return a nullptr representing a Java null value
+ return {nullptr};
+ } else if (value.isConstant()) {
+ //Time to convert the constant value
+ Result<jni::jobject*> result = convert<jni::jobject*, T>(env, value.asConstant());
+ return {*result};
+ //return converted;
+ } else if (value.isFunction()) {
+ //Must be a function than
+ return convert<jni::jobject*, mbgl::style::Function<T>>(env, value.asFunction());
+ } else {
+ throw std::runtime_error("Unknown property value type");
+ }
+
+ }
+};
+
+} // namespace conversion
+} // 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
new file mode 100644
index 0000000000..d3c12ff89a
--- /dev/null
+++ b/platform/android/src/style/conversion/types.hpp
@@ -0,0 +1,98 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+#pragma once
+
+#include "types_string_values.hpp"
+#include "../../conversion/conversion.hpp"
+#include "../../conversion/constant.hpp"
+
+#include <mbgl/style/types.hpp>
+#include <mbgl/util/optional.hpp>
+#include <jni/jni.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::VisibilityType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::VisibilityType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::LineCapType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::LineCapType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::LineJoinType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::LineJoinType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::SymbolPlacementType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::SymbolPlacementType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::AlignmentType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::AlignmentType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::IconTextFitType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::IconTextFitType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
+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));
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::TextAnchorType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextAnchorType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::TextTransformType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextTransformType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::TranslateAnchorType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TranslateAnchorType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::CirclePitchScaleType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::CirclePitchScaleType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+
+} // namespace conversion
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/conversion/types.hpp.ejs b/platform/android/src/style/conversion/types.hpp.ejs
new file mode 100644
index 0000000000..de26d061f7
--- /dev/null
+++ b/platform/android/src/style/conversion/types.hpp.ejs
@@ -0,0 +1,40 @@
+<%
+ const properties = locals.properties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+#pragma once
+
+#include "types_string_values.hpp"
+#include "../../conversion/conversion.hpp"
+#include "../../conversion/constant.hpp"
+
+#include <mbgl/style/types.hpp>
+#include <mbgl/util/optional.hpp>
+#include <jni/jni.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+template <>
+struct Converter<jni::jobject*, mbgl::style::VisibilityType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::VisibilityType& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+<% for (const property of properties) { -%>
+template <>
+struct Converter<jni::jobject*, mbgl::style::<%- propertyNativeType(property) %>> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::<%- propertyNativeType(property) %>& value) const {
+ return convert<jni::jobject*, std::string>(env, toString(value));
+ }
+};
+
+<% } -%>
+
+} // namespace conversion
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/conversion/types_string_values.hpp b/platform/android/src/style/conversion/types_string_values.hpp
new file mode 100644
index 0000000000..35cdb1cbc9
--- /dev/null
+++ b/platform/android/src/style/conversion/types_string_values.hpp
@@ -0,0 +1,209 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+#pragma once
+
+#include <mbgl/style/types.hpp>
+
+#include <string>
+#include <stdexcept>
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+ //visibility
+ inline std::string toString(mbgl::style::VisibilityType value) {
+ switch (value) {
+ case mbgl::style::VisibilityType::Visible:
+ return "visible";
+ break;
+ case mbgl::style::VisibilityType::None:
+ return "none";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+ //line-cap
+ inline std::string toString(mbgl::style::LineCapType value) {
+ switch (value) {
+ case mbgl::style::LineCapType::Butt:
+ return "butt";
+ break;
+ case mbgl::style::LineCapType::Round:
+ return "round";
+ break;
+ case mbgl::style::LineCapType::Square:
+ return "square";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+ //line-join
+ inline std::string toString(mbgl::style::LineJoinType value) {
+ switch (value) {
+ case mbgl::style::LineJoinType::Bevel:
+ return "bevel";
+ break;
+ case mbgl::style::LineJoinType::Round:
+ return "round";
+ break;
+ case mbgl::style::LineJoinType::Miter:
+ return "miter";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+ //symbol-placement
+ inline std::string toString(mbgl::style::SymbolPlacementType value) {
+ switch (value) {
+ case mbgl::style::SymbolPlacementType::Point:
+ return "point";
+ break;
+ case mbgl::style::SymbolPlacementType::Line:
+ return "line";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+ //icon-rotation-alignment
+ inline std::string toString(mbgl::style::AlignmentType value) {
+ switch (value) {
+ case mbgl::style::AlignmentType::Map:
+ return "map";
+ break;
+ case mbgl::style::AlignmentType::Viewport:
+ return "viewport";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+ //icon-text-fit
+ inline std::string toString(mbgl::style::IconTextFitType value) {
+ switch (value) {
+ case mbgl::style::IconTextFitType::None:
+ return "none";
+ break;
+ case mbgl::style::IconTextFitType::Both:
+ return "both";
+ break;
+ case mbgl::style::IconTextFitType::Width:
+ return "width";
+ break;
+ case mbgl::style::IconTextFitType::Height:
+ return "height";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+ //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) {
+ switch (value) {
+ case mbgl::style::TextAnchorType::Center:
+ return "center";
+ break;
+ case mbgl::style::TextAnchorType::Left:
+ return "left";
+ break;
+ case mbgl::style::TextAnchorType::Right:
+ return "right";
+ break;
+ case mbgl::style::TextAnchorType::Top:
+ return "top";
+ break;
+ case mbgl::style::TextAnchorType::Bottom:
+ return "bottom";
+ break;
+ case mbgl::style::TextAnchorType::TopLeft:
+ return "top-left";
+ break;
+ case mbgl::style::TextAnchorType::TopRight:
+ return "top-right";
+ break;
+ case mbgl::style::TextAnchorType::BottomLeft:
+ return "bottom-left";
+ break;
+ case mbgl::style::TextAnchorType::BottomRight:
+ return "bottom-right";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+ //text-transform
+ inline std::string toString(mbgl::style::TextTransformType value) {
+ switch (value) {
+ case mbgl::style::TextTransformType::None:
+ return "none";
+ break;
+ case mbgl::style::TextTransformType::Uppercase:
+ return "uppercase";
+ break;
+ case mbgl::style::TextTransformType::Lowercase:
+ return "lowercase";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+ //fill-translate-anchor
+ inline std::string toString(mbgl::style::TranslateAnchorType value) {
+ switch (value) {
+ case mbgl::style::TranslateAnchorType::Map:
+ return "map";
+ break;
+ case mbgl::style::TranslateAnchorType::Viewport:
+ return "viewport";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+ //circle-pitch-scale
+ inline std::string toString(mbgl::style::CirclePitchScaleType value) {
+ switch (value) {
+ case mbgl::style::CirclePitchScaleType::Map:
+ return "map";
+ break;
+ case mbgl::style::CirclePitchScaleType::Viewport:
+ return "viewport";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+
+} // namespace conversion
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/conversion/types_string_values.hpp.ejs b/platform/android/src/style/conversion/types_string_values.hpp.ejs
new file mode 100644
index 0000000000..db90a614f5
--- /dev/null
+++ b/platform/android/src/style/conversion/types_string_values.hpp.ejs
@@ -0,0 +1,48 @@
+<%
+ const properties = locals.properties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+#pragma once
+
+#include <mbgl/style/types.hpp>
+
+#include <string>
+#include <stdexcept>
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+ //visibility
+ inline std::string toString(mbgl::style::VisibilityType value) {
+ switch (value) {
+ case mbgl::style::VisibilityType::Visible:
+ return "visible";
+ break;
+ case mbgl::style::VisibilityType::None:
+ return "none";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+<% for (const property of properties) { -%>
+ //<%- property.name %>
+ inline std::string toString(mbgl::style::<%- propertyNativeType(property) %> value) {
+ switch (value) {
+<% for (const value of property.values) { -%>
+ case mbgl::style::<%- propertyNativeType(property) %>::<%- camelize(value) %>:
+ return "<%- value %>";
+ break;
+<% } -%>
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
+<% } -%>
+
+} // namespace conversion
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/layers/background_layer.cpp b/platform/android/src/style/layers/background_layer.cpp
new file mode 100644
index 0000000000..25526a07fa
--- /dev/null
+++ b/platform/android/src/style/layers/background_layer.cpp
@@ -0,0 +1,67 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "background_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+
+namespace mbgl {
+namespace android {
+
+ BackgroundLayer::BackgroundLayer(jni::JNIEnv& env, jni::String layerId)
+ : Layer(env, std::make_unique<mbgl::style::BackgroundLayer>(jni::Make<std::string>(env, layerId))) {
+ }
+
+ BackgroundLayer::BackgroundLayer(mbgl::Map& map, mbgl::style::BackgroundLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ BackgroundLayer::~BackgroundLayer() = default;
+
+ // Property getters
+
+ jni::Object<jni::ObjectTag> BackgroundLayer::getBackgroundColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::BackgroundLayer>()->BackgroundLayer::getBackgroundColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> BackgroundLayer::getBackgroundPattern(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::BackgroundLayer>()->BackgroundLayer::getBackgroundPattern());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> BackgroundLayer::getBackgroundOpacity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::BackgroundLayer>()->BackgroundLayer::getBackgroundOpacity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Class<BackgroundLayer> BackgroundLayer::javaClass;
+
+ jni::jobject* BackgroundLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = BackgroundLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return BackgroundLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void BackgroundLayer::registerNative(jni::JNIEnv& env) {
+ //Lookup the class
+ BackgroundLayer::javaClass = *jni::Class<BackgroundLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<BackgroundLayer>(
+ env, BackgroundLayer::javaClass, "nativePtr",
+ std::make_unique<BackgroundLayer, JNIEnv&, jni::String>,
+ "initialize",
+ "finalize",
+ METHOD(&BackgroundLayer::getBackgroundColor, "nativeGetBackgroundColor"),
+ METHOD(&BackgroundLayer::getBackgroundPattern, "nativeGetBackgroundPattern"),
+ METHOD(&BackgroundLayer::getBackgroundOpacity, "nativeGetBackgroundOpacity"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/background_layer.hpp b/platform/android/src/style/layers/background_layer.hpp
new file mode 100644
index 0000000000..f201213e46
--- /dev/null
+++ b/platform/android/src/style/layers/background_layer.hpp
@@ -0,0 +1,39 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/background_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class BackgroundLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/BackgroundLayer"; };
+
+ static jni::Class<BackgroundLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ BackgroundLayer(jni::JNIEnv&, jni::String);
+
+ BackgroundLayer(mbgl::Map&, mbgl::style::BackgroundLayer&);
+
+ ~BackgroundLayer();
+
+ // Property getters
+ jni::Object<jni::ObjectTag> getBackgroundColor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getBackgroundPattern(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getBackgroundOpacity(jni::JNIEnv&);
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class BackgroundLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/circle_layer.cpp b/platform/android/src/style/layers/circle_layer.cpp
new file mode 100644
index 0000000000..c2d6ff06d1
--- /dev/null
+++ b/platform/android/src/style/layers/circle_layer.cpp
@@ -0,0 +1,95 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "circle_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+
+namespace mbgl {
+namespace android {
+
+ CircleLayer::CircleLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::CircleLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ }
+
+ CircleLayer::CircleLayer(mbgl::Map& map, mbgl::style::CircleLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ CircleLayer::~CircleLayer() = default;
+
+ // Property getters
+
+ jni::Object<jni::ObjectTag> CircleLayer::getCircleRadius(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCircleRadius());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> CircleLayer::getCircleColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCircleColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> CircleLayer::getCircleBlur(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCircleBlur());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> CircleLayer::getCircleOpacity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCircleOpacity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> CircleLayer::getCircleTranslate(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCircleTranslate());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> CircleLayer::getCircleTranslateAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCircleTranslateAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> CircleLayer::getCirclePitchScale(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCirclePitchScale());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Class<CircleLayer> CircleLayer::javaClass;
+
+ jni::jobject* CircleLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = CircleLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return CircleLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void CircleLayer::registerNative(jni::JNIEnv& env) {
+ //Lookup the class
+ CircleLayer::javaClass = *jni::Class<CircleLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<CircleLayer>(
+ env, CircleLayer::javaClass, "nativePtr",
+ std::make_unique<CircleLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize",
+ METHOD(&CircleLayer::getCircleRadius, "nativeGetCircleRadius"),
+ METHOD(&CircleLayer::getCircleColor, "nativeGetCircleColor"),
+ METHOD(&CircleLayer::getCircleBlur, "nativeGetCircleBlur"),
+ METHOD(&CircleLayer::getCircleOpacity, "nativeGetCircleOpacity"),
+ METHOD(&CircleLayer::getCircleTranslate, "nativeGetCircleTranslate"),
+ METHOD(&CircleLayer::getCircleTranslateAnchor, "nativeGetCircleTranslateAnchor"),
+ METHOD(&CircleLayer::getCirclePitchScale, "nativeGetCirclePitchScale"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/circle_layer.hpp b/platform/android/src/style/layers/circle_layer.hpp
new file mode 100644
index 0000000000..91c99c686e
--- /dev/null
+++ b/platform/android/src/style/layers/circle_layer.hpp
@@ -0,0 +1,47 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class CircleLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/CircleLayer"; };
+
+ static jni::Class<CircleLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ CircleLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ CircleLayer(mbgl::Map&, mbgl::style::CircleLayer&);
+
+ ~CircleLayer();
+
+ // Property getters
+ jni::Object<jni::ObjectTag> getCircleRadius(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getCircleColor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getCircleBlur(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getCircleOpacity(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getCircleTranslate(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getCircleTranslateAnchor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getCirclePitchScale(jni::JNIEnv&);
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class CircleLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/custom_layer.cpp b/platform/android/src/style/layers/custom_layer.cpp
new file mode 100644
index 0000000000..4be6f3800d
--- /dev/null
+++ b/platform/android/src/style/layers/custom_layer.cpp
@@ -0,0 +1,57 @@
+#include "custom_layer.hpp"
+
+#include <string>
+
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+namespace android {
+
+ CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, 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::CustomLayerDeinitializeFunction>(deinitializeFunction),
+ reinterpret_cast<void*>(context))
+ ) {
+ }
+
+ CustomLayer::CustomLayer(mbgl::Map& map, mbgl::style::CustomLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ CustomLayer::~CustomLayer() = default;
+
+ void CustomLayer::update(jni::JNIEnv&) {
+ if (map) {
+ map->update(mbgl::Update::Repaint);
+ } else {
+ Log::Error(mbgl::Event::JNI, "No map reference, cannot update");
+ }
+ }
+
+ jni::Class<CustomLayer> CustomLayer::javaClass;
+
+ jni::jobject* CustomLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = CustomLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return CustomLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void CustomLayer::registerNative(jni::JNIEnv& env) {
+ //Lookup the class
+ CustomLayer::javaClass = *jni::Class<CustomLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //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>,
+ "initialize",
+ "finalize",
+ METHOD(&CustomLayer::update, "nativeUpdate"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/custom_layer.hpp b/platform/android/src/style/layers/custom_layer.hpp
new file mode 100644
index 0000000000..1173d21bfd
--- /dev/null
+++ b/platform/android/src/style/layers/custom_layer.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/custom_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class CustomLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/CustomLayer"; };
+
+ static jni::Class<CustomLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong);
+
+ CustomLayer(mbgl::Map&, mbgl::style::CustomLayer&);
+
+ ~CustomLayer();
+
+ void update(jni::JNIEnv&);
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class CustomLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/fill_layer.cpp b/platform/android/src/style/layers/fill_layer.cpp
new file mode 100644
index 0000000000..8cb96c9cd3
--- /dev/null
+++ b/platform/android/src/style/layers/fill_layer.cpp
@@ -0,0 +1,95 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "fill_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+
+namespace mbgl {
+namespace android {
+
+ FillLayer::FillLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::FillLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ }
+
+ FillLayer::FillLayer(mbgl::Map& map, mbgl::style::FillLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ FillLayer::~FillLayer() = default;
+
+ // Property getters
+
+ jni::Object<jni::ObjectTag> FillLayer::getFillAntialias(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillLayer>()->FillLayer::getFillAntialias());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> FillLayer::getFillOpacity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillLayer>()->FillLayer::getFillOpacity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> FillLayer::getFillColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillLayer>()->FillLayer::getFillColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> FillLayer::getFillOutlineColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillLayer>()->FillLayer::getFillOutlineColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> FillLayer::getFillTranslate(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillLayer>()->FillLayer::getFillTranslate());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> FillLayer::getFillTranslateAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillLayer>()->FillLayer::getFillTranslateAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> FillLayer::getFillPattern(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillLayer>()->FillLayer::getFillPattern());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Class<FillLayer> FillLayer::javaClass;
+
+ jni::jobject* FillLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = FillLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return FillLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void FillLayer::registerNative(jni::JNIEnv& env) {
+ //Lookup the class
+ FillLayer::javaClass = *jni::Class<FillLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<FillLayer>(
+ env, FillLayer::javaClass, "nativePtr",
+ std::make_unique<FillLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize",
+ METHOD(&FillLayer::getFillAntialias, "nativeGetFillAntialias"),
+ METHOD(&FillLayer::getFillOpacity, "nativeGetFillOpacity"),
+ METHOD(&FillLayer::getFillColor, "nativeGetFillColor"),
+ METHOD(&FillLayer::getFillOutlineColor, "nativeGetFillOutlineColor"),
+ METHOD(&FillLayer::getFillTranslate, "nativeGetFillTranslate"),
+ METHOD(&FillLayer::getFillTranslateAnchor, "nativeGetFillTranslateAnchor"),
+ METHOD(&FillLayer::getFillPattern, "nativeGetFillPattern"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/fill_layer.hpp b/platform/android/src/style/layers/fill_layer.hpp
new file mode 100644
index 0000000000..5dbd7a3412
--- /dev/null
+++ b/platform/android/src/style/layers/fill_layer.hpp
@@ -0,0 +1,47 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class FillLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/FillLayer"; };
+
+ static jni::Class<FillLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ FillLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ FillLayer(mbgl::Map&, mbgl::style::FillLayer&);
+
+ ~FillLayer();
+
+ // Property getters
+ jni::Object<jni::ObjectTag> getFillAntialias(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillOpacity(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillColor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillOutlineColor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillTranslate(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillTranslateAnchor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillPattern(jni::JNIEnv&);
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class FillLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp
new file mode 100644
index 0000000000..4d5f90f67e
--- /dev/null
+++ b/platform/android/src/style/layers/layer.cpp
@@ -0,0 +1,171 @@
+#include "layer.hpp"
+#include "../android_conversion.hpp"
+
+#include <jni/jni.hpp>
+
+#include <mbgl/platform/log.hpp>
+
+//Java -> C++ conversion
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/layer.hpp>
+#include <mbgl/style/conversion/source.hpp>
+
+//C++ -> Java conversion
+#include "../conversion/property_value.hpp"
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+
+ /**
+ * Invoked when the construction is initiated from the jvm through a subclass
+ */
+ Layer::Layer(jni::JNIEnv&, std::unique_ptr<mbgl::style::Layer> coreLayer)
+ : ownedLayer(std::move(coreLayer))
+ , layer(*ownedLayer) {
+ }
+
+ Layer::Layer(mbgl::Map& coreMap, mbgl::style::Layer& coreLayer) : layer(coreLayer) , map(&coreMap) {
+ }
+
+ Layer::~Layer() {
+ }
+
+ jni::String Layer::getId(jni::JNIEnv& env) {
+ return jni::Make<jni::String>(env, layer.getID());
+ }
+
+ std::unique_ptr<mbgl::style::Layer> Layer::releaseCoreLayer() {
+ assert(ownedLayer != nullptr);
+ return std::move(ownedLayer);
+ }
+
+ void Layer::setLayoutProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) {
+ Value value(env, jvalue);
+
+ //Convert and set property
+ optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setLayoutProperty(layer, jni::Make<std::string>(env, jname), value);
+ if (error) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message);
+ return;
+ }
+ }
+
+ void Layer::setPaintProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) {
+ Value value(env, jvalue);
+
+ //Convert and set property
+ optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value, mbgl::optional<std::string>());
+ if (error) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message);
+ return;
+ }
+ }
+
+ void Layer::updateStyle(jni::JNIEnv&, jni::jboolean updateClasses) {
+ //Update the style only if attached
+ if (ownedLayer == nullptr) {
+ Update flags = mbgl::Update::RecalculateStyle;
+ if(updateClasses) {
+ flags = flags | mbgl::Update::Classes;
+ }
+ map->update(flags);
+ } else {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Not updating as layer is not attached to map (yet)");
+ }
+ }
+
+ void Layer::setFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) {
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
+ Value wrapped(env, jfilter);
+ Filter filter;
+
+ Result<Filter> converted = convert<Filter>(wrapped);
+ if (!converted) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + converted.error().message);
+ return;
+ }
+ filter = std::move(*converted);
+
+ if (layer.is<FillLayer>()) {
+ layer.as<FillLayer>()->setFilter(filter);
+ } else if (layer.is<LineLayer>()) {
+ layer.as<LineLayer>()->setFilter(filter);
+ } else if (layer.is<SymbolLayer>()) {
+ layer.as<SymbolLayer>()->setFilter(filter);
+ } else if (layer.is<CircleLayer>()) {
+ layer.as<CircleLayer>()->setFilter(filter);
+ } else {
+ mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support filters");
+ }
+ }
+
+ void Layer::setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer) {
+ using namespace mbgl::style;
+
+ std::string layerId = jni::Make<std::string>(env, sourceLayer);
+
+ if (layer.is<FillLayer>()) {
+ layer.as<FillLayer>()->setSourceLayer(layerId);
+ } else if (layer.is<LineLayer>()) {
+ layer.as<LineLayer>()->setSourceLayer(layerId);
+ } else if (layer.is<SymbolLayer>()) {
+ layer.as<SymbolLayer>()->setSourceLayer(layerId);
+ } else if (layer.is<CircleLayer>()) {
+ layer.as<CircleLayer>()->setSourceLayer(layerId);
+ } else {
+ mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer");
+ }
+ }
+
+ jni::jfloat Layer::getMinZoom(jni::JNIEnv&){
+ return layer.getMinZoom();
+ }
+
+ jni::jfloat Layer::getMaxZoom(jni::JNIEnv&) {
+ return layer.getMaxZoom();
+ }
+
+ void Layer::setMinZoom(jni::JNIEnv&, jni::jfloat zoom) {
+ layer.setMinZoom(zoom);
+ }
+
+ void Layer::setMaxZoom(jni::JNIEnv&, jni::jfloat zoom) {
+ layer.setMaxZoom(zoom);
+ }
+
+ jni::Object<jni::ObjectTag> Layer::getVisibility(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ return jni::Object<jni::ObjectTag>(*convert<jni::jobject*>(env, layer.getVisibility()));
+ }
+
+ jni::Class<Layer> Layer::javaClass;
+
+ void Layer::registerNative(jni::JNIEnv& env) {
+ //Lookup the class
+ Layer::javaClass = *jni::Class<Layer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<Layer>(env, Layer::javaClass, "nativePtr",
+ METHOD(&Layer::getId, "nativeGetId"),
+ METHOD(&Layer::setLayoutProperty, "nativeSetLayoutProperty"),
+ METHOD(&Layer::setPaintProperty, "nativeSetPaintProperty"),
+ METHOD(&Layer::updateStyle, "nativeUpdateStyle"),
+ METHOD(&Layer::setFilter, "nativeSetFilter"),
+ METHOD(&Layer::setSourceLayer, "nativeSetSourceLayer"),
+ METHOD(&Layer::getMinZoom, "nativeGetMinZoom"),
+ METHOD(&Layer::getMaxZoom, "nativeGetMaxZoom"),
+ METHOD(&Layer::setMinZoom, "nativeSetMinZoom"),
+ METHOD(&Layer::setMaxZoom, "nativeSetMaxZoom"),
+ METHOD(&Layer::getVisibility, "nativeGetVisibility")
+ );
+
+ }
+
+} //android
+} //mbgl \ No newline at end of file
diff --git a/platform/android/src/style/layers/layer.cpp.ejs b/platform/android/src/style/layers/layer.cpp.ejs
new file mode 100644
index 0000000000..68dd27b801
--- /dev/null
+++ b/platform/android/src/style/layers/layer.cpp.ejs
@@ -0,0 +1,69 @@
+<%
+ const type = locals.type;
+ const properties = locals.properties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "<%- type %>_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+
+namespace mbgl {
+namespace android {
+
+<% if (type === 'background') { -%>
+ <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(jni::JNIEnv& env, jni::String layerId)
+ : Layer(env, std::make_unique<mbgl::style::<%- camelize(type) %>Layer>(jni::Make<std::string>(env, layerId))) {
+<% } else { -%>
+ <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::<%- camelize(type) %>Layer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+<% } -%>
+ }
+
+ <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(mbgl::Map& map, mbgl::style::<%- camelize(type) %>Layer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ <%- camelize(type) %>Layer::~<%- camelize(type) %>Layer() = default;
+
+ // Property getters
+
+<% for (const property of properties) { -%>
+ jni::Object<jni::ObjectTag> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::<%- camelize(type) %>Layer>()-><%- camelize(type) %>Layer::get<%- camelize(property.name) %>());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+<% } -%>
+ jni::Class<<%- camelize(type) %>Layer> <%- camelize(type) %>Layer::javaClass;
+
+ jni::jobject* <%- camelize(type) %>Layer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = <%- camelize(type) %>Layer::javaClass.template GetConstructor<jni::jlong>(env);
+ return <%- camelize(type) %>Layer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void <%- camelize(type) %>Layer::registerNative(jni::JNIEnv& env) {
+ //Lookup the class
+ <%- camelize(type) %>Layer::javaClass = *jni::Class<<%- camelize(type) %>Layer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<<%- camelize(type) %>Layer>(
+ env, <%- camelize(type) %>Layer::javaClass, "nativePtr",
+<% if (type === 'background') { -%>
+ std::make_unique<<%- camelize(type) %>Layer, JNIEnv&, jni::String>,
+<% } else { -%>
+ std::make_unique<<%- camelize(type) %>Layer, JNIEnv&, jni::String, jni::String>,
+<% } -%>
+ "initialize",
+ "finalize",<% for(var i = 0; i < properties.length; i++) {%>
+ METHOD(&<%- camelize(type) %>Layer::get<%- camelize(properties[i].name) %>, "nativeGet<%- camelize(properties[i].name) %>")<% if(i != (properties.length -1)) {-%>,<% } -%>
+<% } -%>);
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/layer.hpp b/platform/android/src/style/layers/layer.hpp
new file mode 100644
index 0000000000..37eb34cdd7
--- /dev/null
+++ b/platform/android/src/style/layers/layer.hpp
@@ -0,0 +1,80 @@
+#pragma once
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/style/layer.hpp>
+
+#include "../value.hpp"
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class Layer : private mbgl::util::noncopyable {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/Layer"; };
+
+ static jni::Class<Layer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ /*
+ * Called when a Java object is created on the c++ side
+ */
+ Layer(mbgl::Map&, mbgl::style::Layer&);
+
+ /*
+ * Called when a Java object was created from the jvm side
+ */
+ Layer(jni::JNIEnv&, std::unique_ptr<mbgl::style::Layer>);
+
+ virtual ~Layer();
+
+ virtual jni::jobject* createJavaPeer(jni::JNIEnv&) = 0;
+
+ jni::String getId(jni::JNIEnv&);
+
+ //Release the owned view and return it
+ std::unique_ptr<mbgl::style::Layer> releaseCoreLayer();
+
+ void setLayoutProperty(jni::JNIEnv&, jni::String, jni::Object<> value);
+
+ void setPaintProperty(jni::JNIEnv&, jni::String, jni::Object<> value);
+
+ void updateStyle(jni::JNIEnv&, jni::jboolean updateClasses);
+
+ //Zoom
+
+ jni::jfloat getMinZoom(jni::JNIEnv&);
+
+ jni::jfloat getMaxZoom(jni::JNIEnv&);
+
+ void setMinZoom(jni::JNIEnv&, jni::jfloat zoom);
+
+ void setMaxZoom(jni::JNIEnv&, jni::jfloat zoom);
+
+ /* common properties, but not shared by all */
+
+ void setFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter);
+
+ void setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer);
+
+ //Property getters
+
+ jni::Object<jni::ObjectTag> getVisibility(jni::JNIEnv&);
+
+protected:
+ std::unique_ptr<mbgl::style::Layer> ownedLayer;
+ mbgl::style::Layer& layer;
+ mbgl::Map* map;
+
+};
+
+} //android
+} //mbgl
+
+
+
+
diff --git a/platform/android/src/style/layers/layer.hpp.ejs b/platform/android/src/style/layers/layer.hpp.ejs
new file mode 100644
index 0000000000..826e133fca
--- /dev/null
+++ b/platform/android/src/style/layers/layer.hpp.ejs
@@ -0,0 +1,45 @@
+<%
+ const type = locals.type;
+ const properties = locals.properties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/<%- type %>_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class <%- camelize(type) %>Layer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/<%- camelize(type) %>Layer"; };
+
+ static jni::Class<<%- camelize(type) %>Layer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+<% if (type === 'background') { -%>
+ <%- camelize(type) %>Layer(jni::JNIEnv&, jni::String);
+<% } else { -%>
+ <%- camelize(type) %>Layer(jni::JNIEnv&, jni::String, jni::String);
+<% } -%>
+
+ <%- camelize(type) %>Layer(mbgl::Map&, mbgl::style::<%- camelize(type) %>Layer&);
+
+ ~<%- camelize(type) %>Layer();
+
+ // Property getters
+<% for (const property of properties) { -%>
+ jni::Object<jni::ObjectTag> get<%- camelize(property.name) %>(jni::JNIEnv&);
+
+<% } -%>
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class <%- camelize(type) %>Layer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp
new file mode 100644
index 0000000000..57dbf6f4b1
--- /dev/null
+++ b/platform/android/src/style/layers/layers.cpp
@@ -0,0 +1,64 @@
+#include "layers.hpp"
+
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/layers/custom_layer.hpp>
+
+#include "background_layer.hpp"
+#include "circle_layer.hpp"
+#include "fill_layer.hpp"
+#include "line_layer.hpp"
+#include "raster_layer.hpp"
+#include "symbol_layer.hpp"
+#include "custom_layer.hpp"
+
+namespace mbgl {
+namespace android {
+
+Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) {
+ Layer* layer;
+ if (coreLayer.is<mbgl::style::BackgroundLayer>()) {
+ layer = new BackgroundLayer(map, *coreLayer.as<mbgl::style::BackgroundLayer>());
+ } else if (coreLayer.is<mbgl::style::CircleLayer>()) {
+ layer = new CircleLayer(map, *coreLayer.as<mbgl::style::CircleLayer>());
+ } else if (coreLayer.is<mbgl::style::FillLayer>()) {
+ layer = new FillLayer(map, *coreLayer.as<mbgl::style::FillLayer>());
+ } else if (coreLayer.is<mbgl::style::LineLayer>()) {
+ layer = new LineLayer(map, *coreLayer.as<mbgl::style::LineLayer>());
+ } else if (coreLayer.is<mbgl::style::RasterLayer>()) {
+ layer = new RasterLayer(map, *coreLayer.as<mbgl::style::RasterLayer>());
+ } else if (coreLayer.is<mbgl::style::SymbolLayer>()) {
+ layer = new SymbolLayer(map, *coreLayer.as<mbgl::style::SymbolLayer>());
+ } else if (coreLayer.is<mbgl::style::CustomLayer>()) {
+ layer = new CustomLayer(map, *coreLayer.as<mbgl::style::CustomLayer>());
+ } else {
+ throw new std::runtime_error("Layer type not implemented");
+ }
+
+ return layer;
+}
+
+jni::jobject* createJavaLayerPeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style::Layer& coreLayer) {
+ std::unique_ptr<Layer> peerLayer = std::unique_ptr<Layer>(initializeLayerPeer(map, coreLayer));
+ jni::jobject* result = peerLayer->createJavaPeer(env);
+ peerLayer.release();
+ return result;
+}
+
+void registerNativeLayers(jni::JNIEnv& env) {
+ Layer::registerNative(env);
+ BackgroundLayer::registerNative(env);
+ CircleLayer::registerNative(env);
+ FillLayer::registerNative(env);
+ LineLayer::registerNative(env);
+ RasterLayer::registerNative(env);
+ SymbolLayer::registerNative(env);
+ CustomLayer::registerNative(env);
+}
+
+} //android
+} //mbgl \ No newline at end of file
diff --git a/platform/android/src/style/layers/layers.hpp b/platform/android/src/style/layers/layers.hpp
new file mode 100644
index 0000000000..0c979ec2cf
--- /dev/null
+++ b/platform/android/src/style/layers/layers.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/style/layer.hpp>
+
+#include "layer.hpp"
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+mbgl::android::Layer* initializeLayerPeer(mbgl::Map&, mbgl::style::Layer&);
+
+jni::jobject* createJavaLayerPeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Layer&);
+
+void registerNativeLayers(jni::JNIEnv&);
+
+}
+} \ No newline at end of file
diff --git a/platform/android/src/style/layers/line_layer.cpp b/platform/android/src/style/layers/line_layer.cpp
new file mode 100644
index 0000000000..91d3e4a1cd
--- /dev/null
+++ b/platform/android/src/style/layers/line_layer.cpp
@@ -0,0 +1,144 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "line_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+
+namespace mbgl {
+namespace android {
+
+ LineLayer::LineLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::LineLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ }
+
+ LineLayer::LineLayer(mbgl::Map& map, mbgl::style::LineLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ LineLayer::~LineLayer() = default;
+
+ // Property getters
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineCap(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineCap());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineJoin(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineJoin());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineMiterLimit(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineMiterLimit());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineRoundLimit(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineRoundLimit());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineOpacity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineOpacity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineTranslate(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineTranslate());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineTranslateAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineTranslateAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineWidth(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineWidth());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineGapWidth(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineGapWidth());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineOffset(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineOffset());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineBlur(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineBlur());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLineDasharray(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLineDasharray());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> LineLayer::getLinePattern(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::LineLayer>()->LineLayer::getLinePattern());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Class<LineLayer> LineLayer::javaClass;
+
+ jni::jobject* LineLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = LineLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return LineLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void LineLayer::registerNative(jni::JNIEnv& env) {
+ //Lookup the class
+ LineLayer::javaClass = *jni::Class<LineLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<LineLayer>(
+ env, LineLayer::javaClass, "nativePtr",
+ std::make_unique<LineLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize",
+ METHOD(&LineLayer::getLineCap, "nativeGetLineCap"),
+ METHOD(&LineLayer::getLineJoin, "nativeGetLineJoin"),
+ METHOD(&LineLayer::getLineMiterLimit, "nativeGetLineMiterLimit"),
+ METHOD(&LineLayer::getLineRoundLimit, "nativeGetLineRoundLimit"),
+ METHOD(&LineLayer::getLineOpacity, "nativeGetLineOpacity"),
+ METHOD(&LineLayer::getLineColor, "nativeGetLineColor"),
+ METHOD(&LineLayer::getLineTranslate, "nativeGetLineTranslate"),
+ METHOD(&LineLayer::getLineTranslateAnchor, "nativeGetLineTranslateAnchor"),
+ METHOD(&LineLayer::getLineWidth, "nativeGetLineWidth"),
+ METHOD(&LineLayer::getLineGapWidth, "nativeGetLineGapWidth"),
+ METHOD(&LineLayer::getLineOffset, "nativeGetLineOffset"),
+ METHOD(&LineLayer::getLineBlur, "nativeGetLineBlur"),
+ METHOD(&LineLayer::getLineDasharray, "nativeGetLineDasharray"),
+ METHOD(&LineLayer::getLinePattern, "nativeGetLinePattern"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/line_layer.hpp b/platform/android/src/style/layers/line_layer.hpp
new file mode 100644
index 0000000000..da9b564325
--- /dev/null
+++ b/platform/android/src/style/layers/line_layer.hpp
@@ -0,0 +1,61 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/line_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class LineLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/LineLayer"; };
+
+ static jni::Class<LineLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ LineLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ LineLayer(mbgl::Map&, mbgl::style::LineLayer&);
+
+ ~LineLayer();
+
+ // Property getters
+ jni::Object<jni::ObjectTag> getLineCap(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineJoin(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineMiterLimit(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineRoundLimit(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineOpacity(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineColor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineTranslate(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineTranslateAnchor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineWidth(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineGapWidth(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineOffset(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineBlur(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLineDasharray(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getLinePattern(jni::JNIEnv&);
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class LineLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/raster_layer.cpp b/platform/android/src/style/layers/raster_layer.cpp
new file mode 100644
index 0000000000..da78ccb8e3
--- /dev/null
+++ b/platform/android/src/style/layers/raster_layer.cpp
@@ -0,0 +1,95 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "raster_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+
+namespace mbgl {
+namespace android {
+
+ RasterLayer::RasterLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::RasterLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ }
+
+ RasterLayer::RasterLayer(mbgl::Map& map, mbgl::style::RasterLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ RasterLayer::~RasterLayer() = default;
+
+ // Property getters
+
+ jni::Object<jni::ObjectTag> RasterLayer::getRasterOpacity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::RasterLayer>()->RasterLayer::getRasterOpacity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> RasterLayer::getRasterHueRotate(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::RasterLayer>()->RasterLayer::getRasterHueRotate());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> RasterLayer::getRasterBrightnessMin(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::RasterLayer>()->RasterLayer::getRasterBrightnessMin());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> RasterLayer::getRasterBrightnessMax(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::RasterLayer>()->RasterLayer::getRasterBrightnessMax());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> RasterLayer::getRasterSaturation(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::RasterLayer>()->RasterLayer::getRasterSaturation());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> RasterLayer::getRasterContrast(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::RasterLayer>()->RasterLayer::getRasterContrast());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> RasterLayer::getRasterFadeDuration(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::RasterLayer>()->RasterLayer::getRasterFadeDuration());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Class<RasterLayer> RasterLayer::javaClass;
+
+ jni::jobject* RasterLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = RasterLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return RasterLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void RasterLayer::registerNative(jni::JNIEnv& env) {
+ //Lookup the class
+ RasterLayer::javaClass = *jni::Class<RasterLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<RasterLayer>(
+ env, RasterLayer::javaClass, "nativePtr",
+ std::make_unique<RasterLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize",
+ METHOD(&RasterLayer::getRasterOpacity, "nativeGetRasterOpacity"),
+ METHOD(&RasterLayer::getRasterHueRotate, "nativeGetRasterHueRotate"),
+ METHOD(&RasterLayer::getRasterBrightnessMin, "nativeGetRasterBrightnessMin"),
+ METHOD(&RasterLayer::getRasterBrightnessMax, "nativeGetRasterBrightnessMax"),
+ METHOD(&RasterLayer::getRasterSaturation, "nativeGetRasterSaturation"),
+ METHOD(&RasterLayer::getRasterContrast, "nativeGetRasterContrast"),
+ METHOD(&RasterLayer::getRasterFadeDuration, "nativeGetRasterFadeDuration"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/raster_layer.hpp b/platform/android/src/style/layers/raster_layer.hpp
new file mode 100644
index 0000000000..e59cd05f87
--- /dev/null
+++ b/platform/android/src/style/layers/raster_layer.hpp
@@ -0,0 +1,47 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class RasterLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/RasterLayer"; };
+
+ static jni::Class<RasterLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ RasterLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ RasterLayer(mbgl::Map&, mbgl::style::RasterLayer&);
+
+ ~RasterLayer();
+
+ // Property getters
+ jni::Object<jni::ObjectTag> getRasterOpacity(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getRasterHueRotate(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getRasterBrightnessMin(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getRasterBrightnessMax(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getRasterSaturation(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getRasterContrast(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getRasterFadeDuration(jni::JNIEnv&);
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class RasterLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp
new file mode 100644
index 0000000000..fd69b6591f
--- /dev/null
+++ b/platform/android/src/style/layers/symbol_layer.cpp
@@ -0,0 +1,382 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "symbol_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+
+namespace mbgl {
+namespace android {
+
+ SymbolLayer::SymbolLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::SymbolLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ }
+
+ SymbolLayer::SymbolLayer(mbgl::Map& map, mbgl::style::SymbolLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ SymbolLayer::~SymbolLayer() = default;
+
+ // Property getters
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getSymbolPlacement(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getSymbolPlacement());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getSymbolSpacing(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getSymbolSpacing());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getSymbolAvoidEdges(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getSymbolAvoidEdges());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconAllowOverlap(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconAllowOverlap());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconIgnorePlacement(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconIgnorePlacement());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconOptional(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconOptional());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconRotationAlignment(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconRotationAlignment());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconSize(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconSize());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconTextFit(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconTextFit());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconTextFitPadding(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconTextFitPadding());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconImage(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconImage());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconRotate(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconRotate());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconPadding(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconPadding());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconKeepUpright(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconKeepUpright());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconOffset(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconOffset());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextPitchAlignment(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextPitchAlignment());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextRotationAlignment(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextRotationAlignment());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextField(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextField());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextFont(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextFont());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextSize(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextSize());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextMaxWidth(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextMaxWidth());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextLineHeight(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextLineHeight());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextLetterSpacing(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextLetterSpacing());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextJustify(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextJustify());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextMaxAngle(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextMaxAngle());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextRotate(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextRotate());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextPadding(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextPadding());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextKeepUpright(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextKeepUpright());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextTransform(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextTransform());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextOffset(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextOffset());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextAllowOverlap(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextAllowOverlap());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextIgnorePlacement(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextIgnorePlacement());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextOptional(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextOptional());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconOpacity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconOpacity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconHaloColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconHaloColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconHaloWidth(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconHaloWidth());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconHaloBlur(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconHaloBlur());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconTranslate(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconTranslate());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconTranslateAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconTranslateAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextOpacity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextOpacity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextHaloColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextHaloColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextHaloWidth(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextHaloWidth());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextHaloBlur(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextHaloBlur());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextTranslate(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextTranslate());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getTextTranslateAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextTranslateAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Class<SymbolLayer> SymbolLayer::javaClass;
+
+ jni::jobject* SymbolLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = SymbolLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return SymbolLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void SymbolLayer::registerNative(jni::JNIEnv& env) {
+ //Lookup the class
+ SymbolLayer::javaClass = *jni::Class<SymbolLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<SymbolLayer>(
+ env, SymbolLayer::javaClass, "nativePtr",
+ std::make_unique<SymbolLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize",
+ METHOD(&SymbolLayer::getSymbolPlacement, "nativeGetSymbolPlacement"),
+ METHOD(&SymbolLayer::getSymbolSpacing, "nativeGetSymbolSpacing"),
+ METHOD(&SymbolLayer::getSymbolAvoidEdges, "nativeGetSymbolAvoidEdges"),
+ METHOD(&SymbolLayer::getIconAllowOverlap, "nativeGetIconAllowOverlap"),
+ METHOD(&SymbolLayer::getIconIgnorePlacement, "nativeGetIconIgnorePlacement"),
+ METHOD(&SymbolLayer::getIconOptional, "nativeGetIconOptional"),
+ METHOD(&SymbolLayer::getIconRotationAlignment, "nativeGetIconRotationAlignment"),
+ METHOD(&SymbolLayer::getIconSize, "nativeGetIconSize"),
+ METHOD(&SymbolLayer::getIconTextFit, "nativeGetIconTextFit"),
+ METHOD(&SymbolLayer::getIconTextFitPadding, "nativeGetIconTextFitPadding"),
+ METHOD(&SymbolLayer::getIconImage, "nativeGetIconImage"),
+ METHOD(&SymbolLayer::getIconRotate, "nativeGetIconRotate"),
+ METHOD(&SymbolLayer::getIconPadding, "nativeGetIconPadding"),
+ METHOD(&SymbolLayer::getIconKeepUpright, "nativeGetIconKeepUpright"),
+ METHOD(&SymbolLayer::getIconOffset, "nativeGetIconOffset"),
+ METHOD(&SymbolLayer::getTextPitchAlignment, "nativeGetTextPitchAlignment"),
+ METHOD(&SymbolLayer::getTextRotationAlignment, "nativeGetTextRotationAlignment"),
+ METHOD(&SymbolLayer::getTextField, "nativeGetTextField"),
+ METHOD(&SymbolLayer::getTextFont, "nativeGetTextFont"),
+ METHOD(&SymbolLayer::getTextSize, "nativeGetTextSize"),
+ METHOD(&SymbolLayer::getTextMaxWidth, "nativeGetTextMaxWidth"),
+ METHOD(&SymbolLayer::getTextLineHeight, "nativeGetTextLineHeight"),
+ METHOD(&SymbolLayer::getTextLetterSpacing, "nativeGetTextLetterSpacing"),
+ METHOD(&SymbolLayer::getTextJustify, "nativeGetTextJustify"),
+ METHOD(&SymbolLayer::getTextAnchor, "nativeGetTextAnchor"),
+ METHOD(&SymbolLayer::getTextMaxAngle, "nativeGetTextMaxAngle"),
+ METHOD(&SymbolLayer::getTextRotate, "nativeGetTextRotate"),
+ METHOD(&SymbolLayer::getTextPadding, "nativeGetTextPadding"),
+ METHOD(&SymbolLayer::getTextKeepUpright, "nativeGetTextKeepUpright"),
+ METHOD(&SymbolLayer::getTextTransform, "nativeGetTextTransform"),
+ METHOD(&SymbolLayer::getTextOffset, "nativeGetTextOffset"),
+ METHOD(&SymbolLayer::getTextAllowOverlap, "nativeGetTextAllowOverlap"),
+ METHOD(&SymbolLayer::getTextIgnorePlacement, "nativeGetTextIgnorePlacement"),
+ METHOD(&SymbolLayer::getTextOptional, "nativeGetTextOptional"),
+ METHOD(&SymbolLayer::getIconOpacity, "nativeGetIconOpacity"),
+ METHOD(&SymbolLayer::getIconColor, "nativeGetIconColor"),
+ METHOD(&SymbolLayer::getIconHaloColor, "nativeGetIconHaloColor"),
+ METHOD(&SymbolLayer::getIconHaloWidth, "nativeGetIconHaloWidth"),
+ METHOD(&SymbolLayer::getIconHaloBlur, "nativeGetIconHaloBlur"),
+ METHOD(&SymbolLayer::getIconTranslate, "nativeGetIconTranslate"),
+ METHOD(&SymbolLayer::getIconTranslateAnchor, "nativeGetIconTranslateAnchor"),
+ METHOD(&SymbolLayer::getTextOpacity, "nativeGetTextOpacity"),
+ METHOD(&SymbolLayer::getTextColor, "nativeGetTextColor"),
+ METHOD(&SymbolLayer::getTextHaloColor, "nativeGetTextHaloColor"),
+ METHOD(&SymbolLayer::getTextHaloWidth, "nativeGetTextHaloWidth"),
+ METHOD(&SymbolLayer::getTextHaloBlur, "nativeGetTextHaloBlur"),
+ METHOD(&SymbolLayer::getTextTranslate, "nativeGetTextTranslate"),
+ METHOD(&SymbolLayer::getTextTranslateAnchor, "nativeGetTextTranslateAnchor"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/symbol_layer.hpp b/platform/android/src/style/layers/symbol_layer.hpp
new file mode 100644
index 0000000000..f5165327bf
--- /dev/null
+++ b/platform/android/src/style/layers/symbol_layer.hpp
@@ -0,0 +1,129 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class SymbolLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/SymbolLayer"; };
+
+ static jni::Class<SymbolLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ SymbolLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ SymbolLayer(mbgl::Map&, mbgl::style::SymbolLayer&);
+
+ ~SymbolLayer();
+
+ // Property getters
+ jni::Object<jni::ObjectTag> getSymbolPlacement(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getSymbolSpacing(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getSymbolAvoidEdges(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconAllowOverlap(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconIgnorePlacement(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconOptional(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconRotationAlignment(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconSize(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconTextFit(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconTextFitPadding(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconImage(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconRotate(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconPadding(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconKeepUpright(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconOffset(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextPitchAlignment(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextRotationAlignment(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextField(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextFont(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextSize(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextMaxWidth(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextLineHeight(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextLetterSpacing(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextJustify(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextAnchor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextMaxAngle(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextRotate(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextPadding(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextKeepUpright(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextTransform(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextOffset(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextAllowOverlap(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextIgnorePlacement(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextOptional(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconOpacity(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconColor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconHaloColor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconHaloWidth(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconHaloBlur(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconTranslate(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconTranslateAnchor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextOpacity(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextColor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextHaloColor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextHaloWidth(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextHaloBlur(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextTranslate(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getTextTranslateAnchor(jni::JNIEnv&);
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class SymbolLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/sources/sources.cpp b/platform/android/src/style/sources/sources.cpp
new file mode 100644
index 0000000000..47c9757e9d
--- /dev/null
+++ b/platform/android/src/style/sources/sources.cpp
@@ -0,0 +1,28 @@
+#include "sources.hpp"
+
+#include "../value.hpp"
+#include "../android_conversion.hpp"
+#include "../android_geojson.hpp"
+
+#include <mbgl/util/constants.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/source.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+
+ mbgl::optional<std::unique_ptr<mbgl::style::Source>> convertToNativeSource(jni::JNIEnv& env, jni::Object<jni::jobject> jvalue, jni::String id) {
+ using namespace mbgl::style;
+
+ Value value(env, jvalue);
+ conversion::Result<std::unique_ptr<Source>> source = conversion::convert<std::unique_ptr<Source>>(value, jni::Make<std::string>(env, id));
+ if (!source) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Unable to add source: " + source.error().message);
+ return {};
+ }
+ return std::move(*source);
+ }
+}
+} \ No newline at end of file
diff --git a/platform/android/src/style/sources/sources.hpp b/platform/android/src/style/sources/sources.hpp
new file mode 100644
index 0000000000..b967685dfb
--- /dev/null
+++ b/platform/android/src/style/sources/sources.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+ mbgl::optional<std::unique_ptr<mbgl::style::Source>> convertToNativeSource(jni::JNIEnv& env, jni::Object<jni::jobject> jsource, jni::String id);
+
+}
+} \ No newline at end of file
diff --git a/platform/android/src/style/value.cpp b/platform/android/src/style/value.cpp
new file mode 100644
index 0000000000..daad3e998d
--- /dev/null
+++ b/platform/android/src/style/value.cpp
@@ -0,0 +1,67 @@
+#include "value.hpp"
+
+#include "../java_types.hpp"
+
+namespace mbgl {
+namespace android {
+
+ //Instance
+
+ Value::Value(jni::JNIEnv& env, jni::jobject* _value) : jenv(env), value(_value) {}
+
+ Value::~Value() {}
+
+ bool Value::isNull() const {
+ return value == nullptr;
+ }
+
+ bool Value::isArray() const {
+ return jni::IsInstanceOf(jenv, value, *java::ObjectArray::jclass);
+ }
+
+ bool Value::isObject() const {
+ return jni::IsInstanceOf(jenv, value, *java::Map::jclass);;
+ }
+
+ bool Value::isString() const {
+ return jni::IsInstanceOf(jenv, value, *java::String::jclass);
+ }
+
+ bool Value::isBool() const {
+ return jni::IsInstanceOf(jenv, value, *java::Boolean::jclass);
+ }
+
+ bool Value::isNumber() const {
+ return jni::IsInstanceOf(jenv, value, *java::Number::jclass);
+ }
+
+ std::string Value::toString() const {
+ jni::jstring* string = reinterpret_cast<jni::jstring*>(value);
+ return jni::Make<std::string>(jenv, jni::String(string));
+ }
+
+ float Value::toNumber() const {
+ return jni::CallMethod<jni::jfloat>(jenv, value, *java::Number::floatValueMethodId);
+ }
+
+ bool Value::toBool() const {
+ return jni::CallMethod<jni::jboolean>(jenv, value, *java::Boolean::booleanValueMethodId);
+ }
+
+ Value Value::get(const char* key) const {
+ jni::jobject* member = jni::CallMethod<jni::jobject*>(jenv, value, *java::Map::getMethodId, jni::Make<jni::String>(jenv, std::string(key)).Get());
+ return Value(jenv, member);
+ }
+
+ int Value::getLength() const {
+ auto array = (jni::jarray<jni::jobject>*) value;
+ return jni::GetArrayLength(jenv, *array);
+ }
+
+ Value Value::get(const int index ) const {
+ auto array = (jni::jarray<jni::jobject>*) value;
+ return Value(jenv, jni::GetObjectArrayElement(jenv, *array, index));
+ }
+}
+}
+
diff --git a/platform/android/src/style/value.hpp b/platform/android/src/style/value.hpp
new file mode 100644
index 0000000000..2f0b1d9706
--- /dev/null
+++ b/platform/android/src/style/value.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <jni/jni.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+
+class Value {
+public:
+
+ Value(jni::JNIEnv&, jni::jobject*);
+ virtual ~Value();
+
+ bool isNull() const;
+ bool isArray() const;
+ bool isObject() const;
+ bool isString() const;
+ bool isBool() const;
+ bool isNumber() const;
+
+ std::string toString() const;
+ float toNumber() const;
+ bool toBool() const;
+ Value get(const char* key) const;
+ int getLength() const;
+ Value get(const int index ) const;
+
+ jni::JNIEnv& jenv;
+ jni::jobject* value;
+};
+
+}
+}
diff --git a/platform/android/src/thread.cpp b/platform/android/src/thread.cpp
new file mode 100644
index 0000000000..77f9815866
--- /dev/null
+++ b/platform/android/src/thread.cpp
@@ -0,0 +1,37 @@
+#include <mbgl/platform/log.hpp>
+#include <mbgl/platform/platform.hpp>
+
+#include <sys/prctl.h>
+#include <sys/resource.h>
+
+// Implementation based on Chromium's platform_thread_android.cc.
+
+namespace mbgl {
+namespace platform {
+
+std::string getCurrentThreadName() {
+ char name[32] = "unknown";
+
+ if (prctl(PR_GET_NAME, name) == -1) {
+ Log::Warning(Event::General, "Couldn't get thread name");
+ }
+
+ return name;
+}
+
+void setCurrentThreadName(const std::string& name) {
+ if (prctl(PR_SET_NAME, name.c_str()) == -1) {
+ Log::Warning(Event::General, "Couldn't set thread name");
+ }
+}
+
+void makeThreadLowPriority() {
+ // ANDROID_PRIORITY_LOWEST = 19
+ //
+ // Supposedly would set the priority for the whole process, but
+ // on Linux/Android it only sets for the current thread.
+ setpriority(PRIO_PROCESS, 0, 19);
+}
+
+} // namespace platform
+} // namespace mbgl
diff --git a/platform/android/src/timer.cpp b/platform/android/src/timer.cpp
index 741005df23..7057d6de70 100644
--- a/platform/android/src/timer.cpp
+++ b/platform/android/src/timer.cpp
@@ -29,14 +29,13 @@ public:
}
void stop() {
- task = nullptr;
loop->removeRunnable(this);
}
void reschedule() {
if (repeat != Duration::zero()) {
due = Clock::now() + repeat;
- loop->addRunnable(this);
+ loop->wake();
} else {
stop();
}
@@ -47,8 +46,8 @@ public:
}
void runTask() override {
- task();
reschedule();
+ task();
}
private:
diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h
index 69cff6b054..1ab587ede5 100644
--- a/platform/darwin/src/MGLFeature.h
+++ b/platform/darwin/src/MGLFeature.h
@@ -29,13 +29,23 @@ NS_ASSUME_NONNULL_BEGIN
An object that uniquely identifies the feature in its containing
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources">tile source</a>.
- The value of this property is currently always an `NSNumber` object but may in
- the future be an instance of another class, such as `NSString`.
-
The identifier corresponds to the
<a href="https://github.com/mapbox/vector-tile-spec/tree/master/2.1#42-features">feature identifier</a>
(`id`) in the tile source. If the source does not specify the feature’s
- identifier, the value of this property is `nil`.
+ identifier, the value of this property is `nil`. If specified, the identifier
+ may be an integer, floating-point number, or string. These data types are
+ mapped to instances of the following Foundation classes:
+
+ <table>
+ <thead>
+ <tr><th>In the tile source</th><th>This property</th></tr>
+ </thead>
+ <tbody>
+ <tr><td>Integer</td> <td><code>NSNumber</code> (use the <code>unsignedLongLongValue</code> or <code>longLongValue</code> property)</td></tr>
+ <tr><td>Floating-point number</td> <td><code>NSNumber</code> (use the <code>doubleValue</code> property)</td></tr>
+ <tr><td>String</td> <td><code>NSString</code></td></tr>
+ </tbody>
+ </table>
For details about the identifiers used in most Mapbox-provided styles, consult
the
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
index 777b296303..3bf1e61153 100644
--- a/platform/darwin/src/MGLFeature.mm
+++ b/platform/darwin/src/MGLFeature.mm
@@ -118,7 +118,7 @@
*/
class PropertyValueEvaluator {
public:
- id operator()(const std::nullptr_t &) const {
+ id operator()(const mbgl::NullValue &) const {
return [NSNull null];
}
@@ -260,7 +260,7 @@ NS_ARRAY_OF(MGLShape <MGLFeature> *) *MGLFeaturesFromMBGLFeatures(const std::vec
GeometryEvaluator<double> evaluator;
MGLShape <MGLFeaturePrivate> *shape = mapbox::geometry::geometry<double>::visit(feature.geometry, evaluator);
if (feature.id) {
- shape.identifier = @(*feature.id);
+ shape.identifier = mbgl::FeatureIdentifier::visit(*feature.id, PropertyValueEvaluator());
}
shape.attributes = attributes;
[shapes addObject:shape];
diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm
index 6084535d05..a5b9eb8efc 100644
--- a/platform/darwin/src/MGLMultiPoint.mm
+++ b/platform/darwin/src/MGLMultiPoint.mm
@@ -5,11 +5,11 @@
mbgl::Color MGLColorObjectFromCGColorRef(CGColorRef cgColor) {
if (!cgColor) {
- return {{ 0, 0, 0, 0 }};
+ return { 0, 0, 0, 0 };
}
NSCAssert(CGColorGetNumberOfComponents(cgColor) >= 4, @"Color must have at least 4 components");
const CGFloat *components = CGColorGetComponents(cgColor);
- return {{ (float)components[0], (float)components[1], (float)components[2], (float)components[3] }};
+ return { (float)components[0], (float)components[1], (float)components[2], (float)components[3] };
}
@implementation MGLMultiPoint
diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm
index c009d9e3d6..b8f02b6406 100644
--- a/platform/darwin/src/MGLPolygon.mm
+++ b/platform/darwin/src/MGLPolygon.mm
@@ -44,9 +44,9 @@
}
mbgl::FillAnnotation annotation { geometry };
- annotation.opacity = [delegate alphaForShapeAnnotation:self];
- annotation.outlineColor = [delegate strokeColorForShapeAnnotation:self];
- annotation.color = [delegate fillColorForPolygonAnnotation:self];
+ annotation.opacity = { static_cast<float>([delegate alphaForShapeAnnotation:self]) };
+ annotation.outlineColor = { [delegate strokeColorForShapeAnnotation:self] };
+ annotation.color = { [delegate fillColorForPolygonAnnotation:self] };
return annotation;
}
diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm
index 15ea5a0952..b81147a3ba 100644
--- a/platform/darwin/src/MGLPolyline.mm
+++ b/platform/darwin/src/MGLPolyline.mm
@@ -24,9 +24,9 @@
}
mbgl::LineAnnotation annotation { geometry };
- annotation.opacity = [delegate alphaForShapeAnnotation:self];
- annotation.color = [delegate strokeColorForShapeAnnotation:self];
- annotation.width = [delegate lineWidthForPolylineAnnotation:self];
+ annotation.opacity = { static_cast<float>([delegate alphaForShapeAnnotation:self]) };
+ annotation.color = { [delegate strokeColorForShapeAnnotation:self] };
+ annotation.width = { static_cast<float>([delegate lineWidthForPolylineAnnotation:self]) };
return annotation;
}
diff --git a/platform/darwin/src/headless_view_cgl.cpp b/platform/darwin/src/headless_view_cgl.cpp
index a4f809a250..dc58463b5d 100644
--- a/platform/darwin/src/headless_view_cgl.cpp
+++ b/platform/darwin/src/headless_view_cgl.cpp
@@ -19,7 +19,7 @@ gl::glProc HeadlessView::initializeExtension(const char* name) {
}
void HeadlessView::createContext() {
- CGLError error = CGLCreateContext(display->pixelFormat, NULL, &glContext);
+ CGLError error = CGLCreateContext(display->pixelFormat, nullptr, &glContext);
if (error != kCGLNoError) {
throw std::runtime_error(std::string("Error creating GL context object:") + CGLErrorString(error) + "\n");
}
diff --git a/platform/darwin/src/log_nslog.mm b/platform/darwin/src/log_nslog.mm
index a2e31968ab..49583ae3c4 100644
--- a/platform/darwin/src/log_nslog.mm
+++ b/platform/darwin/src/log_nslog.mm
@@ -1,4 +1,5 @@
#include <mbgl/platform/log.hpp>
+#include <mbgl/util/enum.hpp>
#import <Foundation/Foundation.h>
@@ -7,7 +8,7 @@ namespace mbgl {
void Log::platformRecord(EventSeverity severity, const std::string &msg) {
NSString *message =
[[NSString alloc] initWithBytes:msg.data() length:msg.size() encoding:NSUTF8StringEncoding];
- NSLog(@"[%s] %@", EventSeverityClass(severity).c_str(), message);
+ NSLog(@"[%s] %@", Enum<EventSeverity>::toString(severity), message);
}
}
diff --git a/platform/darwin/src/nsthread.mm b/platform/darwin/src/nsthread.mm
index 9ac1d2caa0..eee6d6991b 100644
--- a/platform/darwin/src/nsthread.mm
+++ b/platform/darwin/src/nsthread.mm
@@ -2,9 +2,22 @@
#include <mbgl/platform/platform.hpp>
+#include <pthread.h>
+
namespace mbgl {
namespace platform {
+std::string getCurrentThreadName() {
+ char name[32] = "unknown";
+ pthread_getname_np(pthread_self(), name, sizeof(name));
+
+ return name;
+}
+
+void setCurrentThreadName(const std::string& name) {
+ pthread_setname_np(name.c_str());
+}
+
void makeThreadLowPriority() {
[[NSThread currentThread] setThreadPriority:0.0];
}
diff --git a/platform/darwin/src/settings_nsuserdefaults.mm b/platform/darwin/src/settings_nsuserdefaults.mm
deleted file mode 100644
index 548ee9b220..0000000000
--- a/platform/darwin/src/settings_nsuserdefaults.mm
+++ /dev/null
@@ -1,60 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#include <mbgl/platform/darwin/settings_nsuserdefaults.hpp>
-
-using namespace mbgl;
-
-Settings_NSUserDefaults::Settings_NSUserDefaults()
-{
- [[NSUserDefaults standardUserDefaults] registerDefaults:@{
- @"longitude" : @(longitude),
- @"latitude" : @(latitude),
- @"zoom" : @(zoom),
- @"bearing" : @(bearing),
- @"pitch" : @(pitch),
- @"userTrackingMode" : @(userTrackingMode),
- @"showsUserLocation" : @(showsUserLocation),
- @"debug" : @(debug),
- }];
- load();
-}
-
-void Settings_NSUserDefaults::load()
-{
- NSDictionary *settings = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
-
- longitude = [settings[@"longitude"] doubleValue];
- latitude = [settings[@"latitude"] doubleValue];
- zoom = [settings[@"zoom"] doubleValue];
- bearing = [settings[@"bearing"] doubleValue];
- pitch = [settings[@"pitch"] doubleValue];
- debug = [settings[@"debug"] boolValue];
-
- unsigned uncheckedTrackingMode = [settings[@"userTrackingMode"] unsignedIntValue];
- if (uncheckedTrackingMode > MGLUserTrackingModeNone &&
- uncheckedTrackingMode <= MGLUserTrackingModeFollowWithCourse)
- {
- userTrackingMode = (MGLUserTrackingMode)uncheckedTrackingMode;
- }
- showsUserLocation = [settings[@"showsUserLocation"] boolValue];
-}
-
-void Settings_NSUserDefaults::save()
-{
- [[NSUserDefaults standardUserDefaults] setValuesForKeysWithDictionary:@{
- @"longitude" : @(longitude),
- @"latitude" : @(latitude),
- @"zoom" : @(zoom),
- @"bearing" : @(bearing),
- @"pitch" : @(pitch),
- @"userTrackingMode" : @(userTrackingMode),
- @"showsUserLocation" : @(showsUserLocation),
- @"debug" : @(debug),
- }];
- [[NSUserDefaults standardUserDefaults] synchronize];
-}
-
-void Settings_NSUserDefaults::clear()
-{
- [NSUserDefaults resetStandardUserDefaults];
-}
diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm
index 6cf038d4fb..1b1722f172 100644
--- a/platform/darwin/test/MGLFeatureTests.mm
+++ b/platform/darwin/test/MGLFeatureTests.mm
@@ -1,6 +1,7 @@
#import <Mapbox/Mapbox.h>
#import <XCTest/XCTest.h>
+#import <mbgl/util/geometry.hpp>
#import "../../darwin/src/MGLFeature_Private.h"
@interface MGLFeatureTests : XCTestCase
@@ -12,16 +13,16 @@
- (void)testGeometryConversion {
std::vector<mbgl::Feature> features;
- mapbox::geometry::point<double> point = { -90.066667, 29.95 };
- features.emplace_back(point);
+ mbgl::Point<double> point = { -90.066667, 29.95 };
+ features.push_back(mbgl::Feature { point });
- mapbox::geometry::line_string<double> lineString = {
+ mbgl::LineString<double> lineString = {
{ -84.516667, 39.1 },
{ -90.066667, 29.95 },
};
- features.emplace_back(lineString);
+ features.push_back(mbgl::Feature { lineString });
- mapbox::geometry::polygon<double> polygon = {
+ mbgl::Polygon<double> polygon = {
{
{ 1, 1 },
{ 4, 1 },
@@ -35,7 +36,7 @@
{ 2, 3 },
},
};
- features.emplace_back(polygon);
+ features.push_back(mbgl::Feature { polygon });
NS_ARRAY_OF(MGLShape <MGLFeature> *) *shapes = MGLFeaturesFromMBGLFeatures(features);
XCTAssertEqual(shapes.count, 3, @"All features should be converted into shapes");
@@ -87,9 +88,9 @@
- (void)testPropertyConversion {
std::vector<mbgl::Feature> features;
- mapbox::geometry::point<double> point = { -90.066667, 29.95 };
- mbgl::Feature pointFeature(point);
- pointFeature.id = UINT64_MAX;
+ mbgl::Point<double> point = { -90.066667, 29.95 };
+ mbgl::Feature pointFeature { point };
+ pointFeature.id = { UINT64_MAX };
pointFeature.properties["null"] = nullptr;
pointFeature.properties["bool"] = true;
pointFeature.properties["unsigned int"] = UINT64_MAX;
diff --git a/platform/default/asset_file_source.cpp b/platform/default/asset_file_source.cpp
index 3a47f349fe..2872c6bb3a 100644
--- a/platform/default/asset_file_source.cpp
+++ b/platform/default/asset_file_source.cpp
@@ -14,8 +14,8 @@ namespace mbgl {
class AssetFileSource::Impl {
public:
- Impl(const std::string& root_)
- : root(root_) {
+ Impl(std::string root_)
+ : root(std::move(root_)) {
}
void request(const std::string& url, FileSource::Callback callback) {
@@ -57,7 +57,7 @@ private:
AssetFileSource::AssetFileSource(const std::string& root)
: thread(std::make_unique<util::Thread<Impl>>(
- util::ThreadContext{"AssetFileSource"},
+ util::ThreadContext{"AssetFileSource", util::ThreadPriority::Low},
root)) {
}
diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp
index 67e9748a70..863f42ec76 100644
--- a/platform/default/glfw_view.cpp
+++ b/platform/default/glfw_view.cpp
@@ -23,7 +23,7 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_)
: fullscreen(fullscreen_), benchmark(benchmark_) {
glfwSetErrorCallback(glfwError);
- std::srand(std::time(0));
+ std::srand(std::time(nullptr));
if (!glfwInit()) {
mbgl::Log::Error(mbgl::Event::OpenGL, "failed to initialize glfw");
@@ -55,7 +55,7 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_)
glfwWindowHint(GLFW_STENCIL_BITS, 8);
glfwWindowHint(GLFW_DEPTH_BITS, 16);
- window = glfwCreateWindow(width, height, "Mapbox GL", monitor, NULL);
+ window = glfwCreateWindow(width, height, "Mapbox GL", monitor, nullptr);
if (!window) {
glfwTerminate();
mbgl::Log::Error(mbgl::Event::OpenGL, "failed to initialize window");
@@ -103,6 +103,7 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_)
printf("\n");
printf("- Press `Q` to remove annotations\n");
printf("- Press `P` to add a random custom runtime imagery annotation\n");
+ printf("- Press `L` to add a random line annotation\n");
printf("- Press `W` to pop the last-added annotation off\n");
printf("\n");
printf("- `Control` + mouse drag to rotate\n");
@@ -164,9 +165,12 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
case GLFW_KEY_Q:
view->clearAnnotations();
break;
- case GLFW_KEY_P: {
+ case GLFW_KEY_P:
view->addRandomCustomPointAnnotations(1);
- } break;
+ break;
+ case GLFW_KEY_L:
+ view->addRandomLineAnnotations(1);
+ break;
case GLFW_KEY_A: {
// XXX Fix precision loss in flyTo:
// https://github.com/mapbox/mapbox-gl-native/issues/4298
@@ -207,6 +211,13 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
}
}
+mbgl::Color GLFWView::makeRandomColor() const {
+ const float r = 1.0f * (float(std::rand()) / RAND_MAX);
+ const float g = 1.0f * (float(std::rand()) / RAND_MAX);
+ const float b = 1.0f * (float(std::rand()) / RAND_MAX);
+ return { r, g, b, 1.0f };
+}
+
mbgl::Point<double> GLFWView::makeRandomPoint() const {
const double x = width * double(std::rand()) / RAND_MAX;
const double y = height * double(std::rand()) / RAND_MAX;
@@ -264,16 +275,26 @@ void GLFWView::addRandomCustomPointAnnotations(int count) {
}
void GLFWView::addRandomPointAnnotations(int count) {
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < count; ++i) {
annotationIDs.push_back(map->addAnnotation(mbgl::SymbolAnnotation { makeRandomPoint(), "default_marker" }));
}
}
+void GLFWView::addRandomLineAnnotations(int count) {
+ for (int i = 0; i < count; ++i) {
+ mbgl::LineString<double> lineString;
+ for (int j = 0; j < 3; ++j) {
+ lineString.push_back(makeRandomPoint());
+ }
+ annotationIDs.push_back(map->addAnnotation(mbgl::LineAnnotation { lineString, 1.0f, 2.0f, { makeRandomColor() } }));
+ }
+}
+
void GLFWView::addRandomShapeAnnotations(int count) {
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < count; ++i) {
mbgl::Polygon<double> triangle;
triangle.push_back({ makeRandomPoint(), makeRandomPoint(), makeRandomPoint() });
- annotationIDs.push_back(map->addAnnotation(mbgl::FillAnnotation { triangle, .1 }));
+ annotationIDs.push_back(map->addAnnotation(mbgl::FillAnnotation { triangle, 0.5f, { makeRandomColor() }, { makeRandomColor() } }));
}
}
diff --git a/platform/default/headless_view_glx.cpp b/platform/default/headless_view_glx.cpp
index 3b719ab43a..55d9313f99 100644
--- a/platform/default/headless_view_glx.cpp
+++ b/platform/default/headless_view_glx.cpp
@@ -23,12 +23,12 @@ void HeadlessView::createContext() {
if (!glXIsDirect(xDisplay, glContext)) {
Log::Error(Event::OpenGL, "failed to create direct OpenGL Legacy context");
glXDestroyContext(xDisplay, glContext);
- glContext = 0;
+ glContext = nullptr;
}
}
}
- if (glContext == 0) {
+ if (glContext == nullptr) {
throw std::runtime_error("Error creating GL context object.");
}
diff --git a/platform/default/http_file_source.cpp b/platform/default/http_file_source.cpp
index e83ecfbfc9..17e06a6c0f 100644
--- a/platform/default/http_file_source.cpp
+++ b/platform/default/http_file_source.cpp
@@ -63,8 +63,8 @@ public:
class HTTPRequest : public AsyncRequest {
public:
- HTTPRequest(HTTPFileSource::Impl*, const Resource&, FileSource::Callback);
- ~HTTPRequest();
+ HTTPRequest(HTTPFileSource::Impl*, Resource, FileSource::Callback);
+ ~HTTPRequest() override;
void handleResult(CURLcode code);
@@ -219,10 +219,10 @@ int HTTPFileSource::Impl::startTimeout(CURLM * /* multi */, long timeout_ms, voi
return 0;
}
-HTTPRequest::HTTPRequest(HTTPFileSource::Impl* context_, const Resource& resource_, FileSource::Callback callback_)
+HTTPRequest::HTTPRequest(HTTPFileSource::Impl* context_, Resource resource_, FileSource::Callback callback_)
: context(context_),
- resource(resource_),
- callback(callback_),
+ resource(std::move(resource_)),
+ callback(std::move(callback_)),
handle(context->getHandle()) {
// If there's already a response, set the correct etags/modified headers to make sure we are
diff --git a/platform/default/image.cpp b/platform/default/image.cpp
index 988d2f4a4e..890d442683 100644
--- a/platform/default/image.cpp
+++ b/platform/default/image.cpp
@@ -5,7 +5,7 @@
#include <png.h>
template<size_t max, typename... Args>
-inline static std::string sprintf(const char *msg, Args... args) {
+static std::string sprintf(const char *msg, Args... args) {
char res[max];
int len = snprintf(res, sizeof(res), msg, args...);
return std::string(res, len);
@@ -30,15 +30,15 @@ std::string encodePNG(const PremultipliedImage& pre) {
UnassociatedImage src = util::unpremultiply(std::move(copy));
- png_voidp error_ptr = 0;
- png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr, NULL, NULL);
+ png_voidp error_ptr = nullptr;
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr, nullptr, nullptr);
if (!png_ptr) {
throw std::runtime_error("couldn't create png_ptr");
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!png_ptr) {
- png_destroy_write_struct(&png_ptr, (png_infopp)0);
+ png_destroy_write_struct(&png_ptr, (png_infopp)nullptr);
throw std::runtime_error("couldn't create info_ptr");
}
@@ -55,7 +55,7 @@ std::string encodePNG(const PremultipliedImage& pre) {
png_set_write_fn(png_ptr, &result, [](png_structp png_ptr_, png_bytep data, png_size_t length) {
std::string *out = static_cast<std::string *>(png_get_io_ptr(png_ptr_));
out->append(reinterpret_cast<char *>(data), length);
- }, NULL);
+ }, nullptr);
struct ptrs {
ptrs(size_t count) : rows(new png_bytep[count]) {}
@@ -68,7 +68,7 @@ std::string encodePNG(const PremultipliedImage& pre) {
}
png_set_rows(png_ptr, info_ptr, pointers.rows);
- png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
return result;
@@ -112,4 +112,4 @@ PremultipliedImage decodeImage(const std::string& string) {
throw std::runtime_error("unsupported image type");
}
-}
+} // namespace mbgl
diff --git a/platform/default/jpeg_reader.cpp b/platform/default/jpeg_reader.cpp
index b9518c71bc..9bcf3c6bc1 100644
--- a/platform/default/jpeg_reader.cpp
+++ b/platform/default/jpeg_reader.cpp
@@ -1,14 +1,9 @@
#include <mbgl/util/image.hpp>
+#include <mbgl/util/char_array_buffer.hpp>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunknown-pragmas"
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#pragma GCC diagnostic ignored "-Wshadow"
-#include <boost/iostreams/stream.hpp>
-#pragma GCC diagnostic pop
-
-#include <boost/iostreams/device/file.hpp>
-#include <boost/iostreams/device/array.hpp>
+#include <istream>
+#include <sstream>
+#include <array>
extern "C"
{
@@ -17,27 +12,24 @@ extern "C"
namespace mbgl {
-using source_type = boost::iostreams::array_source;
-using input_stream = boost::iostreams::stream<source_type>;
-
const static unsigned BUF_SIZE = 4096;
struct jpeg_stream_wrapper {
jpeg_source_mgr manager;
- input_stream * stream;
- JOCTET buffer[BUF_SIZE];
+ std::istream* stream;
+ std::array<JOCTET, BUF_SIZE> buffer;
};
static void init_source(j_decompress_ptr cinfo) {
jpeg_stream_wrapper* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
- wrap->stream->seekg(0,std::ios_base::beg);
+ wrap->stream->seekg(0, std::ios_base::beg);
}
static boolean fill_input_buffer(j_decompress_ptr cinfo) {
jpeg_stream_wrapper* wrap = reinterpret_cast<jpeg_stream_wrapper*>(cinfo->src);
- wrap->stream->read(reinterpret_cast<char*>(&wrap->buffer[0]),BUF_SIZE);
+ wrap->stream->read(reinterpret_cast<char*>(&wrap->buffer[0]), BUF_SIZE);
std::streamsize size = wrap->stream->gcount();
- wrap->manager.next_input_byte = wrap->buffer;
+ wrap->manager.next_input_byte = wrap->buffer.data();
wrap->manager.bytes_in_buffer = BUF_SIZE;
return (size > 0) ? TRUE : FALSE;
}
@@ -55,15 +47,15 @@ static void skip(j_decompress_ptr cinfo, long count) {
{
wrap->stream->seekg(count - wrap->manager.bytes_in_buffer, std::ios_base::cur);
// trigger buffer fill
- wrap->manager.next_input_byte = 0;
+ wrap->manager.next_input_byte = nullptr;
wrap->manager.bytes_in_buffer = 0; //bytes_in_buffer may be zero on return.
}
}
static void term(j_decompress_ptr) {}
-static void attach_stream(j_decompress_ptr cinfo, input_stream* in) {
- if (cinfo->src == 0) {
+static void attach_stream(j_decompress_ptr cinfo, std::istream* in) {
+ if (cinfo->src == nullptr) {
cinfo->src = (struct jpeg_source_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(jpeg_stream_wrapper));
}
@@ -74,7 +66,7 @@ static void attach_stream(j_decompress_ptr cinfo, input_stream* in) {
src->manager.resync_to_restart = jpeg_resync_to_restart;
src->manager.term_source = term;
src->manager.bytes_in_buffer = 0;
- src->manager.next_input_byte = 0;
+ src->manager.next_input_byte = nullptr;
src->stream = in;
}
@@ -98,8 +90,8 @@ struct jpeg_info_guard {
};
PremultipliedImage decodeJPEG(const uint8_t* data, size_t size) {
- source_type source(reinterpret_cast<const char*>(data), size);
- input_stream stream(source);
+ util::CharArrayBuffer dataBuffer { reinterpret_cast<const char*>(data), size };
+ std::istream stream(&dataBuffer);
jpeg_decompress_struct cinfo;
jpeg_info_guard iguard(&cinfo);
@@ -156,4 +148,4 @@ PremultipliedImage decodeJPEG(const uint8_t* data, size_t size) {
return image;
}
-}
+} // namespace mbgl
diff --git a/platform/default/log_stderr.cpp b/platform/default/log_stderr.cpp
index 536841617a..145cdeda03 100644
--- a/platform/default/log_stderr.cpp
+++ b/platform/default/log_stderr.cpp
@@ -1,11 +1,12 @@
#include <mbgl/platform/log.hpp>
+#include <mbgl/util/enum.hpp>
#include <iostream>
namespace mbgl {
void Log::platformRecord(EventSeverity severity, const std::string &msg) {
- std::cerr << "[" << severity << "] " << msg << std::endl;
+ std::cerr << "[" << Enum<EventSeverity>::toString(severity) << "] " << msg << std::endl;
}
} // namespace mbgl
diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp
index d8e0357ae2..fd2d47819b 100644
--- a/platform/default/mbgl/storage/offline.cpp
+++ b/platform/default/mbgl/storage/offline.cpp
@@ -11,9 +11,9 @@
namespace mbgl {
OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
- const std::string& styleURL_, const LatLngBounds& bounds_, double minZoom_, double maxZoom_, float pixelRatio_)
- : styleURL(styleURL_),
- bounds(bounds_),
+ std::string styleURL_, LatLngBounds bounds_, double minZoom_, double maxZoom_, float pixelRatio_)
+ : styleURL(std::move(styleURL_)),
+ bounds(std::move(bounds_)),
minZoom(minZoom_),
maxZoom(maxZoom_),
pixelRatio(pixelRatio_) {
@@ -23,9 +23,9 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
}
}
-std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const Tileset& tileset) const {
- double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), tileset.minZoom);
- double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), tileset.maxZoom);
+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);
@@ -97,11 +97,11 @@ std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region)
}
OfflineRegion::OfflineRegion(int64_t id_,
- const OfflineRegionDefinition& definition_,
- const OfflineRegionMetadata& metadata_)
+ OfflineRegionDefinition definition_,
+ OfflineRegionMetadata metadata_)
: id(id_),
- definition(definition_),
- metadata(metadata_) {
+ definition(std::move(definition_)),
+ metadata(std::move(metadata_)) {
}
OfflineRegion::OfflineRegion(OfflineRegion&&) = default;
diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp
index 3193909294..46a2c2bc25 100644
--- a/platform/default/mbgl/storage/offline_database.cpp
+++ b/platform/default/mbgl/storage/offline_database.cpp
@@ -11,15 +11,13 @@
namespace mbgl {
-using namespace mapbox::sqlite;
-
OfflineDatabase::Statement::~Statement() {
stmt.reset();
stmt.clearBindings();
}
-OfflineDatabase::OfflineDatabase(const std::string& path_, uint64_t maximumCacheSize_)
- : path(path_),
+OfflineDatabase::OfflineDatabase(std::string path_, uint64_t maximumCacheSize_)
+ : path(std::move(path_)),
maximumCacheSize(maximumCacheSize_) {
ensureSchema();
}
@@ -36,7 +34,7 @@ OfflineDatabase::~OfflineDatabase() {
}
void OfflineDatabase::connect(int flags) {
- db = std::make_unique<Database>(path.c_str(), flags);
+ db = std::make_unique<mapbox::sqlite::Database>(path.c_str(), flags);
db->setBusyTimeout(Milliseconds::max());
db->exec("PRAGMA foreign_keys = ON");
}
@@ -44,7 +42,7 @@ void OfflineDatabase::connect(int flags) {
void OfflineDatabase::ensureSchema() {
if (path != ":memory:") {
try {
- connect(ReadWrite);
+ connect(mapbox::sqlite::ReadWrite);
switch (userVersion()) {
case 0: break; // cache-only database; ok to delete
@@ -55,7 +53,7 @@ void OfflineDatabase::ensureSchema() {
}
removeExisting();
- connect(ReadWrite | Create);
+ connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create);
} catch (mapbox::sqlite::Exception& ex) {
if (ex.code != SQLITE_CANTOPEN && ex.code != SQLITE_NOTADB) {
Log::Error(Event::Database, "Unexpected error connecting to database: %s", ex.what());
@@ -66,7 +64,7 @@ void OfflineDatabase::ensureSchema() {
if (ex.code == SQLITE_NOTADB) {
removeExisting();
}
- connect(ReadWrite | Create);
+ connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create);
} catch (...) {
Log::Error(Event::Database, "Unexpected error creating database: %s", util::toString(std::current_exception()).c_str());
throw;
@@ -77,7 +75,7 @@ void OfflineDatabase::ensureSchema() {
try {
#include "offline_schema.cpp.include"
- connect(ReadWrite | Create);
+ connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create);
// If you change the schema you must write a migration from the previous version.
db->exec("PRAGMA auto_vacuum = INCREMENTAL");
@@ -178,18 +176,22 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource,
}
optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resource& resource) {
+ // clang-format off
Statement accessedStmt = getStatement(
"UPDATE resources SET accessed = ?1 WHERE url = ?2");
+ // clang-format on
accessedStmt->bind(1, util::now());
accessedStmt->bind(2, resource.url);
accessedStmt->run();
+ // clang-format off
Statement stmt = getStatement(
// 0 1 2 3 4
"SELECT etag, expires, modified, data, compressed "
"FROM resources "
"WHERE url = ?");
+ // clang-format on
stmt->bind(1, resource.url);
@@ -223,11 +225,13 @@ bool OfflineDatabase::putResource(const Resource& resource,
const std::string& data,
bool compressed) {
if (response.notModified) {
+ // clang-format off
Statement update = getStatement(
"UPDATE resources "
"SET accessed = ?1, "
" expires = ?2 "
"WHERE url = ?3 ");
+ // clang-format on
update->bind(1, util::now());
update->bind(2, response.expires);
@@ -240,8 +244,9 @@ bool OfflineDatabase::putResource(const Resource& resource,
// Begin an immediate-mode transaction to ensure that two writers do not attempt
// to INSERT a resource at the same moment.
- Transaction transaction(*db, Transaction::Immediate);
+ mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate);
+ // clang-format off
Statement update = getStatement(
"UPDATE resources "
"SET kind = ?1, "
@@ -252,6 +257,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
" data = ?6, "
" compressed = ?7 "
"WHERE url = ?8 ");
+ // clang-format on
update->bind(1, int(resource.kind));
update->bind(2, response.etag);
@@ -274,9 +280,11 @@ bool OfflineDatabase::putResource(const Resource& resource,
return false;
}
+ // 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) ");
+ // clang-format on
insert->bind(1, resource.url);
insert->bind(2, int(resource.kind));
@@ -300,6 +308,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
}
optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource::TileData& tile) {
+ // clang-format off
Statement accessedStmt = getStatement(
"UPDATE tiles "
"SET accessed = ?1 "
@@ -308,6 +317,7 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
" AND x = ?4 "
" AND y = ?5 "
" AND z = ?6 ");
+ // clang-format on
accessedStmt->bind(1, util::now());
accessedStmt->bind(2, tile.urlTemplate);
@@ -317,6 +327,7 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
accessedStmt->bind(6, tile.z);
accessedStmt->run();
+ // clang-format off
Statement stmt = getStatement(
// 0 1 2 3 4
"SELECT etag, expires, modified, data, compressed "
@@ -326,6 +337,7 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
" AND x = ?3 "
" AND y = ?4 "
" AND z = ?5 ");
+ // clang-format on
stmt->bind(1, tile.urlTemplate);
stmt->bind(2, tile.pixelRatio);
@@ -363,6 +375,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
const std::string& data,
bool compressed) {
if (response.notModified) {
+ // clang-format off
Statement update = getStatement(
"UPDATE tiles "
"SET accessed = ?1, "
@@ -372,6 +385,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
" AND x = ?5 "
" AND y = ?6 "
" AND z = ?7 ");
+ // clang-format on
update->bind(1, util::now());
update->bind(2, response.expires);
@@ -388,8 +402,9 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// Begin an immediate-mode transaction to ensure that two writers do not attempt
// to INSERT a resource at the same moment.
- Transaction transaction(*db, Transaction::Immediate);
+ mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate);
+ // clang-format off
Statement update = getStatement(
"UPDATE tiles "
"SET modified = ?1, "
@@ -403,6 +418,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
" AND x = ?9 "
" AND y = ?10 "
" AND z = ?11 ");
+ // clang-format on
update->bind(1, response.modified);
update->bind(2, response.etag);
@@ -428,9 +444,11 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
return false;
}
+ // 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) ");
+ // clang-format on
insert->bind(1, tile.urlTemplate);
insert->bind(2, tile.pixelRatio);
@@ -457,8 +475,10 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
}
std::vector<OfflineRegion> OfflineDatabase::listRegions() {
+ // clang-format off
Statement stmt = getStatement(
"SELECT id, definition, description FROM regions");
+ // clang-format on
std::vector<OfflineRegion> result;
@@ -474,9 +494,11 @@ std::vector<OfflineRegion> OfflineDatabase::listRegions() {
OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& definition,
const OfflineRegionMetadata& metadata) {
+ // clang-format off
Statement stmt = getStatement(
"INSERT INTO regions (definition, description) "
"VALUES (?1, ?2) ");
+ // clang-format on
stmt->bind(1, encodeOfflineRegionDefinition(definition));
stmt->bindBlob(2, metadata);
@@ -486,8 +508,10 @@ OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& defin
}
void OfflineDatabase::deleteRegion(OfflineRegion&& region) {
+ // clang-format off
Statement stmt = getStatement(
"DELETE FROM regions WHERE id = ?");
+ // clang-format on
stmt->bind(1, region.getID());
stmt->run();
@@ -525,6 +549,7 @@ uint64_t OfflineDatabase::putRegionResource(int64_t regionID, const Resource& re
bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
if (resource.kind == Resource::Kind::Tile) {
+ // clang-format off
Statement insert = getStatement(
"INSERT OR IGNORE INTO region_tiles (region_id, tile_id) "
"SELECT ?1, tiles.id "
@@ -534,6 +559,7 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
" AND x = ?4 "
" AND y = ?5 "
" AND z = ?6 ");
+ // clang-format on
const Resource::TileData& tile = *resource.tileData;
insert->bind(1, regionID);
@@ -548,6 +574,7 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
return false;
}
+ // clang-format off
Statement select = getStatement(
"SELECT region_id "
"FROM region_tiles, tiles "
@@ -558,6 +585,7 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
" AND y = ?5 "
" AND z = ?6 "
"LIMIT 1 ");
+ // clang-format on
select->bind(1, regionID);
select->bind(2, tile.urlTemplate);
@@ -567,11 +595,13 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
select->bind(6, tile.z);
return !select->run();
} else {
+ // clang-format off
Statement insert = getStatement(
"INSERT OR IGNORE INTO region_resources (region_id, resource_id) "
"SELECT ?1, resources.id "
"FROM resources "
"WHERE resources.url = ?2 ");
+ // clang-format on
insert->bind(1, regionID);
insert->bind(2, resource.url);
@@ -581,12 +611,14 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
return false;
}
+ // clang-format off
Statement select = getStatement(
"SELECT region_id "
"FROM region_resources, resources "
"WHERE region_id != ?1 "
" AND resources.url = ?2 "
"LIMIT 1 ");
+ // clang-format on
select->bind(1, regionID);
select->bind(2, resource.url);
@@ -595,8 +627,10 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
}
OfflineRegionDefinition OfflineDatabase::getRegionDefinition(int64_t regionID) {
+ // clang-format off
Statement stmt = getStatement(
"SELECT definition FROM regions WHERE id = ?1");
+ // clang-format on
stmt->bind(1, regionID);
stmt->run();
@@ -619,22 +653,26 @@ OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID)
}
std::pair<int64_t, int64_t> OfflineDatabase::getCompletedResourceCountAndSize(int64_t regionID) {
+ // clang-format off
Statement stmt = getStatement(
"SELECT COUNT(*), SUM(LENGTH(data)) "
"FROM region_resources, resources "
"WHERE region_id = ?1 "
"AND resource_id = resources.id ");
+ // clang-format on
stmt->bind(1, regionID);
stmt->run();
return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) };
}
std::pair<int64_t, int64_t> OfflineDatabase::getCompletedTileCountAndSize(int64_t regionID) {
+ // clang-format off
Statement stmt = getStatement(
"SELECT COUNT(*), SUM(LENGTH(data)) "
"FROM region_tiles, tiles "
"WHERE region_id = ?1 "
"AND tile_id = tiles.id ");
+ // clang-format on
stmt->bind(1, regionID);
stmt->run();
return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) };
@@ -668,6 +706,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) {
// The addition of pageSize is a fudge factor to account for non `data` column
// size, and because pages can get fragmented on the database.
while (usedSize() + neededFreeSize + pageSize > maximumCacheSize) {
+ // clang-format off
Statement stmt1 = getStatement(
"DELETE FROM resources "
"WHERE id IN ( "
@@ -677,10 +716,12 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) {
" WHERE resource_id IS NULL "
" ORDER BY accessed ASC LIMIT ?1 "
") ");
+ // clang-format on
stmt1->bind(1, 50);
stmt1->run();
uint64_t changes1 = db->changes();
+ // clang-format off
Statement stmt2 = getStatement(
"DELETE FROM tiles "
"WHERE id IN ( "
@@ -690,6 +731,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) {
" WHERE tile_id IS NULL "
" ORDER BY accessed ASC LIMIT ?1 "
") ");
+ // clang-format on
stmt2->bind(1, 50);
stmt2->run();
uint64_t changes2 = db->changes();
@@ -727,11 +769,13 @@ uint64_t OfflineDatabase::getOfflineMapboxTileCount() {
return *offlineMapboxTileCount;
}
+ // clang-format off
Statement stmt = getStatement(
"SELECT COUNT(DISTINCT id) "
"FROM region_tiles, tiles "
"WHERE tile_id = tiles.id "
"AND url_template LIKE 'mapbox://%' ");
+ // clang-format on
stmt->run();
diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp
index 1706c6ba5a..55cb7ad2fa 100644
--- a/platform/default/mbgl/storage/offline_database.hpp
+++ b/platform/default/mbgl/storage/offline_database.hpp
@@ -15,8 +15,8 @@ namespace mapbox {
namespace sqlite {
class Database;
class Statement;
-}
-}
+} // namespace sqlite
+} // namespace mapbox
namespace mbgl {
@@ -27,8 +27,7 @@ class OfflineDatabase : private util::noncopyable {
public:
// Limits affect ambient caching (put) only; resources required by offline
// regions are exempt.
- OfflineDatabase(const std::string& path,
- uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE);
+ OfflineDatabase(std::string path, uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE);
~OfflineDatabase();
optional<Response> get(const Resource&);
diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp
index 11ca862925..dd66abf982 100644
--- a/platform/default/mbgl/storage/offline_download.cpp
+++ b/platform/default/mbgl/storage/offline_download.cpp
@@ -1,13 +1,16 @@
-#include <mbgl/storage/offline_download.hpp>
-#include <mbgl/storage/offline_database.hpp>
#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/offline_database.hpp>
+#include <mbgl/storage/offline_download.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/style/parser.hpp>
+#include <mbgl/style/sources/geojson_source_impl.hpp>
+#include <mbgl/style/tile_source_impl.hpp>
#include <mbgl/text/glyph.hpp>
-#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/util/tileset.hpp>
#include <set>
@@ -63,7 +66,8 @@ std::vector<Resource> OfflineDownload::glyphResources(const style::Parser& parse
if (!parser.glyphURL.empty()) {
for (const auto& fontStack : parser.fontStacks()) {
for (uint32_t i = 0; i < 256; i++) {
- result.push_back(Resource::glyphs(parser.glyphURL, fontStack, getGlyphRange(i * 256)));
+ result.push_back(
+ Resource::glyphs(parser.glyphURL, fontStack, getGlyphRange(i * 256)));
}
}
}
@@ -71,11 +75,13 @@ std::vector<Resource> OfflineDownload::glyphResources(const style::Parser& parse
return result;
}
-std::vector<Resource> OfflineDownload::tileResources(SourceType type, uint16_t tileSize, const Tileset& tileset) const {
+std::vector<Resource>
+OfflineDownload::tileResources(SourceType type, uint16_t tileSize, const Tileset& tileset) const {
std::vector<Resource> result;
- for (const auto& tile : definition.tileCover(type, tileSize, tileset)) {
- result.push_back(Resource::tile(tileset.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z));
+ for (const auto& tile : definition.tileCover(type, tileSize, tileset.zoomRange)) {
+ result.push_back(
+ Resource::tile(tileset.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z));
}
return result;
@@ -100,28 +106,45 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
result.requiredResourceCountIsPrecise = true;
for (const auto& source : parser.sources) {
- switch (source->type) {
+ switch (source->baseImpl->type) {
case SourceType::Vector:
- case SourceType::Raster:
- if (source->getTileset()) {
- result.requiredResourceCount += tileResources(source->type, source->tileSize, *source->getTileset()).size();
+ case SourceType::Raster: {
+ style::TileSourceImpl* tileSource =
+ static_cast<style::TileSourceImpl*>(source->baseImpl.get());
+ const variant<std::string, Tileset>& urlOrTileset = tileSource->getURLOrTileset();
+
+ if (urlOrTileset.is<Tileset>()) {
+ result.requiredResourceCount +=
+ tileResources(source->baseImpl->type, tileSource->getTileSize(),
+ urlOrTileset.get<Tileset>())
+ .size();
} else {
result.requiredResourceCount += 1;
- optional<Response> sourceResponse = offlineDatabase.get(Resource::source(source->url));
+ const std::string& url = urlOrTileset.get<std::string>();
+ optional<Response> sourceResponse = offlineDatabase.get(Resource::source(url));
if (sourceResponse) {
- result.requiredResourceCount += tileResources(source->type, source->tileSize,
- *style::parseTileJSON(*sourceResponse->data, source->url, source->type, source->tileSize)).size();
+ result.requiredResourceCount +=
+ tileResources(source->baseImpl->type, tileSource->getTileSize(),
+ style::TileSourceImpl::parseTileJSON(
+ *sourceResponse->data, url, source->baseImpl->type,
+ tileSource->getTileSize()))
+ .size();
} else {
result.requiredResourceCountIsPrecise = false;
}
}
break;
+ }
+
+ case SourceType::GeoJSON: {
+ style::GeoJSONSource::Impl* geojsonSource =
+ static_cast<style::GeoJSONSource::Impl*>(source->baseImpl.get());
- case SourceType::GeoJSON:
- if (!source->url.empty()) {
+ if (!geojsonSource->loaded) {
result.requiredResourceCount += 1;
}
break;
+ }
case SourceType::Video:
case SourceType::Annotations:
@@ -141,28 +164,33 @@ void OfflineDownload::activateDownload() {
requiredSourceURLs.clear();
- ensureResource(Resource::style(definition.styleURL), [&] (Response styleResponse) {
+ ensureResource(Resource::style(definition.styleURL), [&](Response styleResponse) {
status.requiredResourceCountIsPrecise = true;
style::Parser parser;
parser.parse(*styleResponse.data);
for (const auto& source : parser.sources) {
- SourceType type = source->type;
- uint16_t tileSize = source->tileSize;
- std::string url = source->url;
+ SourceType type = source->baseImpl->type;
switch (type) {
case SourceType::Vector:
- case SourceType::Raster:
- if (source->getTileset()) {
- ensureTiles(type, tileSize, *source->getTileset());
+ case SourceType::Raster: {
+ const style::TileSourceImpl* tileSource =
+ static_cast<style::TileSourceImpl*>(source->baseImpl.get());
+ const variant<std::string, Tileset>& urlOrTileset = tileSource->getURLOrTileset();
+ const uint16_t tileSize = tileSource->getTileSize();
+
+ if (urlOrTileset.is<Tileset>()) {
+ ensureTiles(type, tileSize, urlOrTileset.get<Tileset>());
} else {
+ const std::string& url = urlOrTileset.get<std::string>();
status.requiredResourceCountIsPrecise = false;
requiredSourceURLs.insert(url);
- ensureResource(Resource::source(url), [=] (Response sourceResponse) {
- ensureTiles(type, tileSize, *style::parseTileJSON(*sourceResponse.data, url, type, tileSize));
+ ensureResource(Resource::source(url), [=](Response sourceResponse) {
+ ensureTiles(type, tileSize, style::TileSourceImpl::parseTileJSON(
+ *sourceResponse.data, url, type, tileSize));
requiredSourceURLs.erase(url);
if (requiredSourceURLs.empty()) {
@@ -171,19 +199,24 @@ void OfflineDownload::activateDownload() {
});
}
break;
+ }
- case SourceType::GeoJSON:
- if (!source->url.empty()) {
- ensureResource(Resource::source(source->url));
+ case SourceType::GeoJSON: {
+ style::GeoJSONSource::Impl* geojsonSource =
+ static_cast<style::GeoJSONSource::Impl*>(source->baseImpl.get());
+
+ if (!geojsonSource->loaded) {
+ ensureResource(Resource::source(geojsonSource->getURL()));
}
break;
+ }
case SourceType::Video:
case SourceType::Annotations:
break;
}
}
-
+
for (const auto& resource : spriteResources(parser)) {
ensureResource(resource);
}
@@ -204,14 +237,16 @@ void OfflineDownload::ensureTiles(SourceType type, uint16_t tileSize, const Tile
}
}
-void OfflineDownload::ensureResource(const Resource& resource, std::function<void (Response)> callback) {
+void OfflineDownload::ensureResource(const Resource& resource,
+ std::function<void(Response)> callback) {
status.requiredResourceCount++;
auto workRequestsIt = requests.insert(requests.begin(), nullptr);
- *workRequestsIt = util::RunLoop::Get()->invokeCancellable([=] () {
+ *workRequestsIt = util::RunLoop::Get()->invokeCancellable([=]() {
requests.erase(workRequestsIt);
- optional<std::pair<Response, uint64_t>> offlineResponse = offlineDatabase.getRegionResource(id, resource);
+ optional<std::pair<Response, uint64_t>> offlineResponse =
+ offlineDatabase.getRegionResource(id, resource);
if (offlineResponse) {
if (callback) {
callback(offlineResponse->first);
@@ -220,24 +255,25 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi
status.completedResourceCount++;
status.completedResourceSize += offlineResponse->second;
if (resource.kind == Resource::Kind::Tile) {
+ status.completedTileCount += 1;
status.completedTileSize += offlineResponse->second;
}
-
+
observer->statusChanged(status);
-
+
if (status.complete()) {
setState(OfflineRegionDownloadState::Inactive);
}
return;
}
-
+
if (checkTileCountLimit(resource)) {
return;
}
auto fileRequestsIt = requests.insert(requests.begin(), nullptr);
- *fileRequestsIt = onlineFileSource.request(resource, [=] (Response onlineResponse) {
+ *fileRequestsIt = onlineFileSource.request(resource, [=](Response onlineResponse) {
if (onlineResponse.error) {
observer->responseError(*onlineResponse.error);
return;
@@ -253,15 +289,16 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi
uint64_t resourceSize = offlineDatabase.putRegionResource(id, resource, onlineResponse);
status.completedResourceSize += resourceSize;
if (resource.kind == Resource::Kind::Tile) {
+ status.completedTileCount += 1;
status.completedTileSize += resourceSize;
}
observer->statusChanged(status);
-
+
if (checkTileCountLimit(resource)) {
return;
}
-
+
if (status.complete()) {
setState(OfflineRegionDownloadState::Inactive);
}
@@ -270,14 +307,13 @@ void OfflineDownload::ensureResource(const Resource& resource, std::function<voi
}
bool OfflineDownload::checkTileCountLimit(const Resource& resource) {
- if (resource.kind == Resource::Kind::Tile
- && util::mapbox::isMapboxURL(resource.url)
- && offlineDatabase.offlineMapboxTileCountLimitExceeded()) {
+ if (resource.kind == Resource::Kind::Tile && util::mapbox::isMapboxURL(resource.url) &&
+ offlineDatabase.offlineMapboxTileCountLimitExceeded()) {
observer->mapboxTileCountLimitExceeded(offlineDatabase.getOfflineMapboxTileCountLimit());
setState(OfflineRegionDownloadState::Inactive);
return true;
}
-
+
return false;
}
diff --git a/platform/default/mbgl/storage/offline_download.hpp b/platform/default/mbgl/storage/offline_download.hpp
index 1a0d7536d8..27c5f0b139 100644
--- a/platform/default/mbgl/storage/offline_download.hpp
+++ b/platform/default/mbgl/storage/offline_download.hpp
@@ -17,7 +17,7 @@ class Tileset;
namespace style {
class Parser;
-}
+} // namespace style
/**
* Coordinates the request and storage of all resources for an offline region.
diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp
index a4ac2c2b2b..b7d658694c 100644
--- a/platform/default/online_file_source.cpp
+++ b/platform/default/online_file_source.cpp
@@ -26,8 +26,8 @@ class OnlineFileRequest : public AsyncRequest {
public:
using Callback = std::function<void (Response)>;
- OnlineFileRequest(const Resource&, Callback, OnlineFileSource::Impl&);
- ~OnlineFileRequest();
+ OnlineFileRequest(Resource, Callback, OnlineFileSource::Impl&);
+ ~OnlineFileRequest() override;
void networkIsReachableAgain();
void schedule(optional<Timestamp> expires);
@@ -182,9 +182,9 @@ std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource
return std::make_unique<OnlineFileRequest>(res, callback, *impl);
}
-OnlineFileRequest::OnlineFileRequest(const Resource& resource_, Callback callback_, OnlineFileSource::Impl& impl_)
+OnlineFileRequest::OnlineFileRequest(Resource resource_, Callback callback_, OnlineFileSource::Impl& impl_)
: impl(impl_),
- resource(resource_),
+ resource(std::move(resource_)),
callback(std::move(callback_)) {
impl.add(this);
diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp
index 096596ee2e..0461ec61a6 100644
--- a/platform/default/png_reader.cpp
+++ b/platform/default/png_reader.cpp
@@ -1,16 +1,10 @@
#include <mbgl/util/image.hpp>
#include <mbgl/util/premultiply.hpp>
+#include <mbgl/util/char_array_buffer.hpp>
#include <mbgl/platform/log.hpp>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunknown-pragmas"
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#pragma GCC diagnostic ignored "-Wshadow"
-#include <boost/iostreams/stream.hpp>
-#pragma GCC diagnostic pop
-
-#include <boost/iostreams/device/file.hpp>
-#include <boost/iostreams/device/array.hpp>
+#include <istream>
+#include <sstream>
extern "C"
{
@@ -19,9 +13,6 @@ extern "C"
namespace mbgl {
-using source_type = boost::iostreams::array_source;
-using input_stream = boost::iostreams::stream<source_type>;
-
static void user_error_fn(png_structp, png_const_charp error_msg) {
throw std::runtime_error(std::string("failed to read invalid png: '") + error_msg + "'");
}
@@ -31,7 +22,7 @@ static void user_warning_fn(png_structp, png_const_charp warning_msg) {
}
static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
- input_stream * fin = reinterpret_cast<input_stream*>(png_get_io_ptr(png_ptr));
+ std::istream* fin = reinterpret_cast<std::istream*>(png_get_io_ptr(png_ptr));
fin->read(reinterpret_cast<char*>(data), length);
std::streamsize read_count = fin->gcount();
if (read_count < 0 || static_cast<png_size_t>(read_count) != length)
@@ -46,7 +37,7 @@ struct png_struct_guard {
i_(info_ptr_ptr) {}
~png_struct_guard() {
- png_destroy_read_struct(p_,i_,0);
+ png_destroy_read_struct(p_,i_,nullptr);
}
png_structpp p_;
@@ -54,8 +45,8 @@ struct png_struct_guard {
};
PremultipliedImage decodePNG(const uint8_t* data, size_t size) {
- source_type source(reinterpret_cast<const char*>(data), size);
- input_stream stream(source);
+ util::CharArrayBuffer dataBuffer { reinterpret_cast<const char*>(data), size };
+ std::istream stream(&dataBuffer);
png_byte header[8] = { 0 };
stream.read(reinterpret_cast<char*>(header), 8);
@@ -66,7 +57,7 @@ PremultipliedImage decodePNG(const uint8_t* data, size_t size) {
if (!is_png)
throw std::runtime_error("File or stream is not a png");
- png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png_ptr)
throw std::runtime_error("failed to allocate png_ptr");
@@ -87,7 +78,7 @@ PremultipliedImage decodePNG(const uint8_t* data, size_t size) {
png_uint_32 height = 0;
int bit_depth = 0;
int color_type = 0;
- png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr);
UnassociatedImage image { width, height };
@@ -125,9 +116,9 @@ PremultipliedImage decodePNG(const uint8_t* data, size_t size) {
rows[row] = image.data.get() + row * width * 4;
png_read_image(png_ptr, rows.get());
- png_read_end(png_ptr, 0);
+ png_read_end(png_ptr, nullptr);
return util::premultiply(std::move(image));
}
-}
+} // namespace mbgl
diff --git a/platform/default/run_loop.cpp b/platform/default/run_loop.cpp
index 6230e8f1ab..1ebbade7ab 100644
--- a/platform/default/run_loop.cpp
+++ b/platform/default/run_loop.cpp
@@ -14,9 +14,9 @@ using namespace mbgl::util;
static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
-void dummyCallback(uv_async_t*, int) {};
+void dummyCallback(uv_async_t*, int) {}
#else
-void dummyCallback(uv_async_t*) {};
+void dummyCallback(uv_async_t*) {}
#endif
} // namespace
@@ -158,7 +158,7 @@ void RunLoop::run() {
void RunLoop::runOnce() {
MBGL_VERIFY_THREAD(tid);
- uv_run(impl->loop, UV_RUN_ONCE);
+ uv_run(impl->loop, UV_RUN_NOWAIT);
}
void RunLoop::stop() {
diff --git a/platform/default/settings_json.cpp b/platform/default/settings_json.cpp
index 2c1bb3d242..ef53aa83e7 100644
--- a/platform/default/settings_json.cpp
+++ b/platform/default/settings_json.cpp
@@ -1,7 +1,7 @@
#include <mbgl/platform/default/settings_json.hpp>
#include <fstream>
-using namespace mbgl;
+namespace mbgl {
Settings_JSON::Settings_JSON() { load(); }
@@ -37,3 +37,5 @@ void Settings_JSON::clear() {
pitch = 0;
debug = 0;
}
+
+} // namespace mbgl
diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp
index 57ee18e9f3..cdc6f87380 100644
--- a/platform/default/sqlite3.hpp
+++ b/platform/default/sqlite3.hpp
@@ -22,8 +22,8 @@ enum OpenFlag : int {
};
struct Exception : std::runtime_error {
- inline Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {}
- inline Exception(int err, const std::string& msg) : std::runtime_error(msg), code(err) {}
+ Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {}
+ Exception(int err, const std::string& msg) : std::runtime_error(msg), code(err) {}
const int code = 0;
};
diff --git a/platform/default/string_stdlib.cpp b/platform/default/string_stdlib.cpp
index ac4c26234c..90a75c1738 100644
--- a/platform/default/string_stdlib.cpp
+++ b/platform/default/string_stdlib.cpp
@@ -11,17 +11,17 @@ namespace mbgl { namespace platform {
std::string uppercase(const std::string& str)
{
std::stringstream output;
- char const *itr = str.c_str(), *nitr = itr;
+ char const *itr = str.c_str(), *nitr;
char const *end = itr + str.length();
char lo[5] = { 0 };
for (; itr < end; itr = nitr)
{
uint32_t code_point = 0;
- char const* buf = 0;
+ char const* buf = nullptr;
- nitr = _nu_toupper(itr, end, nu_utf8_read, &code_point, &buf, 0);
- if (buf != 0)
+ nitr = _nu_toupper(itr, end, nu_utf8_read, &code_point, &buf, nullptr);
+ if (buf != nullptr)
{
do
{
@@ -44,17 +44,17 @@ std::string uppercase(const std::string& str)
std::string lowercase(const std::string& str)
{
std::stringstream output;
- char const *itr = str.c_str(), *nitr = itr;
+ char const *itr = str.c_str(), *nitr;
char const *end = itr + str.length();
char lo[5] = { 0 };
for (; itr < end; itr = nitr)
{
uint32_t code_point = 0;
- char const* buf = 0;
+ char const* buf = nullptr;
- nitr = _nu_tolower(itr, end, nu_utf8_read, &code_point, &buf, 0);
- if (buf != 0)
+ nitr = _nu_tolower(itr, end, nu_utf8_read, &code_point, &buf, nullptr);
+ if (buf != nullptr)
{
do
{
@@ -73,4 +73,5 @@ std::string lowercase(const std::string& str)
return output.str();
}
-}}
+} // namespace platform
+} // namespace mbgl
diff --git a/platform/default/thread.cpp b/platform/default/thread.cpp
index 3ef3257923..f4d0b9a855 100644
--- a/platform/default/thread.cpp
+++ b/platform/default/thread.cpp
@@ -1,27 +1,36 @@
#include <mbgl/platform/platform.hpp>
-
#include <mbgl/platform/log.hpp>
+#include <string>
+
#include <pthread.h>
-#ifdef __linux__
-#include <linux/sched.h>
-#endif
#include <sched.h>
namespace mbgl {
namespace platform {
+std::string getCurrentThreadName() {
+ char name[32] = "unknown";
+ pthread_getname_np(pthread_self(), name, sizeof(name));
+
+ return name;
+}
+
+void setCurrentThreadName(const std::string& name) {
+ if (name.size() > 15) { // Linux hard limit (see manpages).
+ pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
+ } else {
+ pthread_setname_np(pthread_self(), name.c_str());
+ }
+}
+
void makeThreadLowPriority() {
-#ifdef SCHED_IDLE
struct sched_param param;
param.sched_priority = 0;
- int status = sched_setscheduler(0, SCHED_IDLE, &param);
- if (status != 0) {
+
+ if (sched_setscheduler(0, SCHED_IDLE, &param) != 0) {
Log::Warning(Event::General, "Couldn't set thread scheduling policy");
}
-#else
-#pragma message("Building without thread priority control")
-#endif
}
} // namespace platform
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 1b90d20999..89f301ea37 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -2,17 +2,29 @@
Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started.
-## 3.3.3
+## master
+
+* As the user zooms in, tiles from lower zoom levels are scaled up until tiles for higher zoom levels are loaded. ([#5143](https://github.com/mapbox/mapbox-gl-native/pull/5143))
+* Added [quadkey](https://msdn.microsoft.com/en-us/library/bb259689.aspx) support and limited WMS support in raster tile URL templates. ([#5628](https://github.com/mapbox/mapbox-gl-native/pull/5628))
+* The `text-pitch-alignment` property is now supported in stylesheets for improved street label legibility on a tilted map. ([#5288](https://github.com/mapbox/mapbox-gl-native/pull/5288))
+* The `icon-text-fit` and `icon-text-fit-padding` properties are now supported in stylesheets, allowing the background of a shield to automatically resize to fit the shield’s text. ([#5334](https://github.com/mapbox/mapbox-gl-native/pull/5334))
+* Improved the performance of relocating a non-view-backed point annotation by changing its `coordinate` property. ([#5385](https://github.com/mapbox/mapbox-gl-native/pull/5385))
+* MGLMapDebugOverdrawVisualizationMask does nothing in Release builds of the SDK. This is disabled for performance reasons. ([#5555](https://github.com/mapbox/mapbox-gl-native/pull/5555))
+* Include simulator architecture slices in the dSYM file that is generated for release builds. ([#5740](https://github.com/mapbox/mapbox-gl-native/pull/5740))
+* Fixed an issue where annotation views could be assigned to multipoint annotations. ([#5770](https://github.com/mapbox/mapbox-gl-native/pull/5770))
+* Fixed the static only framework build. ([#5782](https://github.com/mapbox/mapbox-gl-native/issues/5782))
+
+## 3.3.3 - July 29, 2016
* Fixes an issue where the style zoom levels were not respected when deciding when to render a layer. ([#5811](https://github.com/mapbox/mapbox-gl-native/issues/5811))
-## 3.3.2
+## 3.3.2 - July 28, 2016
* Speculatively fixed a crash that occurred when initializing an MGLMapView on iOS 7.x. ([#5791](https://github.com/mapbox/mapbox-gl-native/pull/5791))
* View-backed annotations no longer prevent the user from starting to pan the map. ([#5813](https://github.com/mapbox/mapbox-gl-native/pull/5813))
* Fixed an issue that caused the user dot to be selected when tapping an annotation that lies within the user dot’s accuracy circle. ([#5816](https://github.com/mapbox/mapbox-gl-native/pull/5816))
-## 3.3.1
+## 3.3.1 - July 19, 2016
* Fixed a crash that occurred when a sprite URL lacks a file extension. See [this comment](https://github.com/mapbox/mapbox-gl-native/issues/5722#issuecomment-233701251) to determine who may be affected by this bug. ([#5723](https://github.com/mapbox/mapbox-gl-native/pull/5723))
* Fixed an issue causing overlapping polylines and polygons to be drawn in undefined z-order. Shapes are always drawn in the order they are added to the map, from the oldest on the bottom to the newest on the top. ([#5710](https://github.com/mapbox/mapbox-gl-native/pull/5710))
@@ -23,7 +35,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON
* The compass, Mapbox logo, and attribution button now accommodate the containing map view’s content insets. If your interface elements partially overlap the map view but do not affect the top and bottom layout guides, set the `automaticallyAdjustsScrollViewInsets` property to `NO` and set the `contentInset` property to a suitable value. ([#5671](https://github.com/mapbox/mapbox-gl-native/pull/5671))
* Added a property to MGLOfflineStorage, `countOfBytesCompleted`, that indicates the disk space occupied by all cached and offline resources. ([#5585](https://github.com/mapbox/mapbox-gl-native/pull/5585))
-## 3.3.0
+## 3.3.0 - July 14, 2016
### Styles and data
@@ -78,25 +90,25 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON
- Added NSFormatter subclasses for converting geographic coordinates and directions into display strings. ([#4802](https://github.com/mapbox/mapbox-gl-native/pull/4802))
- Added `MGLCoordinateInCoordinateBounds()`, a function that tests whether or not a coordinate is in a given bounds. ([#5053](https://github.com/mapbox/mapbox-gl-native/pull/5053))
-## 3.2.3
+## 3.2.3 - June 7, 2016
- Fixed an issue preventing `-[MGLMapViewDelegate mapViewDidFinishLoadingMap:]` from being called when returning to the view controller containing the map view from another view controller. ([#5164](https://github.com/mapbox/mapbox-gl-native/pull/5164))
- Declarations in the API documentation are shown in both Objective-C and Swift. ([realm/jazzy#530](https://github.com/realm/jazzy/pull/530))
-## 3.2.2
+## 3.2.2 - May 10, 2016
- Existing MGLStyle class methods that return default style URLs have been deprecated in favor of new methods that require an explicit style version parameter. The deprecated, unversioned methods continue to return version 8 of the respective styles and will not be updated as new versions of the styles are released. ([#4759](https://github.com/mapbox/mapbox-gl-native/pull/4759))
- Deprecated `+[MGLStyle emeraldStyleURL]` with no replacement method. To use the Emerald style going forward, we recommend that you use the underlying URL. ([#4759](https://github.com/mapbox/mapbox-gl-native/pull/4759))
- Added `+[MGLStyle outdoorsStyleURLWithVersion:]` for the new Outdoors style. ([#4759](https://github.com/mapbox/mapbox-gl-native/pull/4759))
- The Hybrid style is now called Satellite Streets. ([#4759](https://github.com/mapbox/mapbox-gl-native/pull/4759))
-## 3.2.1
+## 3.2.1 - April 20, 2016
- Fixed a hang that could occur if the host application attempts to set user defaults on a background queue. ([#4745](https://github.com/mapbox/mapbox-gl-native/pull/4745))
- User location heading updates now resume properly when an app becomes active again. ([#4674](https://github.com/mapbox/mapbox-gl-native/pull/4674))
- Fixed an issue causing hyperlinks in the documentation to be displayed as raw Markdown syntax when viewed in Xcode’s Quick Help popover or sidebar. ([#4760](https://github.com/mapbox/mapbox-gl-native/pull/4760))
-## 3.2.0
+## 3.2.0 - April 5, 2016
- If you’ve previously installed the SDK as a static framework, the installation workflow has changed to address issues when submitting your application to the App Store or installing it on a device. Upon upgrading to this version of the SDK, you’ll need to add Mapbox.bundle to the Copy Bundle Resources build phase and remove Mapbox.framework from the Embed Frameworks build phase. ([#4455](https://github.com/mapbox/mapbox-gl-native/pull/4455))
- Offline packs can now be downloaded to allow users to view specific regions of the map offline. A new MGLOfflineStorage class provides APIs for managing MGLOfflinePacks. ([#4221](https://github.com/mapbox/mapbox-gl-native/pull/4221))
@@ -117,12 +129,12 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON
- The Improve This Map tool now uses the same zoom level that is currently being shown in the map view. ([#4068](https://github.com/mapbox/mapbox-gl-native/pull/4068))
- Fixed a formatting issue in the documentation for `MGLCoordinateBoundsIsEmpty()`. ([#3958](https://github.com/mapbox/mapbox-gl-native/pull/3958))
-## 3.1.2
+## 3.1.2 - February 22, 2016
- You can once again install the static framework without manually linking several framework and library dependencies. ([#4029](https://github.com/mapbox/mapbox-gl-native/pull/4029))
- The location manager used by MGLMapView to show the user’s location is now paused when the application is sent to the background. ([#4034](https://github.com/mapbox/mapbox-gl-native/pull/4034))
-## 3.1.1
+## 3.1.1 - February 15, 2016
- Corrected the dynamic framework’s minimum deployment target to iOS 8.0. ([#3872](https://github.com/mapbox/mapbox-gl-native/pull/3872))
- Fixed Fabric compatibility. ([#3847](https://github.com/mapbox/mapbox-gl-native/pull/3847))
@@ -131,7 +143,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON
- Fixed an issue that incorrectly expanded the tappable area of an annotation and prevented the annotation’s alignment rect insets from having any effect on the tappable area. ([#3898](https://github.com/mapbox/mapbox-gl-native/pull/3898))
- Fixed an issue preventing `-[MGLMapViewDelegate mapView:tapOnCalloutForAnnotation:]` from being called when a non-custom callout view is tapped. ([#3875](https://github.com/mapbox/mapbox-gl-native/pull/3875))
-## 3.1.0
+## 3.1.0 - February 5, 2016
- The SDK is now distributed as a dynamic framework instead of a static library, resulting in a simpler installation workflow and significantly reduced download size. The framework contains both simulator and device content. If you install the dynamic framework manually, you’ll need to strip out the simulator content before submitting your application to the App Store due to [an Xcode bug](http://www.openradar.me/radar?id=6409498411401216); see the installation instructions included with the framework for details. ([#3183](https://github.com/mapbox/mapbox-gl-native/pull/3183))
- Fixed an issue causing the entire MGLMapView to leak. ([#3448](https://github.com/mapbox/mapbox-gl-native/pull/3448))
@@ -160,13 +172,13 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON
- Avoids triggering the blue background location status bar when user has granted "when in use" permission. ([#3671](https://github.com/mapbox/mapbox-gl-native/issues/3671))
- Deprecated the `debugActive` property and `-toggleDebug` method on MGLMapView in favor of a new `debugMask` property that exposes individual style debugging options. ([#3742](https://github.com/mapbox/mapbox-gl-native/pull/3742))
-## 3.0.1
+## 3.0.1 - December 7, 2015
- Fixed CoreTelephony.framework crash. ([#3170](https://github.com/mapbox/mapbox-gl-native/pull/3170))
- Fixed an issue preventing the compass from responding to taps after the compass is moved programmatically. ([#3117](https://github.com/mapbox/mapbox-gl-native/pull/3117))
- CocoaPods is now distributed via a (static) framework. ([#3181](https://github.com/mapbox/mapbox-gl-native/issues/3181))
-## 3.0.0
+## 3.0.0 - November 23, 2015
- If you install this SDK via CocoaPods, CocoaPods version 0.38.0 or above is required. ([#2132](https://github.com/mapbox/mapbox-gl-native/pull/2132))
- The `styleID` property has been removed from MGLMapView. Instead, set the `styleURL` property to an NSURL in the form `mapbox://styles/STYLE_ID`. If you previously set the style ID in Interface Builder’s Attributes inspector, delete the `styleID` entry from the User Defined Runtime Attributes section of the Identity inspector, then set the new “Style URL” inspectable to a value in the form `mapbox://styles/STYLE_ID`. ([#2632](https://github.com/mapbox/mapbox-gl-native/pull/2632))
@@ -191,15 +203,15 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON
- Removed CoreTelephony.framework dependency. ([#2581](https://github.com/mapbox/mapbox-gl-native/pull/2581))
- Improved user location annotation responsiveness. ([#2643](https://github.com/mapbox/mapbox-gl-native/pull/2643))
-## 2.1.2
+## 2.1.2 - September 15, 2015
- Built with Xcode 6.4 to not yet trigger Bitcode compatibility until Xcode 7 stabilizes. ([#2332](https://github.com/mapbox/mapbox-gl-native/issues/2332))
-## 2.1.1
+## 2.1.1 - September 15, 2015
- Fixes for Xcode 7 and Bitcode. ([#2238](https://github.com/mapbox/mapbox-gl-native/pull/2238))
-## 2.1.0
+## 2.1.0 - September 14, 2015
- A two-finger vertical swipe now tilts the map into perspective mode. ([#2116](https://github.com/mapbox/mapbox-gl-native/pull/2116))
- A new `MGLMapCamera` API allows you to transition multiple viewpoint properties, including rotation and pitch, simultaneously with an optional custom duration and timing function. ([#2193](https://github.com/mapbox/mapbox-gl-native/pull/2193))
@@ -218,21 +230,21 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON
- Minor style updates. ([#1910](https://github.com/mapbox/mapbox-gl-native/pull/1910))
- The CocoaPods pod now contains a `README.md` file. ([#1886](https://github.com/mapbox/mapbox-gl-native/pull/1886))
-## 2.0.0
+## 2.0.0 - August 21, 2015
Repackaging 2.0.0-pre.1 as it contained no issues.
-## 2.0.0-pre.1
+## 2.0.0-pre.1 - August 21, 2015
Repackaging 0.5.1 as the Mapbox iOS SDK 2.0.0 series.
-## 0.5.1
+## 0.5.1 - July 13, 2015
### iOS
- Added support for CocoaPods 0.38.0. ([#1876](https://github.com/mapbox/mapbox-gl-native/pull/1876))
-## 0.5.0
+## 0.5.0 - July 9, 2015
### Core
@@ -261,7 +273,7 @@ Repackaging 0.5.1 as the Mapbox iOS SDK 2.0.0 series.
- Fixed an issue in which `-[MGLMapView direction]` would sometimes return 360 instead of 0. ([#1829](https://github.com/mapbox/mapbox-gl-native/pull/1829))
- Build against iOS 8.4. ([#1868](https://github.com/mapbox/mapbox-gl-native/pull/1868))
-## 0.4.0
+## 0.4.0 - June 19, 2015
### Core
@@ -285,11 +297,11 @@ Repackaging 0.5.1 as the Mapbox iOS SDK 2.0.0 series.
- Internal use of the Reachability library has been cleaned up so that your app can include its own copy of Reachability. ([#1718](https://github.com/mapbox/mapbox-gl-native/issues/1718))
- Now distribute a binary stripped of debugging symbols by default with an optional, secondary symbols build. ([#1650](https://github.com/mapbox/mapbox-gl-native/issues/1650))
-## 0.3.1
+## 0.3.1 - May 15, 2015
- Temporarily removed `IBDesignable` support on iOS.
-## 0.3.0
+## 0.3.0 - May 14, 2015
- Initial iOS beta release.
diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md
index 4efc6af480..d78518eddd 100644
--- a/platform/ios/DEVELOPING.md
+++ b/platform/ios/DEVELOPING.md
@@ -63,6 +63,21 @@ The products of these build commands can be found in the `build/ios/pkg` folder
## Contributing
+### Making any symbol public
+
+To add any Objective-C type, constant, or member to the iOS SDK’s public interface:
+
+1. Ensure that the symbol is pure Objective-C and does not rely on any language features specific to Objective-C++ or the C11 dialect of C – so no namespaced types or classes named with emoji! 🙃 Most projects that depend on this SDK are either written in pure Objective-C (GNU99 dialect) or Swift, which cannot yet bridge C++ types.
+1. Name the symbol according to [Cocoa naming conventions](https://developer.apple.com/library/prerelease/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html#//apple_ref/doc/uid/10000146i). Use the `MGL` class prefix to avoid conflicts with client code. If the symbol has an analogue in MapKit, name the symbol according to MapKit.
+1. Provide full documentation comments. We use [jazzy](https://github.com/realm/jazzy/) to produce the documentation found in the SDK distribution and [on the Mapbox iOS SDK website](https://www.mapbox.com/ios-sdk/api/). We also recognize that many developers rely on Xcode’s Quick Help feature. jazzy supports Markdown formatting; however, Quick Help supports only [HeaderDoc](https://developer.apple.com/legacy/library/documentation/DeveloperTools/Conceptual/HeaderDoc/intro/intro.html) syntax and a subset of Doxygen syntax. For hyperlinks, use HTML syntax, which is recognized by both tools.
+
+### Making a type or constant public
+
+To add an Objective-C class, protocol, category, typedef, enumeration, or global constant to the iOS SDK’s public interface:
+
+1. _(Optional.)_ Add the type or constant’s name to the relevant category in the `custom_categories` section of [the jazzy configuration file](./jazzy.yml). This is required for classes and protocols and also recommended for any other type that is strongly associated with a particular class or protocol. If you leave out this step, the symbol will appear in an “Other” section in the generated HTML documentation’s table of contents.
+1. _(Optional.)_ If the symbol would also be publicly exposed in the macOS SDK, consult [the companion macOS document](../macos/DEVELOPING.md#making-a-type-or-constant-public) for further instructions.
+
### Adding a source code file
To add an Objective-C header or implementation file to the iOS SDK:
diff --git a/platform/ios/Mapbox.playground/Contents.swift b/platform/ios/Mapbox.playground/Contents.swift
index b1b11e5e34..1f368be73b 100644
--- a/platform/ios/Mapbox.playground/Contents.swift
+++ b/platform/ios/Mapbox.playground/Contents.swift
@@ -1,5 +1,9 @@
import UIKit
-import XCPlayground
+#if swift(>=3)
+ import PlaygroundSupport
+#else
+ import XCPlayground
+#endif
import Mapbox
let width: CGFloat = 700
@@ -9,7 +13,11 @@ class Responder: NSObject {
var mapView: MGLMapView?
func togglePitch(sender: UISwitch) {
let camera = mapView!.camera
- camera.pitch = sender.on ? 60 : 0
+ #if swift(>=3)
+ camera.pitch = sender.isOn ? 60 : 0
+ #else
+ camera.pitch = sender.on ? 60 : 0
+ #endif
mapView!.setCamera(camera, animated: false)
}
}
@@ -18,7 +26,11 @@ class Responder: NSObject {
let panelWidth: CGFloat = 200
let panel = UIView(frame: CGRect(x: width - panelWidth, y: 0, width: 200, height: 100))
panel.alpha = 0.8
-panel.backgroundColor = UIColor.whiteColor()
+#if swift(>=3)
+ panel.backgroundColor = .white
+#else
+ panel.backgroundColor = UIColor.whiteColor()
+#endif
// Delete markers
let deleteSwitchLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
@@ -41,7 +53,11 @@ let pitchLabel = UILabel(frame: CGRect(x: 0, y: 60, width: 100, height: 30))
pitchLabel.text = "Pitch"
let pitchSwitch = UISwitch(frame: CGRect(x: panelWidth-panelWidth / 2.0, y: 60, width: 100, height: 50))
let responder = Responder()
-pitchSwitch.addTarget(responder, action: #selector(responder.togglePitch(_:)), forControlEvents: .ValueChanged)
+#if swift(>=3)
+ pitchSwitch.addTarget(responder, action: #selector(responder.togglePitch(sender:)), for: .valueChanged)
+#else
+ pitchSwitch.addTarget(responder, action: #selector(responder.togglePitch(_:)), forControlEvents: .ValueChanged)
+#endif
panel.addSubview(pitchLabel)
panel.addSubview(pitchSwitch)
@@ -56,7 +72,11 @@ MGLAccountManager.setAccessToken(accessToken)
class PlaygroundAnnotationView: MGLAnnotationView {
override func prepareForReuse() {
- hidden = hideMarkerSwitchView.on
+ #if swift(>=3)
+ isHidden = hideMarkerSwitchView.isOn
+ #else
+ hidden = hideMarkerSwitchView.on
+ #endif
}
}
@@ -67,6 +87,29 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
var annotationViewByAnnotation = [MGLPointAnnotation: PlaygroundAnnotationView]()
+ #if swift(>=3)
+ func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
+
+ var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "annotation") as? PlaygroundAnnotationView
+
+ if (annotationView == nil) {
+ let av = PlaygroundAnnotationView(reuseIdentifier: "annotation")
+ av.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
+ av.centerOffset = CGVector(dx: -15, dy: -15)
+ let centerView = UIView(frame: av.bounds.insetBy(dx: 3, dy: 3))
+ centerView.backgroundColor = .white
+ av.addSubview(centerView)
+ av.backgroundColor = .purple
+ annotationView = av
+ } else {
+ annotationView!.subviews.first?.backgroundColor = .green
+ }
+
+ annotationViewByAnnotation[annotation as! MGLPointAnnotation] = annotationView
+
+ return annotationView
+ }
+ #else
func mapView(mapView: MGLMapView, viewForAnnotation annotation: MGLAnnotation) -> MGLAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier("annotation") as? PlaygroundAnnotationView
@@ -83,12 +126,40 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
} else {
annotationView!.subviews.first?.backgroundColor = UIColor.greenColor()
}
-
+
annotationViewByAnnotation[annotation as! MGLPointAnnotation] = annotationView
return annotationView
}
+ #endif
+ #if swift(>=3)
+ func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) {
+ let pointAnnotation = annotation as! MGLPointAnnotation
+ let annotationView: PlaygroundAnnotationView = annotationViewByAnnotation[pointAnnotation]!
+
+ for view in annotationViewByAnnotation.values {
+ view.layer.zPosition = -1
+ }
+
+ annotationView.layer.zPosition = 1
+
+ UIView.animate(withDuration: 1.25, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.6, options: .curveEaseOut, animations: {
+ annotationView.transform = CGAffineTransform(scaleX: 1.8, y: 1.8)
+ }) { _ in
+ annotationView.transform = CGAffineTransform(scaleX: 1, y: 1)
+
+ if deleteMarkerSwitchView.isOn {
+ mapView.removeAnnotation(pointAnnotation)
+ return
+ }
+
+ if hideMarkerSwitchView.isOn {
+ annotationView.isHidden = true
+ }
+ }
+ }
+ #else
func mapView(mapView: MGLMapView, didSelectAnnotation annotation: MGLAnnotation) {
let pointAnnotation = annotation as! MGLPointAnnotation
let annotationView: PlaygroundAnnotationView = annotationViewByAnnotation[pointAnnotation]!
@@ -114,15 +185,25 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
}
}
}
+ #endif
func handleTap(press: UILongPressGestureRecognizer) {
let mapView: MGLMapView = press.view as! MGLMapView
- if (press.state == .Recognized) {
- let coordiante: CLLocationCoordinate2D = mapView.convertPoint(press.locationInView(mapView), toCoordinateFromView: mapView)
+ #if swift(>=3)
+ let isRecognized = press.state == .recognized
+ #else
+ let isRecognized = press.state == .Recognized
+ #endif
+ if (isRecognized) {
+ #if swift(>=3)
+ let coordinate: CLLocationCoordinate2D = mapView.convert(press.location(in: mapView), toCoordinateFrom: mapView)
+ #else
+ let coordinate: CLLocationCoordinate2D = mapView.convertPoint(press.locationInView(mapView), toCoordinateFromView: mapView)
+ #endif
let annotation = MGLPointAnnotation()
annotation.title = "Dropped Marker"
- annotation.coordinate = coordiante
+ annotation.coordinate = coordinate
mapView.addAnnotation(annotation)
mapView.showAnnotations([annotation], animated: true)
}
@@ -132,14 +213,16 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
//: Create a map and its delegate
-let lat: CLLocationDegrees = 37.174057
-let lng: CLLocationDegrees = -104.490984
-let centerCoordinate = CLLocationCoordinate2D(latitude: lat, longitude: lng)
+let centerCoordinate = CLLocationCoordinate2D(latitude: 37.174057, longitude: -104.490984)
let mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: width, height: height))
mapView.frame = CGRect(x: 0, y: 0, width: width, height: height)
-XCPlaygroundPage.currentPage.liveView = mapView
+#if swift(>=3)
+ PlaygroundPage.current.liveView = mapView
+#else
+ XCPlaygroundPage.currentPage.liveView = mapView
+#endif
let mapDelegate = MapDelegate()
mapView.delegate = mapDelegate
@@ -150,7 +233,11 @@ mapView.addGestureRecognizer(tapGesture)
//: Zoom in to a location
-mapView.setCenterCoordinate(centerCoordinate, zoomLevel: 12, animated: false)
+#if swift(>=3)
+ mapView.setCenter(centerCoordinate, zoomLevel: 12, animated: false)
+#else
+ mapView.setCenterCoordinate(centerCoordinate, zoomLevel: 12, animated: false)
+#endif
//: Add control panel
diff --git a/platform/ios/app/MBXAppDelegate.h b/platform/ios/app/MBXAppDelegate.h
index 8145d3b8ca..7ff321b6c7 100644
--- a/platform/ios/app/MBXAppDelegate.h
+++ b/platform/ios/app/MBXAppDelegate.h
@@ -6,4 +6,4 @@ extern NSString * const MBXMapboxAccessTokenDefaultsKey;
@property (strong, nonatomic) UIWindow *window;
-@end \ No newline at end of file
+@end
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 789ee00372..ef91c847be 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -30,6 +30,12 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
@implementation MBXCustomCalloutAnnotation
@end
+@interface MBXSpriteBackedAnnotation : MGLPointAnnotation
+@end
+
+@implementation MBXSpriteBackedAnnotation
+@end
+
@interface MBXViewController () <UIActionSheetDelegate, MGLMapViewDelegate>
@property (nonatomic) IBOutlet MGLMapView *mapView;
@@ -184,9 +190,12 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
((debugMask & MGLMapDebugOverdrawVisualizationMask)
? @"Hide Overdraw Visualization"
: @"Show Overdraw Visualization"),
- @"Add 100 Points",
- @"Add 1,000 Points",
- @"Add 10,000 Points",
+ @"Add 100 Views",
+ @"Add 1,000 Views",
+ @"Add 10,000 Views",
+ @"Add 100 Sprites",
+ @"Add 1,000 Sprites",
+ @"Add 10,000 Sprites",
@"Add Test Shapes",
@"Start World Tour",
@"Add Custom Callout Point",
@@ -230,109 +239,42 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 6)
{
- [self parseFeaturesAddingCount:100];
+ [self parseFeaturesAddingCount:100 usingViews:YES];
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 7)
{
- [self parseFeaturesAddingCount:1000];
+ [self parseFeaturesAddingCount:1000 usingViews:YES];
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 8)
{
- [self parseFeaturesAddingCount:10000];
+ [self parseFeaturesAddingCount:10000 usingViews:YES];
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 9)
{
- // PNW triangle
- //
- CLLocationCoordinate2D triangleCoordinates[3] =
- {
- CLLocationCoordinate2DMake(44, -122),
- CLLocationCoordinate2DMake(46, -122),
- CLLocationCoordinate2DMake(46, -121)
- };
-
- MGLPolygon *triangle = [MGLPolygon polygonWithCoordinates:triangleCoordinates count:3];
-
- [self.mapView addAnnotation:triangle];
-
- // Orcas Island hike
- //
- NSDictionary *hike = [NSJSONSerialization JSONObjectWithData:
- [NSData dataWithContentsOfFile:
- [[NSBundle mainBundle] pathForResource:@"polyline" ofType:@"geojson"]]
- options:0
- error:nil];
-
- NSArray *hikeCoordinatePairs = hike[@"features"][0][@"geometry"][@"coordinates"];
-
- CLLocationCoordinate2D *polylineCoordinates = (CLLocationCoordinate2D *)malloc([hikeCoordinatePairs count] * sizeof(CLLocationCoordinate2D));
-
- for (NSUInteger i = 0; i < [hikeCoordinatePairs count]; i++)
- {
- polylineCoordinates[i] = CLLocationCoordinate2DMake([hikeCoordinatePairs[i][1] doubleValue], [hikeCoordinatePairs[i][0] doubleValue]);
- }
-
- MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:polylineCoordinates
- count:[hikeCoordinatePairs count]];
-
- [self.mapView addAnnotation:polyline];
-
- free(polylineCoordinates);
-
- // PA/NJ/DE polys
- //
- NSDictionary *threestates = [NSJSONSerialization JSONObjectWithData:
- [NSData dataWithContentsOfFile:
- [[NSBundle mainBundle] pathForResource:@"threestates" ofType:@"geojson"]]
- options:0
- error:nil];
-
- for (NSDictionary *feature in threestates[@"features"])
- {
- NSArray *stateCoordinatePairs = feature[@"geometry"][@"coordinates"];
-
- while ([stateCoordinatePairs count] == 1) stateCoordinatePairs = stateCoordinatePairs[0];
-
- CLLocationCoordinate2D *polygonCoordinates = (CLLocationCoordinate2D *)malloc([stateCoordinatePairs count] * sizeof(CLLocationCoordinate2D));
-
- for (NSUInteger i = 0; i < [stateCoordinatePairs count]; i++)
- {
- polygonCoordinates[i] = CLLocationCoordinate2DMake([stateCoordinatePairs[i][1] doubleValue], [stateCoordinatePairs[i][0] doubleValue]);
- }
-
- MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]];
-
- [self.mapView addAnnotation:polygon];
-
- free(polygonCoordinates);
- }
-
- CLLocationCoordinate2D innerCoordinates[] = {
- CLLocationCoordinate2DMake(-5, -5),
- CLLocationCoordinate2DMake(-5, 5),
- CLLocationCoordinate2DMake(5, 5),
- CLLocationCoordinate2DMake(5, -5),
- };
- MGLPolygon *innerPolygon = [MGLPolygon polygonWithCoordinates:innerCoordinates count:sizeof(innerCoordinates) / sizeof(innerCoordinates[0])];
- CLLocationCoordinate2D outerCoordinates[] = {
- CLLocationCoordinate2DMake(-10, -20),
- CLLocationCoordinate2DMake(-10, 10),
- CLLocationCoordinate2DMake(10, 10),
- CLLocationCoordinate2DMake(10, -10),
- };
- MGLPolygon *outerPolygon = [MGLPolygon polygonWithCoordinates:outerCoordinates count:sizeof(outerCoordinates) / sizeof(outerCoordinates[0]) interiorPolygons:@[innerPolygon]];
- [self.mapView addAnnotation:outerPolygon];
+ [self parseFeaturesAddingCount:100 usingViews:NO];
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 10)
{
- [self startWorldTour:actionSheet];
+ [self parseFeaturesAddingCount:1000 usingViews:NO];
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 11)
{
- [self presentAnnotationWithCustomCallout];
+ [self parseFeaturesAddingCount:10000 usingViews:NO];
}
else if (buttonIndex == actionSheet.firstOtherButtonIndex + 12)
{
+ [self addTestShapes];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 13)
+ {
+ [self startWorldTour:actionSheet];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 14)
+ {
+ [self presentAnnotationWithCustomCallout];
+ }
+ else if (buttonIndex == actionSheet.firstOtherButtonIndex + 15)
+ {
[self.mapView removeAnnotations:self.mapView.annotations];
}
else if (buttonIndex == actionSheet.numberOfButtons - 2 && self.debugLoggingEnabled)
@@ -355,7 +297,7 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
}
}
-- (void)parseFeaturesAddingCount:(NSUInteger)featuresCount
+- (void)parseFeaturesAddingCount:(NSUInteger)featuresCount usingViews:(BOOL)useViews
{
[self.mapView removeAnnotations:self.mapView.annotations];
@@ -377,7 +319,8 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
[feature[@"geometry"][@"coordinates"][0] doubleValue]);
NSString *title = feature[@"properties"][@"NAME"];
- MGLPointAnnotation *annotation = [MGLPointAnnotation new];
+ MGLPointAnnotation *annotation = (useViews ? [MGLPointAnnotation new] : [MBXSpriteBackedAnnotation new]);
+
annotation.coordinate = coordinate;
annotation.title = title;
@@ -395,6 +338,92 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
});
}
+- (void)addTestShapes
+{
+ // Pacific Northwest triangle
+ //
+ CLLocationCoordinate2D triangleCoordinates[3] =
+ {
+ CLLocationCoordinate2DMake(44, -122),
+ CLLocationCoordinate2DMake(46, -122),
+ CLLocationCoordinate2DMake(46, -121)
+ };
+
+ MGLPolygon *triangle = [MGLPolygon polygonWithCoordinates:triangleCoordinates count:3];
+
+ [self.mapView addAnnotation:triangle];
+
+ // Orcas Island, WA hike polyline
+ //
+ NSDictionary *hike = [NSJSONSerialization JSONObjectWithData:
+ [NSData dataWithContentsOfFile:
+ [[NSBundle mainBundle] pathForResource:@"polyline" ofType:@"geojson"]]
+ options:0
+ error:nil];
+
+ NSArray *hikeCoordinatePairs = hike[@"features"][0][@"geometry"][@"coordinates"];
+
+ CLLocationCoordinate2D *polylineCoordinates = (CLLocationCoordinate2D *)malloc([hikeCoordinatePairs count] * sizeof(CLLocationCoordinate2D));
+
+ for (NSUInteger i = 0; i < [hikeCoordinatePairs count]; i++)
+ {
+ polylineCoordinates[i] = CLLocationCoordinate2DMake([hikeCoordinatePairs[i][1] doubleValue], [hikeCoordinatePairs[i][0] doubleValue]);
+ }
+
+ MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:polylineCoordinates
+ count:[hikeCoordinatePairs count]];
+
+ [self.mapView addAnnotation:polyline];
+
+ free(polylineCoordinates);
+
+ // PA/NJ/DE polygons
+ //
+ NSDictionary *threestates = [NSJSONSerialization JSONObjectWithData:
+ [NSData dataWithContentsOfFile:
+ [[NSBundle mainBundle] pathForResource:@"threestates" ofType:@"geojson"]]
+ options:0
+ error:nil];
+
+ for (NSDictionary *feature in threestates[@"features"])
+ {
+ NSArray *stateCoordinatePairs = feature[@"geometry"][@"coordinates"];
+
+ while ([stateCoordinatePairs count] == 1) stateCoordinatePairs = stateCoordinatePairs[0];
+
+ CLLocationCoordinate2D *polygonCoordinates = (CLLocationCoordinate2D *)malloc([stateCoordinatePairs count] * sizeof(CLLocationCoordinate2D));
+
+ for (NSUInteger i = 0; i < [stateCoordinatePairs count]; i++)
+ {
+ polygonCoordinates[i] = CLLocationCoordinate2DMake([stateCoordinatePairs[i][1] doubleValue], [stateCoordinatePairs[i][0] doubleValue]);
+ }
+
+ MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]];
+
+ [self.mapView addAnnotation:polygon];
+
+ free(polygonCoordinates);
+ }
+
+ // Null Island polygon with an interior hole
+ //
+ CLLocationCoordinate2D innerCoordinates[] = {
+ CLLocationCoordinate2DMake(-5, -5),
+ CLLocationCoordinate2DMake(-5, 5),
+ CLLocationCoordinate2DMake(5, 5),
+ CLLocationCoordinate2DMake(5, -5),
+ };
+ MGLPolygon *innerPolygon = [MGLPolygon polygonWithCoordinates:innerCoordinates count:sizeof(innerCoordinates) / sizeof(innerCoordinates[0])];
+ CLLocationCoordinate2D outerCoordinates[] = {
+ CLLocationCoordinate2DMake(-10, -10),
+ CLLocationCoordinate2DMake(-10, 10),
+ CLLocationCoordinate2DMake(10, 10),
+ CLLocationCoordinate2DMake(10, -10),
+ };
+ MGLPolygon *outerPolygon = [MGLPolygon polygonWithCoordinates:outerCoordinates count:sizeof(outerCoordinates) / sizeof(outerCoordinates[0]) interiorPolygons:@[innerPolygon]];
+ [self.mapView addAnnotation:outerPolygon];
+}
+
- (void)presentAnnotationWithCustomCallout
{
[self.mapView removeAnnotations:self.mapView.annotations];
@@ -575,7 +604,7 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
- (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAnnotation>)annotation
{
// Use GL backed pins for dropped pin annotations
- if ([annotation isKindOfClass:[MBXDroppedPinAnnotation class]])
+ if ([annotation isKindOfClass:[MBXDroppedPinAnnotation class]] || [annotation isKindOfClass:[MBXSpriteBackedAnnotation class]])
{
return nil;
}
@@ -603,6 +632,73 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
return annotationView;
}
+- (MGLAnnotationImage *)mapView:(MGLMapView * __nonnull)mapView imageForAnnotation:(id <MGLAnnotation> __nonnull)annotation
+{
+ if ([annotation isKindOfClass:[MBXDroppedPinAnnotation class]] || [annotation isKindOfClass:[MBXCustomCalloutAnnotation class]])
+ {
+ return nil; // use default marker
+ }
+
+ NSAssert([annotation isKindOfClass:[MBXSpriteBackedAnnotation class]], @"Annotations should be sprite-backed.");
+
+ NSString *title = [(MGLPointAnnotation *)annotation title];
+ if (!title.length) return nil;
+ NSString *lastTwoCharacters = [title substringFromIndex:title.length - 2];
+
+ MGLAnnotationImage *annotationImage = [mapView dequeueReusableAnnotationImageWithIdentifier:lastTwoCharacters];
+
+ if ( ! annotationImage)
+ {
+ UIColor *color;
+
+ // make every tenth annotation blue
+ if ([lastTwoCharacters hasSuffix:@"0"]) {
+ color = [UIColor blueColor];
+ } else {
+ color = [UIColor redColor];
+ }
+
+ UIImage *image = [self imageWithText:lastTwoCharacters backgroundColor:color];
+ annotationImage = [MGLAnnotationImage annotationImageWithImage:image reuseIdentifier:lastTwoCharacters];
+
+ // don't allow touches on blue annotations
+ if ([color isEqual:[UIColor blueColor]]) annotationImage.enabled = NO;
+ }
+
+ return annotationImage;
+}
+
+
+- (UIImage *)imageWithText:(NSString *)text backgroundColor:(UIColor *)color
+{
+ CGRect rect = CGRectMake(0, 0, 20, 15);
+
+ UIGraphicsBeginImageContextWithOptions(rect.size, NO, [[UIScreen mainScreen] scale]);
+
+ CGContextRef ctx = UIGraphicsGetCurrentContext();
+
+ CGContextSetFillColorWithColor(ctx, [[color colorWithAlphaComponent:0.75] CGColor]);
+ CGContextFillRect(ctx, rect);
+
+ CGContextSetStrokeColorWithColor(ctx, [[UIColor blackColor] CGColor]);
+ CGContextStrokeRectWithWidth(ctx, rect, 2);
+
+ NSAttributedString *drawString = [[NSAttributedString alloc] initWithString:text attributes:@{
+ NSFontAttributeName: [UIFont fontWithName:@"Arial-BoldMT" size:12],
+ NSForegroundColorAttributeName: [UIColor whiteColor],
+ }];
+ CGSize stringSize = drawString.size;
+ CGRect stringRect = CGRectMake((rect.size.width - stringSize.width) / 2,
+ (rect.size.height - stringSize.height) / 2,
+ stringSize.width,
+ stringSize.height);
+ [drawString drawInRect:stringRect];
+
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return image;
+}
+
- (BOOL)mapView:(__unused MGLMapView *)mapView annotationCanShowCallout:(__unused id <MGLAnnotation>)annotation
{
return YES;
diff --git a/platform/ios/benchmark/MBXBenchAppDelegate.h b/platform/ios/benchmark/MBXBenchAppDelegate.h
index e89add93fd..6ae9cdf27c 100644
--- a/platform/ios/benchmark/MBXBenchAppDelegate.h
+++ b/platform/ios/benchmark/MBXBenchAppDelegate.h
@@ -6,4 +6,4 @@ extern NSString * const MBXMapboxAccessTokenDefaultsKey;
@property (strong, nonatomic) UIWindow *window;
-@end \ No newline at end of file
+@end
diff --git a/platform/ios/docs/doc-README.md b/platform/ios/docs/doc-README.md
index 057dbe719c..22493b1502 100644
--- a/platform/ios/docs/doc-README.md
+++ b/platform/ios/docs/doc-README.md
@@ -1,6 +1,6 @@
# [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)
-The Mapbox iOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 7.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox GL Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
+The Mapbox iOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 7.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
![Mapbox iOS SDK screenshots](screenshot.png)
diff --git a/platform/ios/docs/pod-README.md b/platform/ios/docs/pod-README.md
index 1633c7d126..93aa18bc65 100644
--- a/platform/ios/docs/pod-README.md
+++ b/platform/ios/docs/pod-README.md
@@ -1,6 +1,6 @@
# [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)
-The Mapbox iOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 7.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox GL Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
+The Mapbox iOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 7.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
For more information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/) and the [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) online.
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index 83ae3e6406..bfd5a5e8a9 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -13,6 +13,8 @@
353794D01D22B3BD002C281C /* NSData+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35305D461D22AA450007D005 /* NSData+MGLAdditions.h */; settings = {ATTRIBUTES = (Private, ); }; };
353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */; };
35E208A71D24210F00EC9A46 /* MGLNSDataAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */; };
+ 36F1153D1D46080700878E1A /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 36F1153B1D46080700878E1A /* libmbgl-core.a */; };
+ 36F1153E1D46080700878E1A /* libmbgl-platform-ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 36F1153C1D46080700878E1A /* libmbgl-platform-ios.a */; };
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 */; };
@@ -22,6 +24,7 @@
40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; };
40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; };
40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; };
+ 554180421D2E97DE00012372 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; };
DA0CD5901CF56F6A00A5F5A5 /* MGLFeatureTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */; };
DA17BE301CC4BAC300402C41 /* MGLMapView_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = DA17BE2F1CC4BAC300402C41 /* MGLMapView_Internal.h */; };
DA17BE311CC4BDAA00402C41 /* MGLMapView_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = DA17BE2F1CC4BAC300402C41 /* MGLMapView_Internal.h */; };
@@ -335,6 +338,8 @@
35305D471D22AA450007D005 /* NSData+MGLAdditions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSData+MGLAdditions.mm"; sourceTree = "<group>"; };
353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationViewTests.m; sourceTree = "<group>"; };
35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNSDataAdditionsTests.m; sourceTree = "<group>"; };
+ 36F1153B1D46080700878E1A /* libmbgl-core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-core.a"; path = "build/Debug-iphoneos/libmbgl-core.a"; sourceTree = "<group>"; };
+ 36F1153C1D46080700878E1A /* libmbgl-platform-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-platform-ios.a"; path = "build/Debug-iphoneos/libmbgl-platform-ios.a"; sourceTree = "<group>"; };
4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView_Private.h; sourceTree = "<group>"; };
4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAnnotationView.mm; sourceTree = "<group>"; };
4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView.h; sourceTree = "<group>"; };
@@ -343,6 +348,7 @@
40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationContainerView.m; sourceTree = "<group>"; };
40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXAnnotationView.h; sourceTree = "<group>"; };
40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAnnotationView.m; sourceTree = "<group>"; };
+ 554180411D2E97DE00012372 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFeatureTests.mm; path = ../../darwin/test/MGLFeatureTests.mm; sourceTree = "<group>"; };
DA17BE2F1CC4BAC300402C41 /* MGLMapView_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapView_Internal.h; sourceTree = "<group>"; };
DA1DC94A1CB6C1C2006E619F /* Mapbox GL.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -516,6 +522,7 @@
buildActionMask = 2147483647;
files = (
DA8847D91CBAF91600AB86E3 /* Mapbox.framework in Frameworks */,
+ 554180421D2E97DE00012372 /* OpenGLES.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -543,6 +550,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 36F1153D1D46080700878E1A /* libmbgl-core.a in Frameworks */,
+ 36F1153E1D46080700878E1A /* libmbgl-platform-ios.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -631,6 +640,9 @@
DA1DC9921CB6DF24006E619F /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 36F1153B1D46080700878E1A /* libmbgl-core.a */,
+ 36F1153C1D46080700878E1A /* libmbgl-platform-ios.a */,
+ 554180411D2E97DE00012372 /* OpenGLES.framework */,
DAABF73B1CBC59BB005B1825 /* libmbgl-core.a */,
DAABF73C1CBC59BB005B1825 /* libmbgl-platform-ios.a */,
DAA4E4021CBB5C2F00178DFB /* CoreGraphics.framework */,
@@ -1701,6 +1713,7 @@
"$(OTHER_CFLAGS)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1719,6 +1732,7 @@
"$(OTHER_CFLAGS)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1751,12 +1765,13 @@
"$(rapidjson_cflags)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
OTHER_LDFLAGS = (
"$(sqlite_ldflags)",
"$(zlib_ldflags)",
"$(opengl_ldflags)",
- "$(geojsonvt_static_libs)",
+ "$(geojson_static_libs)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
PRODUCT_NAME = Mapbox;
@@ -1792,12 +1807,13 @@
"$(rapidjson_cflags)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
OTHER_LDFLAGS = (
"$(sqlite_ldflags)",
"$(zlib_ldflags)",
"$(opengl_ldflags)",
- "$(geojsonvt_static_libs)",
+ "$(geojson_static_libs)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
PRODUCT_NAME = Mapbox;
@@ -1839,6 +1855,10 @@
../../include,
../../src,
);
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/build/Debug-iphoneos",
+ );
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"$(sqlite_cflags)",
@@ -1846,13 +1866,14 @@
"$(rapidjson_cflags)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
OTHER_LDFLAGS = (
"-ObjC",
"$(sqlite_ldflags)",
"$(zlib_ldflags)",
"$(opengl_ldflags)",
- "$(geojsonvt_static_libs)",
+ "$(geojson_static_libs)",
);
PRODUCT_NAME = Mapbox;
PUBLIC_HEADERS_FOLDER_PATH = Headers;
@@ -1870,6 +1891,10 @@
../../include,
../../src,
);
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/build/Debug-iphoneos",
+ );
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"$(sqlite_cflags)",
@@ -1877,13 +1902,14 @@
"$(rapidjson_cflags)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
OTHER_LDFLAGS = (
"-ObjC",
"$(sqlite_ldflags)",
"$(zlib_ldflags)",
"$(opengl_ldflags)",
- "$(geojsonvt_static_libs)",
+ "$(geojson_static_libs)",
);
PRODUCT_NAME = Mapbox;
PUBLIC_HEADERS_FOLDER_PATH = Headers;
diff --git a/platform/ios/scripts/configure.sh b/platform/ios/scripts/configure.sh
index 408180881c..82599f0fe9 100644
--- a/platform/ios/scripts/configure.sh
+++ b/platform/ios/scripts/configure.sh
@@ -5,8 +5,11 @@ PROTOZERO_VERSION=1.3.0
BOOST_VERSION=1.60.0
SQLITE_VERSION=system
ZLIB_VERSION=system
-GEOMETRY_VERSION=0.5.0
-GEOJSONVT_VERSION=4.1.2
+GEOMETRY_VERSION=0.8.0
+GEOJSON_VERSION=0.1.4
+GEOJSONVT_VERSION=6.1.2
+SUPERCLUSTER_VERSION=0.2.0
+KDBUSH_VERSION=0.1.1
VARIANT_VERSION=1.1.0
RAPIDJSON_VERSION=1.0.2
GTEST_VERSION=1.7.0
diff --git a/platform/ios/scripts/package.sh b/platform/ios/scripts/package.sh
index c753c63dfd..c012934203 100755
--- a/platform/ios/scripts/package.sh
+++ b/platform/ios/scripts/package.sh
@@ -108,7 +108,7 @@ if [[ ${BUILD_FOR_DEVICE} == true ]]; then
-jobs ${JOBS} | xcpretty
fi
-LIBS=(Mapbox.a mbgl-core.a mbgl-platform-ios.a)
+LIBS=(Mapbox.a)
# https://medium.com/@syshen/create-an-ios-universal-framework-148eb130a46c
if [[ "${BUILD_FOR_DEVICE}" == true ]]; then
@@ -119,7 +119,7 @@ if [[ "${BUILD_FOR_DEVICE}" == true ]]; then
-o ${OUTPUT}/static/${NAME}.framework/${NAME} \
${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphoneos/lib} \
${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphonesimulator/lib} \
- `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libgeojsonvt.a`
+ `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libgeojson.a`
cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.bundle ${STATIC_BUNDLE_DIR}
fi
@@ -129,9 +129,19 @@ if [[ "${BUILD_FOR_DEVICE}" == true ]]; then
cp -r \
${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.framework \
${OUTPUT}/dynamic/
+
if [[ -e ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.framework.dSYM ]]; then
+ step "Copying dSYM"
cp -r ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.framework.dSYM \
- ${OUTPUT}/dynamic/
+ ${OUTPUT}/dynamic/
+ if [[ -e ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.framework.dSYM ]]; then
+ step "Merging device and simulator dSYMs…"
+ lipo \
+ ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME} \
+ ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME} \
+ -create -output ${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}
+ lipo -info ${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}
+ fi
fi
step "Merging simulator dynamic library into device dynamic library…"
@@ -149,7 +159,7 @@ else
libtool -static -no_warning_for_no_symbols \
-o ${OUTPUT}/static/${NAME}.framework/${NAME} \
${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphonesimulator/lib} \
- `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libgeojsonvt.a`
+ `find mason_packages/ios-${IOS_SDK_VERSION} -type f -name libgeojson.a`
cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.bundle ${STATIC_BUNDLE_DIR}
fi
@@ -160,6 +170,7 @@ else
${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.framework \
${OUTPUT}/dynamic/${NAME}.framework
if [[ -e ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.framework.dSYM ]]; then
+ step "Copying dSYM"
cp -r ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.framework.dSYM \
${OUTPUT}/dynamic/
fi
diff --git a/platform/ios/src/MGLAnnotationImage.h b/platform/ios/src/MGLAnnotationImage.h
index fa2adb3830..5ded4016f7 100644
--- a/platform/ios/src/MGLAnnotationImage.h
+++ b/platform/ios/src/MGLAnnotationImage.h
@@ -4,7 +4,12 @@
NS_ASSUME_NONNULL_BEGIN
-/** The MGLAnnotationImage class is responsible for presenting point-based annotations visually on a map view. Annotation image objects wrap `UIImage` objects and may be recycled later and put into a reuse queue that is maintained by the map view. */
+/**
+ The `MGLAnnotationImage` class is responsible for presenting point-based
+ annotations visually on a map view. Annotation image objects wrap `UIImage`
+ objects and may be recycled later and put into a reuse queue that is maintained
+ by the map view.
+ */
@interface MGLAnnotationImage : NSObject
#pragma mark Initializing and Preparing the Image Object
@@ -13,8 +18,10 @@ NS_ASSUME_NONNULL_BEGIN
Initializes and returns a new annotation image object.
@param image The image to be displayed for the annotation.
- @param reuseIdentifier The string that identifies that this annotation image is reusable.
- @return The initialized annotation image object or `nil` if there was a problem initializing the object.
+ @param reuseIdentifier The string that identifies that this annotation image is
+ reusable.
+ @return The initialized annotation image object or `nil` if there was a problem
+ initializing the object.
*/
+ (instancetype)annotationImageWithImage:(UIImage *)image reuseIdentifier:(NSString *)reuseIdentifier;
@@ -26,16 +33,21 @@ NS_ASSUME_NONNULL_BEGIN
/**
The string that identifies that this annotation image is reusable. (read-only)
- You specify the reuse identifier when you create the image object. You use this type later to retrieve an annotation image object that was created previously but which is currently unused because its annotation is not on screen.
+ You specify the reuse identifier when you create the image object. You use this
+ type later to retrieve an annotation image object that was created previously
+ but which is currently unused because its annotation is not on screen.
- If you define distinctly different types of annotations (with distinctly different annotation images to go with them), you can differentiate between the annotation types by specifying different reuse identifiers for each one.
+ If you define distinctly different types of annotations (with distinctly
+ different annotation images to go with them), you can differentiate between the
+ annotation types by specifying different reuse identifiers for each one.
*/
@property (nonatomic, readonly) NSString *reuseIdentifier;
/**
A Boolean value indicating whether the annotation is enabled.
- The default value of this property is `YES`. If the value of this property is `NO`, the annotation image ignores touch events and cannot be selected.
+ The default value of this property is `YES`. If the value of this property is
+ `NO`, the annotation image ignores touch events and cannot be selected.
*/
@property (nonatomic, getter=isEnabled) BOOL enabled;
diff --git a/platform/ios/src/MGLCalloutView.h b/platform/ios/src/MGLCalloutView.h
index 641976dfee..27d173b230 100644
--- a/platform/ios/src/MGLCalloutView.h
+++ b/platform/ios/src/MGLCalloutView.h
@@ -8,32 +8,38 @@ NS_ASSUME_NONNULL_BEGIN
@protocol MGLAnnotation;
/**
- A protocol for a `UIView` subclass that displays information about a selected annotation near that annotation.
+ A protocol for a `UIView` subclass that displays information about a selected
+ annotation near that annotation.
*/
@protocol MGLCalloutView <NSObject>
/**
- An object conforming to the `MGLAnnotation` protocol whose details this callout view displays.
+ An object conforming to the `MGLAnnotation` protocol whose details this callout
+ view displays.
*/
@property (nonatomic, strong) id <MGLAnnotation> representedObject;
/**
- A view that the user may tap to perform an action. This view is conventionally positioned on the left side of the callout view.
+ A view that the user may tap to perform an action. This view is conventionally
+ positioned on the left side of the callout view.
*/
@property (nonatomic, strong) UIView *leftAccessoryView;
/**
- A view that the user may tap to perform an action. This view is conventionally positioned on the right side of the callout view.
+ A view that the user may tap to perform an action. This view is conventionally
+ positioned on the right side of the callout view.
*/
@property (nonatomic, strong) UIView *rightAccessoryView;
/**
- An object conforming to the `MGLCalloutViewDelegate` method that receives messages related to the callout view’s interactive subviews.
+ An object conforming to the `MGLCalloutViewDelegate` method that receives
+ messages related to the callout view’s interactive subviews.
*/
@property (nonatomic, weak) id<MGLCalloutViewDelegate> delegate;
/**
- Presents a callout view by adding it to `inView` and pointing at the given rect of `inView`’s bounds. Constrains the callout to the bounds of the given view.
+ Presents a callout view by adding it to `view` and pointing at the given rect
+ of `view`’s bounds. Constrains the callout to the bounds of the given view.
*/
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated;
@@ -45,15 +51,21 @@ NS_ASSUME_NONNULL_BEGIN
@end
/**
- The MGLCalloutViewDelegate protocol defines a set of optional methods that you can use to receive messages from an object that conforms to the MGLCalloutView protocol. The callout view uses these methods to inform the delegate that the user has interacted with the the callout view.
+ The `MGLCalloutViewDelegate` protocol defines a set of optional methods that
+ you can use to receive messages from an object that conforms to the
+ `MGLCalloutView` protocol. The callout view uses these methods to inform the
+ delegate that the user has interacted with the the callout view.
*/
@protocol MGLCalloutViewDelegate <NSObject>
@optional
/**
- Returns a Boolean value indicating whether the entire callout view “highlights” when tapped. The default value is `YES`, which means the callout view highlights when tapped.
+ Returns a Boolean value indicating whether the entire callout view “highlights”
+ when tapped. The default value is `YES`, which means the callout view
+ highlights when tapped.
- The return value of this method is ignored unless the delegate also responds to the `-calloutViewTapped` method.
+ The return value of this method is ignored unless the delegate also responds to
+ the `-calloutViewTapped` method.
*/
- (BOOL)calloutViewShouldHighlight:(UIView<MGLCalloutView> *)calloutView;
@@ -63,12 +75,14 @@ NS_ASSUME_NONNULL_BEGIN
- (void)calloutViewTapped:(UIView<MGLCalloutView> *)calloutView;
/**
- Called before the callout view appears on screen, or before the appearance animation will start.
+ Called before the callout view appears on screen, or before the appearance
+ animation will start.
*/
- (void)calloutViewWillAppear:(UIView<MGLCalloutView> *)calloutView;
/**
- Called after the callout view appears on screen, or after the appearance animation is complete.
+ Called after the callout view appears on screen, or after the appearance
+ animation is complete.
*/
- (void)calloutViewDidAppear:(UIView<MGLCalloutView> *)calloutView;
diff --git a/platform/ios/src/MGLCompactCalloutView.h b/platform/ios/src/MGLCompactCalloutView.h
index ebe27c842c..56c48a99e5 100644
--- a/platform/ios/src/MGLCompactCalloutView.h
+++ b/platform/ios/src/MGLCompactCalloutView.h
@@ -2,7 +2,10 @@
#import "MGLCalloutView.h"
/**
- A concrete implementation of `MGLCalloutView` based on <a href="https://github.com/nfarina/calloutview">SMCalloutView</a>. This callout view displays the represented annotation’s title, subtitle, and accessory views in a compact, two-line layout.
+ A concrete implementation of `MGLCalloutView` based on
+ <a href="https://github.com/nfarina/calloutview">SMCalloutView</a>. This
+ callout view displays the represented annotation’s title, subtitle, and
+ accessory views in a compact, two-line layout.
*/
@interface MGLCompactCalloutView : SMCalloutView <MGLCalloutView>
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index a70b603f02..56236c54d8 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -43,7 +43,7 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationVerticalAlignment) {
MGLAnnotationVerticalAlignmentBottom,
};
-/** Options for enabling debugging features in an MGLMapView instance. */
+/** Options for enabling debugging features in an `MGLMapView` instance. */
typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
/** Edges of tile boundaries are shown as thick, red lines to help diagnose
tile clipping issues. */
@@ -57,13 +57,14 @@ typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
MGLMapDebugCollisionBoxesMask = 1 << 4,
/** Each drawing operation is replaced by a translucent fill. Overlapping
drawing operations appear more prominent to help diagnose overdrawing.
+ @note This option does nothing in Release builds of the SDK.
*/
MGLMapDebugOverdrawVisualizationMask = 1 << 5,
};
/**
An interactive, customizable map view with an interface similar to the one
- provided by Apple's MapKit.
+ provided by Apple’s MapKit.
Using `MGLMapView`, you can embed the map inside a view, allow users to
manipulate it with standard gestures, animate the map between different
@@ -72,7 +73,7 @@ typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
The map view loads scalable vector tiles that conform to the
<a href="https://github.com/mapbox/vector-tile-spec">Mapbox Vector Tile Specification</a>.
It styles them with a style that conforms to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/">Mapbox GL style specification</a>.
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/">Mapbox Style Specification</a>.
Such styles can be designed in
<a href="https://www.mapbox.com/studio/">Mapbox Studio</a> and hosted on
mapbox.com.
@@ -86,7 +87,7 @@ typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
Mapbox-hosted vector tiles and styles require an API access token, which you
can obtain from the
<a href="https://www.mapbox.com/studio/account/tokens/">Mapbox account page</a>.
- Access tokens associate requests to Mapbox's vector tile and style APIs with
+ Access tokens associate requests to Mapbox’s vector tile and style APIs with
your Mapbox account. They also deter other developers from using your styles
without your permission.
@@ -113,9 +114,9 @@ IB_DESIGNABLE
@param frame The frame for the view, measured in points.
@param styleURL URL of the map style to display. The URL may be a full HTTP
- or HTTPS URL, a Mapbox URL indicating the style's map ID
+ 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.
+ to the application’s resource path. Specify `nil` for the default style.
@return An initialized map view.
*/
- (instancetype)initWithFrame:(CGRect)frame styleURL:(nullable NSURL *)styleURL;
@@ -123,7 +124,7 @@ IB_DESIGNABLE
#pragma mark Accessing the Delegate
/**
- The receiver's delegate.
+ The receiver’s delegate.
A map view sends messages to its delegate to notify it of changes to its
contents or the viewpoint. The delegate also provides information about
@@ -132,7 +133,7 @@ IB_DESIGNABLE
*/
@property(nonatomic, weak, nullable) IBOutlet id<MGLMapViewDelegate> delegate;
-#pragma mark Configuring the Map's Appearance
+#pragma mark Configuring the Map’s Appearance
/**
URLs of the styles bundled with the library.
@@ -145,12 +146,12 @@ IB_DESIGNABLE
/**
URL of the style currently displayed in the receiver.
- The URL may be a full HTTP or HTTPS URL, a Mapbox URL indicating the style's
+ 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.
+ relative to the application’s resource path.
If you set this property to `nil`, the receiver will use the default style
- and this property will automatically be set to that style's URL.
+ and this property will automatically be set to that style’s URL.
*/
@property (nonatomic, null_resettable) NSURL *styleURL;
@@ -169,7 +170,7 @@ IB_DESIGNABLE
- (IBAction)reloadStyle:(id)sender;
/**
- A control indicating the map's direction and allowing the user to manipulate
+ A control indicating the map’s direction and allowing the user to manipulate
the direction, positioned in the upper-right corner.
*/
@property (nonatomic, readonly) UIImageView *compassView;
@@ -235,18 +236,18 @@ IB_DESIGNABLE
*/
- (void)removeStyleClass:(NSString *)styleClass;
-#pragma mark Displaying the User's Location
+#pragma mark Displaying the User’s Location
/**
A Boolean value indicating whether the map may display the user location.
Setting this property to `YES` causes the map view to use the Core Location
framework to find the current location. As long as this property is `YES`, the
- map view continues to track the user's location and update it periodically.
+ map view continues to track the user’s location and update it periodically.
- This property does not indicate whether the user's position is actually visible
+ This property does not indicate whether the user’s position is actually visible
on the map, only whether the map view is allowed to display it. To determine
- whether the user's position is visible, use the `userLocationVisible` property.
+ whether the user’s position is visible, use the `userLocationVisible` property.
The default value of this property is `NO`.
On iOS 8 and above, your app must specify a value for
@@ -257,7 +258,7 @@ IB_DESIGNABLE
@property (nonatomic, assign) BOOL showsUserLocation;
/**
- A Boolean value indicating whether the device's current location is visible in
+ A Boolean value indicating whether the device’s current location is visible in
the map view.
Use `showsUserLocation` to control the visibility of the on-screen user
@@ -266,7 +267,7 @@ IB_DESIGNABLE
@property (nonatomic, assign, readonly, getter=isUserLocationVisible) BOOL userLocationVisible;
/**
- Returns the annotation object indicating the user's current location.
+ Returns the annotation object indicating the user’s current location.
*/
@property (nonatomic, readonly, nullable) MGLUserLocation *userLocation;
@@ -577,7 +578,7 @@ IB_DESIGNABLE
- (IBAction)resetNorth;
/**
- The coordinate bounds visible in the receiver's viewport.
+ The coordinate bounds visible in the receiver’s viewport.
Changing the value of this property updates the receiver immediately. If you
want to animate the change, call `-setVisibleCoordinateBounds:animated:`
@@ -596,7 +597,7 @@ IB_DESIGNABLE
- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)animated;
/**
- Changes the receiver's viewport to fit the given coordinate bounds and
+ Changes the receiver’s viewport to fit the given coordinate bounds and
optionally some additional padding on each side.
@param bounds The bounds that the viewport will show in its entirety.
@@ -608,7 +609,7 @@ IB_DESIGNABLE
- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
/**
- Changes the receiver's viewport to fit all of the given coordinates and
+ Changes the receiver’s viewport to fit all of the given coordinates and
optionally some additional padding on each side.
@param coordinates The coordinates that the viewport will show.
@@ -622,7 +623,7 @@ IB_DESIGNABLE
- (void)setVisibleCoordinates:(CLLocationCoordinate2D *)coordinates count:(NSUInteger)count edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
/**
- Changes the receiver's viewport to fit all of the given coordinates and
+ Changes the receiver’s viewport to fit all of the given coordinates and
optionally some additional padding on each side.
@param coordinates The coordinates that the viewport will show.
@@ -656,7 +657,7 @@ IB_DESIGNABLE
Sets the visible region so that the map displays the specified annotations with
the specified amount of padding on each side.
- Calling this method updates the value in the visibleCoordinateBounds property
+ Calling this method updates the value in the `visibleCoordinateBounds` property
and potentially other properties to reflect the new map region.
@param annotations The annotations that you want to be visible in the map.
@@ -787,7 +788,7 @@ IB_DESIGNABLE
- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets;
/**
- Returns the point in this view's coordinate system on which to "anchor" in
+ Returns the point in this view’s coordinate system on which to "anchor" in
response to a user-initiated gesture.
For example, a pinch-to-zoom gesture would anchor the map at the midpoint of
@@ -797,7 +798,7 @@ IB_DESIGNABLE
user annotation is used as the anchor point.
Subclasses may override this method to provide specialized behavior - for
- example, anchoring on the map's center point to provide a "locked" zooming
+ example, anchoring on the map’s center point to provide a "locked" zooming
mode.
@param gesture An anchorable user gesture.
@@ -849,7 +850,7 @@ IB_DESIGNABLE
#pragma mark Converting Geographic Coordinates
/**
- Converts a point in the given view's coordinate system to a geographic
+ Converts a point in the given view’s coordinate system to a geographic
coordinate.
@param point The point to convert.
@@ -859,13 +860,13 @@ IB_DESIGNABLE
- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(nullable UIView *)view;
/**
- Converts a geographic coordinate to a point in the given view's coordinate
+ Converts a geographic coordinate to a point in the given view’s coordinate
system.
@param coordinate The geographic coordinate to convert.
@param view The view in whose coordinate system the returned point should be
expressed. If this parameter is `nil`, the returned point is expressed
- in the window's coordinate system. If `view` is not `nil`, it must
+ in the window’s coordinate system. If `view` is not `nil`, it must
belong to the same window as the map view.
@return The point (in the appropriate view or window coordinate system)
corresponding to the given geographic coordinate.
@@ -895,7 +896,7 @@ IB_DESIGNABLE
- (CGRect)convertCoordinateBounds:(MGLCoordinateBounds)bounds toRectToView:(nullable UIView *)view;
/**
- Returns the distance spanned by one point in the map view's coordinate system
+ Returns the distance spanned by one point in the map view’s coordinate system
at the given latitude and current zoom level.
The distance between points decreases as the latitude approaches the poles.
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 4ec3619189..b0c213ebfb 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -134,11 +134,11 @@ mbgl::Color MGLColorObjectFromUIColor(UIColor *color)
{
if (!color)
{
- return {{ 0, 0, 0, 0 }};
+ return { 0, 0, 0, 0 };
}
CGFloat r, g, b, a;
[color getRed:&r green:&g blue:&b alpha:&a];
- return {{ (float)r, (float)g, (float)b, (float)a }};
+ return { (float)r, (float)g, (float)b, (float)a };
}
@interface MGLAnnotationAccessibilityElement : UIAccessibilityElement
@@ -1815,7 +1815,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
{
mask |= MGLMapDebugCollisionBoxesMask;
}
- if (options & mbgl::MapDebugOptions::Wireframe)
+ if (options & mbgl::MapDebugOptions::Overdraw)
{
mask |= MGLMapDebugOverdrawVisualizationMask;
}
@@ -1843,7 +1843,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
}
if (debugMask & MGLMapDebugOverdrawVisualizationMask)
{
- options |= mbgl::MapDebugOptions::Wireframe;
+ options |= mbgl::MapDebugOptions::Overdraw;
}
_mbglMap->setDebug(options);
}
@@ -2182,8 +2182,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
mbgl::AnimationOptions animationOptions;
if (duration)
{
- animationOptions.duration = MGLDurationInSeconds(duration);
- animationOptions.easing = MGLUnitBezierForMediaTimingFunction(function);
+ animationOptions.duration.emplace(MGLDurationInSeconds(duration));
+ animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
if (completion)
{
@@ -2335,8 +2335,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
mbgl::AnimationOptions animationOptions;
if (duration > 0)
{
- animationOptions.duration = MGLDurationInSeconds(duration);
- animationOptions.easing = MGLUnitBezierForMediaTimingFunction(function);
+ animationOptions.duration.emplace(MGLDurationInSeconds(duration));
+ animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
if (completion)
{
@@ -2442,8 +2442,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
mbgl::AnimationOptions animationOptions;
if (duration > 0)
{
- animationOptions.duration = MGLDurationInSeconds(duration);
- animationOptions.easing = MGLUnitBezierForMediaTimingFunction(function);
+ animationOptions.duration.emplace(MGLDurationInSeconds(duration));
+ animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
if (completion)
{
@@ -4556,7 +4556,13 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
MGLAnnotationContext &annotationContext = pair.second;
MGLAnnotationView *annotationView = annotationContext.annotationView;
-
+
+ // Defer to the shape/polygon styling delegate methods
+ if ([annotationContext.annotation isKindOfClass:[MGLMultiPoint class]])
+ {
+ continue;
+ }
+
if (!annotationView)
{
MGLAnnotationView *annotationView = [self annotationViewForAnnotation:annotationContext.annotation];
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
index 12a0658a51..db1327685a 100644
--- a/platform/ios/src/MGLMapViewDelegate.h
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -204,12 +204,12 @@ NS_ASSUME_NONNULL_BEGIN
/**
Returns the alpha value to use when rendering a shape annotation.
- A value of 0.0 results in a completely transparent shape. A value of 1.0, the
- default, results in a completely opaque shape.
+ A value of `0.0` results in a completely transparent shape. A value of `1.0`,
+ the default, results in a completely opaque shape.
@param mapView The map view rendering the shape annotation.
@param annotation The annotation being rendered.
- @return An alpha value between 0 and 1.0.
+ @return An alpha value between `0` and `1.0`.
*/
- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation;
@@ -241,7 +241,7 @@ NS_ASSUME_NONNULL_BEGIN
Returns the line width in points to use when rendering the outline of a
polyline annotation.
- By default, the polyline is outlined with a line 3.0 points wide.
+ By default, the polyline is outlined with a line `3.0` points wide.
@param mapView The map view rendering the polygon annotation.
@param annotation The annotation being rendered.
diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h
index 6160413510..81f9557b16 100644
--- a/platform/ios/src/MGLUserLocation.h
+++ b/platform/ios/src/MGLUserLocation.h
@@ -7,7 +7,12 @@
NS_ASSUME_NONNULL_BEGIN
-/** The MGLUserLocation class defines a specific type of annotation that identifies the user’s current location. You do not create instances of this class directly. Instead, you retrieve an existing MGLUserLocation object from the userLocation property of the map view displayed in your application. */
+/**
+ The MGLUserLocation class defines a specific type of annotation that identifies
+ the user’s current location. You do not create instances of this class
+ directly. Instead, you retrieve an existing MGLUserLocation object from the
+ `userLocation` property of the map view displayed in your application.
+ */
@interface MGLUserLocation : NSObject <MGLAnnotation>
#pragma mark Determining the User’s Position
@@ -15,17 +20,22 @@ NS_ASSUME_NONNULL_BEGIN
/**
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 contains `nil` if the map view is not currently showing the user
+ location or if the user’s location has not yet been determined.
*/
@property (nonatomic, readonly, nullable) CLLocation *location;
-/** A Boolean value indicating whether the user’s location is currently being updated. (read-only) */
+/**
+ A Boolean value indicating whether the user’s location is currently being
+ updated. (read-only)
+ */
@property (nonatomic, readonly, getter=isUpdating) BOOL updating;
/**
The heading of the user location. (read-only)
- This property is `nil` if the user location tracking mode is not `MGLUserTrackingModeFollowWithHeading`.
+ This property is `nil` if the user location tracking mode is not
+ `MGLUserTrackingModeFollowWithHeading`.
*/
@property (nonatomic, readonly, nullable) CLHeading *heading;
diff --git a/platform/ios/src/MGLUserLocationAnnotationView.h b/platform/ios/src/MGLUserLocationAnnotationView.h
index 1eb50b7740..40432581fd 100644
--- a/platform/ios/src/MGLUserLocationAnnotationView.h
+++ b/platform/ios/src/MGLUserLocationAnnotationView.h
@@ -8,7 +8,7 @@ NS_ASSUME_NONNULL_BEGIN
@class MGLMapView;
@class MGLUserLocation;
-/** View representing an MGLUserLocation on screen. */
+/** View representing an `MGLUserLocation` on screen. */
@interface MGLUserLocationAnnotationView : UIView
@property (nonatomic, weak) MGLMapView *mapView;
diff --git a/platform/linux/scripts/configure.sh b/platform/linux/scripts/configure.sh
index 8f041d589a..3d37994868 100644
--- a/platform/linux/scripts/configure.sh
+++ b/platform/linux/scripts/configure.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-CXX11ABI=$(scripts/check-cxx11abi.sh)
+CXX11ABI=${CXX11ABI:-$(scripts/check-cxx11abi.sh)}
UNIQUE_RESOURCE_VERSION=dev
PROTOZERO_VERSION=1.3.0
@@ -14,8 +14,11 @@ SQLITE_VERSION=3.9.1
LIBUV_VERSION=1.7.5
ZLIB_VERSION=system
NUNICODE_VERSION=1.6
-GEOMETRY_VERSION=0.5.0
-GEOJSONVT_VERSION=4.1.2${CXX11ABI:-}
+GEOMETRY_VERSION=0.8.0
+GEOJSON_VERSION=0.1.4${CXX11ABI:-}
+GEOJSONVT_VERSION=6.1.2
+SUPERCLUSTER_VERSION=0.2.0
+KDBUSH_VERSION=0.1.1
VARIANT_VERSION=1.1.0
RAPIDJSON_VERSION=1.0.2
GTEST_VERSION=1.7.0${CXX11ABI:-}
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index e09f1e5b5f..9bd4188552 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -2,24 +2,42 @@
## master
+* Added `showAnnotations:animated:` and `showAnnotations:edgePadding:animated:`, which moves the map viewport to show the specified annotations. ([#5749](https://github.com/mapbox/mapbox-gl-native/pull/5749))
+
+## 0.2.1 - July 19, 2016
+
* Fixed a crash that occurred when a sprite URL lacks a file extension. See [this comment](https://github.com/mapbox/mapbox-gl-native/issues/5722#issuecomment-233701251) to determine who may be affected by this bug. ([#5723](https://github.com/mapbox/mapbox-gl-native/pull/5723))
+* Right-clicking to open MGLMapView’s context menu no longer prevents the user from subsequently panning the map by clicking and dragging. ([#5593](https://github.com/mapbox/mapbox-gl-native/pull/5593))
* Fixed an issue causing overlapping polylines and polygons to be drawn in undefined z-order. Shapes are always drawn in the order they are added to the map, from the oldest on the bottom to the newest on the top. ([#5710](https://github.com/mapbox/mapbox-gl-native/pull/5710))
+* Improved the design of the generated API documentation. ([#5306](https://github.com/mapbox/mapbox-gl-native/pull/5306))
+* As the user zooms in, tiles from lower zoom levels are scaled up until tiles for higher zoom levels are loaded. ([#5143](https://github.com/mapbox/mapbox-gl-native/pull/5143))
+* Added [quadkey](https://msdn.microsoft.com/en-us/library/bb259689.aspx) support and limited WMS support in raster tile URL templates. ([#5628](https://github.com/mapbox/mapbox-gl-native/pull/5628))
+* Fixed a crash that occurred when a style or other resource URL has a query string. ([#5554](https://github.com/mapbox/mapbox-gl-native/pull/5554))
* Fixed an issue causing polyline and polygon annotations to disappear when the zoom level is one less than the maximum zoom level. ([#5418](https://github.com/mapbox/mapbox-gl-native/pull/5418))
* Added a property to MGLOfflineStorage, `countOfBytesCompleted`, that indicates the disk space occupied by all cached and offline resources. ([#5585](https://github.com/mapbox/mapbox-gl-native/pull/5585))
-* Fixed a crash that occurred when a style or other resource URL has a query string. ([#5554](https://github.com/mapbox/mapbox-gl-native/pull/5554))
+* The `text-pitch-alignment` property is now supported in stylesheets for improved street label legibility on a tilted map. ([#5288](https://github.com/mapbox/mapbox-gl-native/pull/5288))
+* The `icon-text-fit` and `icon-text-fit-padding` properties are now supported in stylesheets, allowing the background of a shield to automatically resize to fit the shield’s text. ([#5334](https://github.com/mapbox/mapbox-gl-native/pull/5334))
+* Improved the performance of relocating a point annotation by changing its `coordinate` property. ([#5385](https://github.com/mapbox/mapbox-gl-native/pull/5385))
* Replaced the wireframe debug mask with an overdraw visualization debug mask to match Mapbox GL JS’s overdraw inspector. ([#5403](https://github.com/mapbox/mapbox-gl-native/pull/5403))
-* Improved the design of the generated API documentation. ([#5306](https://github.com/mapbox/mapbox-gl-native/pull/5306))
+* MGLMapDebugOverdrawVisualizationMask and MGLMapDebugStencilBufferMask does nothing in Release builds of the SDK. These are disabled for performance reasons. ([#5555](https://github.com/mapbox/mapbox-gl-native/pull/5555))
-## 0.2.0
+## 0.2.0 - June 14, 2016
+* This version of the Mapbox macOS SDK roughly corresponds to version 3.3.0-beta.1 of the Mapbox iOS SDK. The two SDKs have very similar feature sets. The main differences are the lack of user location tracking and annotation views. Some APIs have been adapted to macOS conventions, particularly the use of NSPopover for callout views.
* Renamed the SDK to the Mapbox macOS SDK.
* Fixed an issue in which Mapbox.framework was nested inside another folder named Mapbox.framework. ([#4998](https://github.com/mapbox/mapbox-gl-native/pull/4998))
* Added methods to MGLMapView for obtaining the underlying map data rendered by the current style, along with additional classes to represent complex geometry in that data. ([#5110](https://github.com/mapbox/mapbox-gl-native/pull/5110))
* An MGLPolygon can now have interior polygons, representing holes knocked out of the overall shape. ([#5110](https://github.com/mapbox/mapbox-gl-native/pull/5110))
+* Fixed a vector tile parsing bug that sometimes caused properties in the vector tile source to be mismatched. ([#5183](https://github.com/mapbox/mapbox-gl-native/pull/5183))
* Fixed a crash passing a mixture of point and shape annotations into `-[MGLMapView addAnnotations:]`. ([#5097](https://github.com/mapbox/mapbox-gl-native/pull/5097))
-* Added new options to `MGLMapDebugMaskOptions` that show wireframes and the stencil buffer instead of the color buffer. ([#4359](https://github.com/mapbox/mapbox-gl-native/pull/4359))
+* Fixed an issue (speculatively) where the tile cache could be included in iCloud backups. ([#5124](https://github.com/mapbox/mapbox-gl-native/pull/5124))
+* Improved performance viewing regions with large landcover polygons when viewing a style that uses the Mapbox Streets source. ([#2444](https://github.com/mapbox/mapbox-gl-native/pull/2444))
* Fixed a memory leak when using raster resources. ([#5141](https://github.com/mapbox/mapbox-gl-native/pull/5141))
+* Added `MGLCoordinateInCoordinateBounds()`, a function that tests whether or not a coordinate is in a given bounds. ([#5053](https://github.com/mapbox/mapbox-gl-native/pull/5053))
+* Fixed an issue in which fade transitions (such as on street labels in some styles) lagged behind the map when quickly zooming in and out. ([#4579](https://github.com/mapbox/mapbox-gl-native/pull/4579))
+* Added new options to `MGLMapDebugMaskOptions` that show wireframes and the stencil buffer instead of the color buffer. ([#4359](https://github.com/mapbox/mapbox-gl-native/pull/4359))
+* Declarations in the API documentation are shown in both Objective-C and Swift. ([realm/jazzy#530](https://github.com/realm/jazzy/pull/530))
-## 0.1.0
+## 0.1.0 - May 10, 2016
* This version of the Mapbox OS X SDK roughly corresponds to version 3.3.0-alpha.2 of the Mapbox iOS SDK. The two SDKs have very similar feature sets. The main difference is the lack of user location tracking. Some APIs have been adapted to OS X conventions, particularly the use of NSPopover for callout views.
diff --git a/platform/macos/DEVELOPING.md b/platform/macos/DEVELOPING.md
index fc7b0330a3..5f437f4172 100644
--- a/platform/macos/DEVELOPING.md
+++ b/platform/macos/DEVELOPING.md
@@ -14,6 +14,21 @@ The Mapbox macOS SDK and the macosapp demo application run on macOS 10.10.0 and
## Contributing
+### Making any symbol public
+
+To add any Objective-C type, constant, or member to the iOS SDK’s public interface:
+
+1. Ensure that the symbol is pure Objective-C and does not rely on any language features specific to Objective-C++ or the C11 dialect of C – so no namespaced types or classes named with emoji! 🙃 Most projects that depend on this SDK are either written in pure Objective-C (GNU99 dialect) or Swift, which cannot yet bridge C++ types.
+1. Name the symbol according to [Cocoa naming conventions](https://developer.apple.com/library/prerelease/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html#//apple_ref/doc/uid/10000146i). Use the `MGL` class prefix to avoid conflicts with client code. If the symbol has an analogue in MapKit, name the symbol according to MapKit.
+1. Provide full documentation comments. We use [jazzy](https://github.com/realm/jazzy/) to produce the documentation found in the SDK distribution. We also recognize that many developers rely on Xcode’s Quick Help feature. jazzy supports Markdown formatting; however, Quick Help supports only [HeaderDoc](https://developer.apple.com/legacy/library/documentation/DeveloperTools/Conceptual/HeaderDoc/intro/intro.html) syntax and a subset of Doxygen syntax. For hyperlinks, use HTML syntax, which is recognized by both tools.
+
+### Making a type or constant public
+
+To add an Objective-C class, protocol, category, typedef, enumeration, or global constant to the macOS SDK’s public interface:
+
+1. _(Optional.)_ Add the type or constant’s name to the relevant category in the `custom_categories` section of [the jazzy configuration file](./jazzy.yml). This is required for classes and protocols and also recommended for any other type that is strongly associated with a particular class or protocol. If you leave out this step, the symbol will appear in an “Other” section in the generated HTML documentation’s table of contents.
+1. _(Optional.)_ If the symbol would also be publicly exposed in the iOS SDK, consult [the companion iOS document](../ios/DEVELOPING.md#making-a-type-or-constant-public) for further instructions.
+
### Adding a source code file
To add an Objective-C header or implementation file to the macOS SDK:
diff --git a/platform/macos/INSTALL.md b/platform/macos/INSTALL.md
index 42896d380d..07aae88a2e 100644
--- a/platform/macos/INSTALL.md
+++ b/platform/macos/INSTALL.md
@@ -21,7 +21,15 @@ Grab a [prebuilt release](https://github.com/mapbox/mapbox-gl-native/releases/)
## Usage
-In a storyboard or XIB, add a view to your view controller. (Drag Custom View from the Object library to the View Controller scene on the Interface Builder canvas.) In the Identity inspector, set the view’s custom class to `MGLMapView`. If you need to manipulate the map view programmatically:
+In a storyboard or XIB:
+
+1. Add a view to your view controller or window. (Drag Custom View from the Object library to the View Controller scene on the Interface Builder canvas. In a XIB, drag it instead to the window on the canvas.)
+2. In the Identity inspector, set the view’s custom class to `MGLMapView`.
+3. MGLMapView needs to be layer-backed:
+ * You can make the window layer-backed by selecting the window and checking Full Size Content View in the Attributes inspector. This allows the map view to underlap the title bar and toolbar.
+ * Alternatively, if you don’t want the entire window to be layer-backed, you can make just the map view layer-backed by selecting it and checking its entry under the View Effects inspector’s Core Animation Layer section.
+
+If you need to manipulate the map view programmatically:
1. Switch to the Assistant Editor.
1. Import the `Mapbox` module.
@@ -47,4 +55,12 @@ class ViewController: NSViewController {
}
```
+```applescript
+-- AppDelegate.applescript
+script AppDelegate
+ property parent : class "NSObject"
+ property theMapView : missing value
+end script
+```
+
Run `make xdocument` to generate complete API documentation. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox macOS SDK with few differences, mostly around unimplemented features like user location tracking.
diff --git a/platform/macos/app/AppDelegate.m b/platform/macos/app/AppDelegate.m
index b7860cf130..d3fe2d204e 100644
--- a/platform/macos/app/AppDelegate.m
+++ b/platform/macos/app/AppDelegate.m
@@ -53,6 +53,22 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
return self.progress.countOfBytesCompleted;
}
++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfTilesCompleted {
+ return [NSSet setWithObjects:@"progress", nil];
+}
+
+- (uint64_t)countOfTilesCompleted {
+ return self.progress.countOfTilesCompleted;
+}
+
++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfTileBytesCompleted {
+ return [NSSet setWithObjects:@"progress", nil];
+}
+
+- (uint64_t)countOfTileBytesCompleted {
+ return self.progress.countOfTileBytesCompleted;
+}
+
@end
@interface AppDelegate ()
@@ -233,7 +249,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
alert.informativeText = @"\
• To scroll, swipe with two fingers on a trackpad, or drag the cursor, or press the arrow keys.\n\
• To zoom in, pinch two fingers apart on a trackpad, or double-click, or hold down Shift while dragging the cursor down, or hold down Option while pressing the up key.\n\
-• To zoom out, pinch two fingers together on a trackpad, or double-tap on a mouse, or hold down Shift while dragging the cursor up, or hold down Option while pressing the down key.\n\
+• To zoom out, pinch two fingers together on a trackpad, or double-tap with two fingers on a trackpad, or double-tap on a mouse, or hold down Shift while dragging the cursor up, or hold down Option while pressing the down key.\n\
• To rotate, move two fingers opposite each other in a circle on a trackpad, or hold down Option while dragging the cursor left and right, or hold down Option while pressing the left and right arrow keys.\n\
• To tilt, hold down Option while dragging the cursor up and down.\n\
• To drop a pin, click and hold.\
diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib
index 36fb02736c..4d81289314 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" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11129.15" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G26a" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11129.15"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -129,7 +129,6 @@
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
- <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
@@ -437,45 +436,40 @@
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Debug" id="McE-ka-r79">
<items>
- <menuItem title="Show Tile Boundaries" id="rDE-dG-rTR">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <menuItem title="Show Tile Boundaries" keyEquivalent="b" id="rDE-dG-rTR">
<connections>
<action selector="toggleTileBoundaries:" target="-1" id="LAO-88-F7h"/>
</connections>
</menuItem>
- <menuItem title="Show Tile Info" id="LoH-qD-kb0">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <menuItem title="Show Tile Info" keyEquivalent="i" id="LoH-qD-kb0">
<connections>
<action selector="toggleTileInfo:" target="-1" id="KCn-0G-V87"/>
</connections>
</menuItem>
- <menuItem title="Show Tile Timestamps" id="bY0-2E-LZ7">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <menuItem title="Show Tile Timestamps" keyEquivalent="t" id="bY0-2E-LZ7">
<connections>
<action selector="toggleTileTimestamps:" target="-1" id="tBs-2N-KEG"/>
</connections>
</menuItem>
- <menuItem title="Show Collision Boxes" id="Y0b-3K-mJE">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <menuItem title="Show Collision Boxes" keyEquivalent="C" id="Y0b-3K-mJE">
<connections>
<action selector="toggleCollisionBoxes:" target="-1" id="EYa-7n-iWZ"/>
</connections>
</menuItem>
- <menuItem title="Show Overdraw Visualization" id="hSX-Be-8xC">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <menuItem title="Show Overdraw Visualization" keyEquivalent="O" id="hSX-Be-8xC">
<connections>
<action selector="toggleOverdrawVisualization:" target="-1" id="usj-ug-upt"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="2EG-Hp-4FA"/>
- <menuItem title="Show Color Buffer" id="Eao-WE-BWz">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <menuItem title="Color Buffer" keyEquivalent="c" id="Eao-WE-BWz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="showColorBuffer:" target="-1" id="Nuq-Qs-98g"/>
</connections>
</menuItem>
- <menuItem title="Show Stencil Buffer" id="LlS-Yh-RkN">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <menuItem title="Stencil Buffer" keyEquivalent="s" id="LlS-Yh-RkN">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="showStencilBuffer:" target="-1" id="WkN-t9-Mpv"/>
</connections>
@@ -494,27 +488,40 @@
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="Sl5-nE-kHd"/>
- <menuItem title="Blanket Map With Pins" id="LMZ-oe-Ngh">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <menuItem title="Blanket Map With Pins" keyEquivalent="." id="LMZ-oe-Ngh">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="dropManyPins:" target="-1" id="Rtv-8N-3Z8"/>
</connections>
</menuItem>
- <menuItem title="Add Polygon and Polyline" id="DVr-vT-lpe">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <menuItem title="Add Polygon and Polyline" keyEquivalent="l" id="DVr-vT-lpe">
<connections>
<action selector="drawPolygonAndPolyLineAnnotations:" target="-1" id="EhT-CB-gee"/>
</connections>
</menuItem>
- <menuItem title="Remove All Annotations" id="6rC-68-vk0">
+ <menuItem title="Add Animated Annotation" id="Etf-JN-Aoc">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
+ <action selector="drawAnimatedAnnotation:" target="-1" id="CYM-WB-s97"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All Annnotations" keyEquivalent="A" id="yMj-uM-8SN">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <connections>
+ <action selector="showAllAnnotations:" target="-1" id="ahr-OR-Em2"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Remove All Annotations" id="6rC-68-vk0">
+ <string key="keyEquivalent" base64-UTF8="YES">
+CA
+</string>
+ <connections>
<action selector="removeAllAnnotations:" target="-1" id="6v3-0E-LsR"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="wQq-Mx-QY0"/>
<menuItem title="Start World Tour" id="VFo-Jh-2sw">
- <modifierMask key="keyEquivalentModifierMask"/>
+ <modifierMask key="keyEquivalentModifierMask" option="YES"/>
<connections>
<action selector="startWorldTour:" target="-1" id="66Y-Gm-Yn1"/>
</connections>
@@ -711,13 +718,13 @@
</connections>
</tableColumn>
<tableColumn editable="NO" width="50" minWidth="40" maxWidth="1000" id="pkI-c7-xoD">
- <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Downloaded">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Downloaded Resources">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="WfC-qb-HsW">
- <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="sNm-Qn-ne6"/>
+ <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" minimumIntegerDigits="1" maximumIntegerDigits="2000000000" id="sNm-Qn-ne6"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -728,13 +735,13 @@
</connections>
</tableColumn>
<tableColumn identifier="" editable="NO" width="50" minWidth="10" maxWidth="3.4028234663852886e+38" id="Rrd-A9-jqc">
- <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Total">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Total Resources">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="mHy-qJ-rOA">
- <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="kyx-ZP-OBH"/>
+ <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" minimumIntegerDigits="1" maximumIntegerDigits="2000000000" id="kyx-ZP-OBH"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -744,14 +751,56 @@
<binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesExpected" id="mh2-k0-vvB"/>
</connections>
</tableColumn>
+ <tableColumn editable="NO" width="50" minWidth="40" maxWidth="1000" id="kCO-Cd-bQt">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Downloaded Tiles">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="hUl-2C-sHr">
+ <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" minimumIntegerDigits="1" maximumIntegerDigits="2000000000" id="KjY-J1-gSm"/>
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfTilesCompleted" id="XHn-D7-zqf">
+ <dictionary key="options">
+ <bool key="NSConditionallySetsEditable" value="YES"/>
+ </dictionary>
+ </binding>
+ </connections>
+ </tableColumn>
+ <tableColumn editable="NO" width="60" minWidth="10" maxWidth="3.4028234663852886e+38" id="WO5-Ci-HgG">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Downloaded Tiles Size">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="cKy-kF-5Pv">
+ <byteCountFormatter key="formatter" allowsNonnumericFormatting="NO" includesActualByteCount="YES" id="bHS-Ch-aXU"/>
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfTileBytesCompleted" id="Xpk-BZ-Xcr">
+ <dictionary key="options">
+ <bool key="NSConditionallySetsEditable" value="YES"/>
+ </dictionary>
+ </binding>
+ </connections>
+ </tableColumn>
<tableColumn identifier="" editable="NO" width="60" minWidth="10" maxWidth="3.4028234663852886e+38" id="h7m-6l-KaS">
- <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Size">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Downloaded Resources Size">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="701-bg-k6L">
- <byteCountFormatter key="formatter" allowsNonnumericFormatting="NO" id="IXV-J9-sP3"/>
+ <byteCountFormatter key="formatter" allowsNonnumericFormatting="NO" includesActualByteCount="YES" id="IXV-J9-sP3"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -767,10 +816,9 @@
</connections>
</tableView>
</subviews>
- <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
- <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="QLr-6P-Ogs">
- <rect key="frame" x="1" y="7" width="0.0" height="16"/>
+ <scroller key="horizontalScroller" verticalHuggingPriority="750" horizontal="YES" id="QLr-6P-Ogs">
+ <rect key="frame" x="1" y="264" width="400" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="q0K-eE-mzL">
@@ -778,7 +826,7 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="MAZ-Iq-hBi">
- <rect key="frame" x="0.0" y="0.0" width="400" height="23"/>
+ <rect key="frame" x="0.0" y="0.0" width="423" height="23"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
@@ -832,6 +880,8 @@ CA
<string>context</string>
<string>countOfResourcesCompleted</string>
<string>countOfResourcesExpected</string>
+ <string>countOfTilesCompleted</string>
+ <string>countOfTileBytesCompleted</string>
<string>countOfBytesCompleted</string>
<string>stateImage</string>
</declaredKeys>
diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m
index 1b920a029b..81fd85e156 100644
--- a/platform/macos/app/MapDocument.m
+++ b/platform/macos/app/MapDocument.m
@@ -65,6 +65,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
BOOL _randomizesCursorsOnDroppedPins;
BOOL _isTouringWorld;
BOOL _isShowingPolygonAndPolylineAnnotations;
+ BOOL _isShowingAnimatedAnnotation;
}
#pragma mark Lifecycle
@@ -180,7 +181,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
- (IBAction)chooseCustomStyle:(id)sender {
NSAlert *alert = [[NSAlert alloc] init];
alert.messageText = @"Apply custom style";
- alert.informativeText = @"Enter the URL to a JSON file that conforms to the Mapbox GL style specification, such as a style designed in Mapbox Studio:";
+ alert.informativeText = @"Enter the URL to a JSON file that conforms to the Mapbox Style Specification, such as a style designed in Mapbox Studio:";
NSTextField *textField = [[NSTextField alloc] initWithFrame:NSZeroRect];
[textField sizeToFit];
NSRect textFieldFrame = textField.frame;
@@ -307,7 +308,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
}
}
- [NSTimer scheduledTimerWithTimeInterval:1/60
+ [NSTimer scheduledTimerWithTimeInterval:1.0/60.0
target:self
selector:@selector(dropOneOfManyPins:)
userInfo:annotations
@@ -329,9 +330,14 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
}
}
+- (IBAction)showAllAnnotations:(id)sender {
+ [self.mapView showAnnotations:self.mapView.annotations animated:YES];
+}
+
- (IBAction)removeAllAnnotations:(id)sender {
[self.mapView removeAnnotations:self.mapView.annotations];
_isShowingPolygonAndPolylineAnnotations = NO;
+ _isShowingAnimatedAnnotation = NO;
}
- (IBAction)startWorldTour:(id)sender {
@@ -405,6 +411,27 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
[self.mapView addAnnotation:line];
}
+- (IBAction)drawAnimatedAnnotation:(id)sender {
+ DroppedPinAnnotation *annotation = [[DroppedPinAnnotation alloc] init];
+ [self.mapView addAnnotation:annotation];
+
+ _isShowingAnimatedAnnotation = YES;
+
+ [NSTimer scheduledTimerWithTimeInterval:1.0/60.0
+ target:self
+ selector:@selector(updateAnimatedAnnotation:)
+ userInfo:annotation
+ repeats:YES];
+}
+
+- (void)updateAnimatedAnnotation:(NSTimer *)timer {
+ DroppedPinAnnotation *annotation = timer.userInfo;
+ double angle = timer.fireDate.timeIntervalSinceReferenceDate;
+ annotation.coordinate = CLLocationCoordinate2DMake(
+ sin(angle) * 20,
+ cos(angle) * 20);
+}
+
#pragma mark Offline packs
- (IBAction)addOfflinePack:(id)sender {
@@ -613,7 +640,13 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
if (menuItem.action == @selector(dropManyPins:)) {
return YES;
}
- if (menuItem.action == @selector(removeAllAnnotations:)) {
+ if (menuItem.action == @selector(drawPolygonAndPolyLineAnnotations:)) {
+ return !_isShowingPolygonAndPolylineAnnotations;
+ }
+ if (menuItem.action == @selector(drawAnimatedAnnotation:)) {
+ return !_isShowingAnimatedAnnotation;
+ }
+ if (menuItem.action == @selector(showAllAnnotations:) || menuItem.action == @selector(removeAllAnnotations:)) {
return self.mapView.annotations.count > 0;
}
if (menuItem.action == @selector(startWorldTour:)) {
@@ -622,9 +655,6 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
if (menuItem.action == @selector(stopWorldTour:)) {
return _isTouringWorld;
}
- if (menuItem.action == @selector(drawPolygonAndPolyLineAnnotations:)) {
- return !_isShowingPolygonAndPolylineAnnotations;
- }
if (menuItem.action == @selector(addOfflinePack:)) {
NSURL *styleURL = self.mapView.styleURL;
return !styleURL.isFileURL;
diff --git a/platform/macos/app/wms.json b/platform/macos/app/wms.json
new file mode 100644
index 0000000000..e5fb236259
--- /dev/null
+++ b/platform/macos/app/wms.json
@@ -0,0 +1,21 @@
+{
+ "version": 8,
+ "name": "WMS Test",
+ "sources": {
+ "wms-test": {
+ "type": "raster",
+ "tiles": [
+ "https://geodata.state.nj.us/imagerywms/Natural2015?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&width=256&height=256&layers=Natural2015"
+ ],
+ "tileSize": 256
+ }
+ },
+ "layers": [{
+ "id": "wms-test-layer",
+ "type": "raster",
+ "source": "wms-test",
+ "paint": {
+ "raster-fade-duration": 100
+ }
+ }]
+}
diff --git a/platform/macos/docs/doc-README.md b/platform/macos/docs/doc-README.md
index 58dc6001df..4aba26afd9 100644
--- a/platform/macos/docs/doc-README.md
+++ b/platform/macos/docs/doc-README.md
@@ -1,6 +1,6 @@
# [Mapbox macOS SDK](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/macos/)
-The Mapbox macOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox GL Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
+The Mapbox macOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
<img alt="Mapbox macOS SDK screenshot" src="screenshot.png" width="645">
diff --git a/platform/macos/docs/pod-README.md b/platform/macos/docs/pod-README.md
index 70d98ecdb9..b86ccfb897 100644
--- a/platform/macos/docs/pod-README.md
+++ b/platform/macos/docs/pod-README.md
@@ -1,6 +1,6 @@
# [Mapbox macOS SDK](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/macos/)
-The Mapbox macOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox GL Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
+The Mapbox macOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
<img alt="" src="https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/macos/screenshot.png" width="645">
diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj
index 7271356f2a..ed980294a9 100644
--- a/platform/macos/macos.xcodeproj/project.pbxproj
+++ b/platform/macos/macos.xcodeproj/project.pbxproj
@@ -20,6 +20,7 @@
DA35A2C21CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */; };
DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA35A2D01CCAAED300E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */; };
+ DA5589771D320C41006B7F64 /* wms.json in Resources */ = {isa = PBXBuildFile; fileRef = DA5589761D320C41006B7F64 /* wms.json */; };
DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA839E961CC2E3400062CAFB /* AppDelegate.m */; };
DA839E9A1CC2E3400062CAFB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA839E991CC2E3400062CAFB /* main.m */; };
DA839E9D1CC2E3400062CAFB /* MapDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = DA839E9C1CC2E3400062CAFB /* MapDocument.m */; };
@@ -159,6 +160,7 @@
DA35A2C11CCA9F4A00E826B2 /* MGLClockDirectionFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLClockDirectionFormatterTests.m; path = ../../darwin/test/MGLClockDirectionFormatterTests.m; sourceTree = "<group>"; };
DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLAdditions.h"; sourceTree = "<group>"; };
DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+MGLAdditions.m"; sourceTree = "<group>"; };
+ DA5589761D320C41006B7F64 /* wms.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = wms.json; sourceTree = "<group>"; };
DA839E921CC2E3400062CAFB /* Mapbox GL.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL.app"; sourceTree = BUILT_PRODUCTS_DIR; };
DA839E951CC2E3400062CAFB /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
DA839E961CC2E3400062CAFB /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@@ -334,6 +336,7 @@
DAE6C2EC1CC3050F00DB3429 /* TimeIntervalTransformer.m */,
DA839EA11CC2E3400062CAFB /* Assets.xcassets */,
DA839EA31CC2E3400062CAFB /* MainMenu.xib */,
+ DA5589761D320C41006B7F64 /* wms.json */,
DAE6C2E11CC304F900DB3429 /* Credits.rtf */,
DA839EA61CC2E3400062CAFB /* Info.plist */,
DA839E981CC2E3400062CAFB /* Supporting Files */,
@@ -721,6 +724,7 @@
DA839EA21CC2E3400062CAFB /* Assets.xcassets in Resources */,
DA839EA01CC2E3400062CAFB /* MapDocument.xib in Resources */,
DA839EA51CC2E3400062CAFB /* MainMenu.xib in Resources */,
+ DA5589771D320C41006B7F64 /* wms.json in Resources */,
DAE6C2E21CC304F900DB3429 /* Credits.rtf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1035,11 +1039,12 @@
"$(rapidjson_cflags)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
OTHER_LDFLAGS = (
"$(zlib_ldflags)",
"$(opengl_ldflags)",
- "$(geojsonvt_static_libs)",
+ "$(geojson_static_libs)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = Mapbox;
@@ -1074,11 +1079,12 @@
"$(rapidjson_cflags)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
OTHER_LDFLAGS = (
"$(zlib_ldflags)",
"$(opengl_ldflags)",
- "$(geojsonvt_static_libs)",
+ "$(geojson_static_libs)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = Mapbox;
@@ -1100,6 +1106,7 @@
"$(OTHER_CFLAGS)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1118,6 +1125,7 @@
"$(OTHER_CFLAGS)",
"$(variant_cflags)",
"$(geometry_cflags)",
+ "$(geojson_cflags)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test;
PRODUCT_NAME = "$(TARGET_NAME)";
diff --git a/platform/macos/macos.xcworkspace/xcshareddata/xcschemes/test.xcscheme b/platform/macos/macos.xcworkspace/xcshareddata/xcschemes/test.xcscheme
index 20394eb258..bc8a3350f1 100644
--- a/platform/macos/macos.xcworkspace/xcshareddata/xcschemes/test.xcscheme
+++ b/platform/macos/macos.xcworkspace/xcshareddata/xcschemes/test.xcscheme
@@ -62,6 +62,12 @@
ReferencedContainer = "container:../../build/macos/platform/macos/platform.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
+ <CommandLineArguments>
+ <CommandLineArgument
+ argument = "--gtest_filter="
+ isEnabled = "NO">
+ </CommandLineArgument>
+ </CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
diff --git a/platform/macos/scripts/configure.sh b/platform/macos/scripts/configure.sh
index 2952ec2535..d408eef7e4 100644
--- a/platform/macos/scripts/configure.sh
+++ b/platform/macos/scripts/configure.sh
@@ -8,8 +8,11 @@ GLFW_VERSION=3.1.2
SQLITE_VERSION=3.9.1
ZLIB_VERSION=system
NUNICODE_VERSION=1.6
-GEOMETRY_VERSION=0.5.0
-GEOJSONVT_VERSION=4.1.2
+GEOMETRY_VERSION=0.8.0
+GEOJSON_VERSION=0.1.4
+GEOJSONVT_VERSION=6.1.2
+SUPERCLUSTER_VERSION=0.2.0
+KDBUSH_VERSION=0.1.1
VARIANT_VERSION=1.1.0
RAPIDJSON_VERSION=1.0.2
GTEST_VERSION=1.7.0
diff --git a/platform/macos/scripts/deploy-packages.sh b/platform/macos/scripts/deploy-packages.sh
new file mode 100755
index 0000000000..c137401748
--- /dev/null
+++ b/platform/macos/scripts/deploy-packages.sh
@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+set -u
+
+usage() {
+cat <<EOF
+# Usage: sh $0 version target_directory [argument]
+
+# version: The semver version plus optional alpha beta distinction (i.e. {major.minor.patch}{-alpha.N})
+# target_directory: The directory where build output should be placed
+
+# argument:
+# -g: Upload to github
+
+# environment variables and dependencies:
+# - You must run "mbx auth ..." before running
+# - Set GITHUB_TOKEN to a GitHub API access token in your environment to use GITHUB_RELEASE
+# - "wget" is required for downloading the zip files from s3
+# - The "github-release" gem is required to use GITHUB_RELEASE
+EOF
+}
+
+buildPackageStyle() {
+ local package=$1 style=""
+ if [[ ${#} -eq 2 ]]; then
+ style="$2"
+ fi
+ echo "make ${package} ${style}"
+ make ${package}
+ echo "publish ${package} with ${style}"
+ local file_name=""
+ if [ -z ${style} ]
+ then
+ file_name=mapbox-macos-sdk-${PUBLISH_VERSION}.zip
+ else
+ file_name=mapbox-macos-sdk-${PUBLISH_VERSION}-${style}.zip
+ fi
+ echo "compress ${file_name}"
+ cd build/macos/pkg
+ rm -f ../${file_name}
+ zip -r ../${file_name} *
+ cd -
+ if [[ "${GITHUB_RELEASE}" == true ]]; then
+ echo "publish ${file_name} to GitHub"
+ github-release --verbose upload --tag "macos-v${PUBLISH_VERSION}" --name ${file_name} --file "build/macos/${file_name}"
+ fi
+}
+
+if [ ${#} -eq 0 -o ${#} -gt 3 ]; then
+ usage
+ exit 1
+fi
+
+export TRAVIS_REPO_SLUG=mapbox-gl-native
+export PUBLISH_VERSION=$1
+export GITHUB_USER=mapbox
+export GITHUB_REPO=mapbox-gl-native
+export BUILDTYPE=Release
+
+BINARY_DIRECTORY=$2
+PUBLISH_PRE_FLAG=''
+GITHUB_RELEASE=false
+
+echo "Deploying version ${PUBLISH_VERSION}..."
+
+if [[ ${#} -eq 3 && $3 == "-g" ]]; then
+ GITHUB_RELEASE=true
+fi
+
+make clean && make distclean
+
+if [[ "${GITHUB_RELEASE}" == true ]]; then
+ echo "Create GitHub release..."
+ if [[ $( echo ${PUBLISH_VERSION} | awk '/[0-9]-/' ) ]]; then
+ PUBLISH_PRE_FLAG='--pre-release'
+ fi
+ github-release --verbose release --tag "macos-v${PUBLISH_VERSION}" --name "macos-v${PUBLISH_VERSION}" --draft ${PUBLISH_PRE_FLAG}
+fi
+
+buildPackageStyle "xpackage" "symbols"
+buildPackageStyle "xpackage SYMBOLS=NO"
diff --git a/platform/macos/scripts/package.sh b/platform/macos/scripts/package.sh
index a6952e8ab2..c2a015b346 100755
--- a/platform/macos/scripts/package.sh
+++ b/platform/macos/scripts/package.sh
@@ -38,7 +38,7 @@ xcodebuild \
step "Copying dynamic framework into place"
mkdir -p "${OUTPUT}/${NAME}.framework"
-cp -r ${PRODUCTS}/${BUILDTYPE}/${NAME}.framework/* "${OUTPUT}/${NAME}.framework"
+ditto ${PRODUCTS}/${BUILDTYPE}/${NAME}.framework "${OUTPUT}/${NAME}.framework"
if [[ -e ${PRODUCTS}/${BUILDTYPE}/${NAME}.framework.dSYM ]]; then
cp -r ${PRODUCTS}/${BUILDTYPE}/${NAME}.framework.dSYM "${OUTPUT}"
fi
diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h
index 8f1fa40137..7c464fe83a 100644
--- a/platform/macos/src/MGLMapView.h
+++ b/platform/macos/src/MGLMapView.h
@@ -23,10 +23,13 @@ typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
/** Each drawing operation is replaced by a translucent fill. Overlapping
drawing operations appear more prominent to help diagnose overdrawing.
+ @note This option does nothing in Release builds of the SDK.
*/
MGLMapDebugOverdrawVisualizationMask = 1 << 5,
- /** The stencil buffer is shown instead of the color buffer. */
+ /** The stencil buffer is shown instead of the color buffer.
+ @note This option does nothing in Release builds of the SDK.
+ */
MGLMapDebugStencilBufferMask = 1 << 6,
};
@@ -49,7 +52,7 @@ typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
The map view loads scalable vector tiles that conform to the
<a href="https://github.com/mapbox/vector-tile-spec">Mapbox Vector Tile Specification</a>.
It styles them with a style that conforms to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/">Mapbox GL style specification</a>.
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/">Mapbox Style Specification</a>.
Such styles can be designed in
<a href="https://www.mapbox.com/studio/">Mapbox Studio</a> and hosted on
mapbox.com.
@@ -384,6 +387,35 @@ IB_DESIGNABLE
- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated;
/**
+ Sets the visible region so that the map displays the specified annotations.
+
+ Calling this method updates the value in the `visibleCoordinateBounds` property
+ and potentially other properties to reflect the new map region. A small amount
+ of padding is reserved around the edges of the map view. To specify a different
+ amount of padding, use the `-showAnnotations:edgePadding:animated:` method.
+
+ @param annotations The annotations that you want to be visible in the map.
+ @param animated `YES` if you want the map region change to be animated, or `NO`
+ if you want the map to display the new region immediately without animations.
+ */
+- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated;
+
+/**
+ Sets the visible region so that the map displays the specified annotations with
+ the specified amount of padding on each side.
+
+ Calling this method updates the value in the `visibleCoordinateBounds` property
+ and potentially other properties to reflect the new map region.
+
+ @param annotations The annotations that you want to be visible in the map.
+ @param insets The minimum padding (in screen points) around the edges of the
+ map view to keep clear of annotations.
+ @param animated `YES` if you want the map region change to be animated, or `NO`
+ if you want the map to display the new region immediately without animations.
+ */
+- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated;
+
+/**
Returns the camera that best fits the given coordinate bounds.
@param bounds The coordinate bounds to fit to the receiver’s viewport.
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index e02a878bb1..7b0aad1db9 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -126,11 +126,11 @@ mbgl::util::UnitBezier MGLUnitBezierForMediaTimingFunction(CAMediaTimingFunction
/// Converts the given color into an mbgl::Color in calibrated RGB space.
mbgl::Color MGLColorObjectFromNSColor(NSColor *color) {
if (!color) {
- return {{ 0, 0, 0, 0 }};
+ return { 0, 0, 0, 0 };
}
CGFloat r, g, b, a;
[[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&r green:&g blue:&b alpha:&a];
- return {{ (float)r, (float)g, (float)b, (float)a }};
+ return { (float)r, (float)g, (float)b, (float)a };
}
/// Lightweight container for metadata about an annotation, including the annotation itself.
@@ -395,6 +395,10 @@ public:
clickGestureRecognizer.delaysPrimaryMouseButtonEvents = NO;
[self addGestureRecognizer:clickGestureRecognizer];
+ NSClickGestureRecognizer *rightClickGestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightClickGesture:)];
+ rightClickGestureRecognizer.buttonMask = 0x2;
+ [self addGestureRecognizer:rightClickGestureRecognizer];
+
NSClickGestureRecognizer *doubleClickGestureRecognizer = [[NSClickGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleClickGesture:)];
doubleClickGestureRecognizer.numberOfClicksRequired = 2;
doubleClickGestureRecognizer.delaysPrimaryMouseButtonEvents = NO;
@@ -1037,8 +1041,8 @@ public:
mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera];
mbgl::AnimationOptions animationOptions;
if (duration > 0) {
- animationOptions.duration = MGLDurationInSeconds(duration);
- animationOptions.easing = MGLUnitBezierForMediaTimingFunction(function);
+ animationOptions.duration.emplace(MGLDurationInSeconds(duration));
+ animationOptions.easing.emplace(MGLUnitBezierForMediaTimingFunction(function));
}
if (completion) {
animationOptions.transitionFinishFn = [completion]() {
@@ -1359,6 +1363,14 @@ public:
}
}
+/// Right-click to show the context menu.
+- (void)handleRightClickGesture:(NSClickGestureRecognizer *)gestureRecognizer {
+ NSMenu *menu = [self menuForEvent:[NSApp currentEvent]];
+ if (menu) {
+ [NSMenu popUpContextMenu:menu withEvent:[NSApp currentEvent] forView:self];
+ }
+}
+
/// Double-click or double-tap to zoom in.
- (void)handleDoubleClickGesture:(NSClickGestureRecognizer *)gestureRecognizer {
if (!self.zoomEnabled || gestureRecognizer.state != NSGestureRecognizerStateEnded
@@ -1986,6 +1998,38 @@ public:
}
}
+- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated {
+ CGFloat maximumPadding = 100;
+ CGFloat yPadding = (NSHeight(self.bounds) / 5 <= maximumPadding) ? (NSHeight(self.bounds) / 5) : maximumPadding;
+ CGFloat xPadding = (NSWidth(self.bounds) / 5 <= maximumPadding) ? (NSWidth(self.bounds) / 5) : maximumPadding;
+
+ NSEdgeInsets edgeInsets = NSEdgeInsetsMake(yPadding, xPadding, yPadding, xPadding);
+
+ [self showAnnotations:annotations edgePadding:edgeInsets animated:animated];
+}
+
+- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated {
+ if ( ! annotations || ! annotations.count) return;
+
+ mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
+
+ for (id <MGLAnnotation> annotation in annotations)
+ {
+ if ([annotation conformsToProtocol:@protocol(MGLOverlay)])
+ {
+ bounds.extend(MGLLatLngBoundsFromCoordinateBounds(((id <MGLOverlay>)annotation).overlayBounds));
+ }
+ else
+ {
+ bounds.extend(MGLLatLngFromLocationCoordinate2D(annotation.coordinate));
+ }
+ }
+
+ [self setVisibleCoordinateBounds:MGLCoordinateBoundsFromLatLngBounds(bounds)
+ edgePadding:insets
+ animated:animated];
+}
+
/// Returns a popover detailing the annotation.
- (NSPopover *)calloutForAnnotation:(id <MGLAnnotation>)annotation {
NSPopover *callout = [[NSPopover alloc] init];
@@ -2398,7 +2442,7 @@ public:
if (options & mbgl::MapDebugOptions::Collision) {
mask |= MGLMapDebugCollisionBoxesMask;
}
- if (options & mbgl::MapDebugOptions::Wireframe) {
+ if (options & mbgl::MapDebugOptions::Overdraw) {
mask |= MGLMapDebugOverdrawVisualizationMask;
}
if (options & mbgl::MapDebugOptions::StencilClip) {
@@ -2422,7 +2466,7 @@ public:
options |= mbgl::MapDebugOptions::Collision;
}
if (debugMask & MGLMapDebugOverdrawVisualizationMask) {
- options |= mbgl::MapDebugOptions::Wireframe;
+ options |= mbgl::MapDebugOptions::Overdraw;
}
if (debugMask & MGLMapDebugStencilBufferMask) {
options |= mbgl::MapDebugOptions::StencilClip;
diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md
index e1bff6cc28..e08c2fbc3f 100644
--- a/platform/node/CHANGELOG.md
+++ b/platform/node/CHANGELOG.md
@@ -1,25 +1,41 @@
-# 3.2.1
+# 3.3.2 - August 1, 2016
+
+- Fixes Node.js binary publishing to build with `BUILDTYPE=Release` ([#5838](https://github.com/mapbox/mapbox-gl-native/pull/5838))
+
+# 3.3.1 - July 29, 2016
+
+- Fixes `minzoom` and `maxzoom` properties ([#5828](https://github.com/mapbox/mapbox-gl-native/pull/5828))
+- Fixes `RunLoop::runOnce()` to use `UV_RUN_NOWAIT` instead of `UV_RUN_ONCE` (which can block the libuv threadpool) ([#5758](https://github.com/mapbox/mapbox-gl-native/pull/5758))
+- Map debug options 'overdraw' and 'stencil clip' are now disabled (no-ops) in release mode ([#5555](https://github.com/mapbox/mapbox-gl-native/pull/5555))
+
+# 3.3.0 - July 14, 2016
+
+- Adds runtime styling API ([#5318](https://github.com/mapbox/mapbox-gl-native/pull/5318), [#5380](https://github.com/mapbox/mapbox-gl-native/pull/5380), [#5428](https://github.com/mapbox/mapbox-gl-native/pull/5428), [#5429](https://github.com/mapbox/mapbox-gl-native/pull/5429), [#5462](https://github.com/mapbox/mapbox-gl-native/pull/5462), [#5614](https://github.com/mapbox/mapbox-gl-native/pull/5614), [#5670](https://github.com/mapbox/mapbox-gl-native/pull/5670))
+- Adds `BUILDTYPE=Debug` support to `make node` ([#5474](https://github.com/mapbox/mapbox-gl-native/pull/5474))
+- Fixes a memory leak in `NodeRequest` ([#5529](https://github.com/mapbox/mapbox-gl-native/pull/5529))
+
+# 3.2.1 - June 7, 2016
- Fixes a memory leak in raster image data ([#5269](https://github.com/mapbox/mapbox-gl-native/pull/5269))
-# 3.2.0
+# 3.2.0 - June 3, 2016
- Switches to [earcut.hpp](https://github.com/mapbox/earcut.hpp) for tessellation ([#2444](https://github.com/mapbox/mapbox-gl-native/pull/2444))
-# 3.1.3
+# 3.1.3 - May 27, 2016
- Fixes a leak in TexturePoolHolder ([#5141](https://github.com/mapbox/mapbox-gl-native/pull/5141))
- Fixes a bug where a callback would be fired after an AsyncRequest had been cancelled ([#5162](https://github.com/mapbox/mapbox-gl-native/pull/5162))
-# 3.1.2
+# 3.1.2 - April 26, 2016
- Fixes a race condition with animated transitions ([#4836](https://github.com/mapbox/mapbox-gl-native/pull/4836))
-# 3.1.1
+# 3.1.1 - April 11, 2016
- Moves node-pre-gyp from `bundledDependencies` to `preinstall` ([#4680](https://github.com/mapbox/mapbox-gl-native/pull/4680))
-# 3.1.0
+# 3.1.0 - April 8, 2016
- Adds debug render options ([#3840](https://github.com/mapbox/mapbox-gl-native/pull/3840))
- Fixes circle bucket rendering on tile boundaries ([#3764](https://github.com/mapbox/mapbox-gl-native/issues/3764))
@@ -29,45 +45,44 @@
- Fixes intermittent `stencil mask overflow` error ([#962](https://github.com/mapbox/mapbox-gl-native/issues/962))
- Drops support for Node.js v5.x prebuilt binaries due to ongoing npm3 instability ([#4370](https://github.com/mapbox/mapbox-gl-native/issues/4370))
-# 3.0.2
+# 3.0.2 - February 4, 2016
- Fixes a memory leak in `NodeMap::request` ([#3829](https://github.com/mapbox/mapbox-gl-native/pull/3829))
- Increases default max zoom level from 18 to 20 ([#3712](https://github.com/mapbox/mapbox-gl-native/pull/3712))
- Support tiles with non-4096 extents ([#3766](https://github.com/mapbox/mapbox-gl-native/pull/3766))
-# 3.0.1
+# 3.0.1 - January 26, 2016
- Fixes missing icon collision boxes ([#3672](https://github.com/mapbox/mapbox-gl-native/pull/3672))
- Fixes texture filtering to draw sharper icons ([#3669](https://github.com/mapbox/mapbox-gl-native/pull/3669))
-
-# 3.0.0
+# 3.0.0 - January 21, 2016
- Drops support for Node.js v0.10.x ([#3635](https://github.com/mapbox/mapbox-gl-native/pull/3635))
- Fixes label clipping issues with `symbol-avoid-edges` ([#3623](https://github.com/mapbox/mapbox-gl-native/pull/3623))
- Avoids label placement around sharp zig-zags ([#3640](https://github.com/mapbox/mapbox-gl-native/pull/3640))
-# 2.2.2
+# 2.2.2 - January 19, 2016
- Fixes a bug with non-deterministic label placement [#3543](https://github.com/mapbox/mapbox-gl-native/pull/3543)
-# 2.2.1
+# 2.2.1 - January 7, 2016
- Fixes a bug which clipped labels at tile boundaries [#2829](https://github.com/mapbox/mapbox-gl-native/pull/2829)
-# 2.2.0
+# 2.2.0 - December 16, 2015
- Adds support for GeoJSON sources [#2161](https://github.com/mapbox/mapbox-gl-native/pull/2161)
-# 2.1.0
+# 2.1.0 - December 8, 2015
- Adds [`line-offset`](https://github.com/mapbox/mapbox-gl/issues/3) style property support
-# 2.0.1
+# 2.0.1 - November 25, 2015
- Test and publish binaries for Node.js v5.x. ([#3129](https://github.com/mapbox/mapbox-gl-native/pull/3129))
-# 2.0.0
+# 2.0.0 - November 24, 2015
- Integrates Node.js bindings into core mapbox-gl-native project. ([#2179](https://github.com/mapbox/mapbox-gl-native/pull/2179))
- Adds Node.js v4.x and io.js v3.x support. ([#2261](https://github.com/mapbox/mapbox-gl-native/pull/2261))
@@ -89,11 +104,11 @@
- Fade transitions are now ignored to prevent half faded labels. ([#942](https://github.com/mapbox/mapbox-gl-native/pull/942))
- Labels can now line wrap on hyphens and other punctuation. ([#2598](https://github.com/mapbox/mapbox-gl-native/pull/2598))
-# 1.1.3
+# 1.1.3 - June 25, 2015
- Removes deprecated mbgl::Environment from NodeLogObserver.
-# 1.1.2
+# 1.1.2 - June 22, 2015
- Check libuv version semver-ishly, fixes segfaults in Node.js 0.12.x
and io.js.
@@ -101,11 +116,11 @@
render without first loading a style.
- Bumps mbgl submodule to v0.4.0
-# 1.1.1
+# 1.1.1 - June 16, 2015
- Bumps mbgl submodule to v0.3.5
-# 1.1.0
+# 1.1.0 - June 15, 2015
- Adds Node.js v0.12.x and io.js support.
- Adds `map.release()` method for manual cleanup of map resources.
@@ -121,23 +136,23 @@
- Fixes uncaught exception from missing sprites.
- Fixes Unicode glyph range end.
-# 1.0.3
+# 1.0.3 - April 3, 2015
- Fixes crash during garbage collection by assigning FileSource handle
to a v8::Persistent in NodeMap constructor.
-# 1.0.2
+# 1.0.2 - April 2, 2015
- Initialize shared display connection at module load time to avoid
race condition when display connection is initialized on-demand.
-# 1.0.1
+# 1.0.1 - March 19, 2015
- Adapts NodeFileSource around mbgl::Environment additions.
- Adapts to minor changes in mapbox-gl-test-suite.
- Adds tests for gzipped vector tile handling.
- Cleans up documentation.
-# 1.0.0
+# 1.0.0 - February 25, 2015
- Initial release.
diff --git a/platform/node/README.md b/platform/node/README.md
index 9e14d1bbd6..545d87861f 100644
--- a/platform/node/README.md
+++ b/platform/node/README.md
@@ -29,13 +29,25 @@ npm run test-suite
## Rendering a map tile
```js
+var fs = require('fs');
+var path = require('path');
var mbgl = require('mapbox-gl-native');
var sharp = require('sharp');
-var map = new mbgl.Map({ request: function() {} });
+
+var options = {
+ request: function(req, callback) {
+ fs.readFile(path.join(__dirname, 'test', req.url), function(err, data) {
+ callback(err, { data: data });
+ });
+ },
+ ratio: 1
+};
+
+var map = new mbgl.Map(options);
map.load(require('./test/fixtures/style.json'));
-map.render({}, function(err, buffer) {
+map.render({zoom: 0}, function(err, buffer) {
if (err) throw err;
map.release();
diff --git a/platform/node/scripts/after_script.sh b/platform/node/scripts/after_script.sh
index 905055ad11..311511726a 100755
--- a/platform/node/scripts/after_script.sh
+++ b/platform/node/scripts/after_script.sh
@@ -6,17 +6,19 @@ set -o pipefail
JOB=$1
TAG=$2
-if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
- gzip --stdout node_modules/mapbox-gl-test-suite/render-tests/index.html | \
- aws s3 cp --acl public-read --content-encoding gzip --content-type text/html \
- - s3://mapbox/mapbox-gl-native/render-tests/$JOB/index.html
+if [[ ${PUBLISH} ]]; then
+ if [[ "$BUILDTYPE" == "Debug" ]]; then
+ echo "Please run this script in release mode (BUILDTYPE=Release)."
+ exit 1
+ else
+ ./node_modules/.bin/node-pre-gyp package publish info
+ fi
+else
+ if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
+ gzip --stdout node_modules/mapbox-gl-test-suite/render-tests/index.html | \
+ aws s3 cp --acl public-read --content-encoding gzip --content-type text/html \
+ - s3://mapbox/mapbox-gl-native/render-tests/$JOB/index.html
- echo http://mapbox.s3.amazonaws.com/mapbox-gl-native/render-tests/$JOB/index.html
-fi
-
-PACKAGE_JSON_VERSION=$(node -e "console.log(require('./package.json').version)")
-
-if [[ $TAG == node-v${PACKAGE_JSON_VERSION} ]]; then
- ./node_modules/.bin/node-pre-gyp package
- ./node_modules/.bin/node-pre-gyp publish info
+ echo http://mapbox.s3.amazonaws.com/mapbox-gl-native/render-tests/$JOB/index.html
+ fi
fi
diff --git a/platform/node/src/node_conversion.hpp b/platform/node/src/node_conversion.hpp
new file mode 100644
index 0000000000..6ebc846d11
--- /dev/null
+++ b/platform/node/src/node_conversion.hpp
@@ -0,0 +1,113 @@
+#pragma once
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#pragma GCC diagnostic ignored "-Wshadow"
+#include <nan.h>
+#pragma GCC diagnostic pop
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/style/conversion.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+inline bool isUndefined(v8::Local<v8::Value> value) {
+ Nan::HandleScope scope;
+ return value->IsUndefined() || value->IsNull();
+}
+
+inline bool isArray(v8::Local<v8::Value> value) {
+ Nan::HandleScope scope;
+ return value->IsArray();
+}
+
+inline std::size_t arrayLength(v8::Local<v8::Value> value) {
+ Nan::HandleScope scope;
+ return value.As<v8::Array>()->Length();
+}
+
+inline v8::Local<v8::Value> arrayMember(v8::Local<v8::Value> value, std::size_t i) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::Get(value.As<v8::Array>(), i).ToLocalChecked());
+}
+
+inline bool isObject(v8::Local<v8::Value> value) {
+ Nan::HandleScope scope;
+ return value->IsObject();
+}
+
+inline optional<v8::Local<v8::Value>> objectMember(v8::Local<v8::Value> value, const char * name) {
+ Nan::EscapableHandleScope scope;
+ if (!Nan::Has(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked()).FromJust()) {
+ return {};
+ }
+ Nan::MaybeLocal<v8::Value> result = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked());
+ if (result.IsEmpty()) {
+ return {};
+ }
+ return scope.Escape(result.ToLocalChecked());
+}
+
+template <class Fn>
+optional<Error> eachMember(v8::Local<v8::Value> value, Fn&& fn) {
+ Nan::HandleScope scope;
+ v8::Local<v8::Array> names = Nan::GetOwnPropertyNames(Nan::To<v8::Object>(value).ToLocalChecked()).ToLocalChecked();
+ for (uint32_t i = 0; i < names->Length(); ++i) {
+ v8::Local<v8::Value> k = Nan::Get(names, i).ToLocalChecked();
+ v8::Local<v8::Value> v = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), k).ToLocalChecked();
+ optional<Error> result = fn(*Nan::Utf8String(k), v);
+ if (result) {
+ return result;
+ }
+ }
+ return {};
+}
+
+inline optional<bool> toBool(v8::Local<v8::Value> value) {
+ Nan::HandleScope scope;
+ if (!value->IsBoolean()) {
+ return {};
+ }
+ return value->BooleanValue();
+}
+
+inline optional<float> toNumber(v8::Local<v8::Value> value) {
+ Nan::HandleScope scope;
+ if (!value->IsNumber()) {
+ return {};
+ }
+ return value->NumberValue();
+}
+
+inline optional<std::string> toString(v8::Local<v8::Value> value) {
+ Nan::HandleScope scope;
+ if (!value->IsString()) {
+ return {};
+ }
+ return std::string(*Nan::Utf8String(value));
+}
+
+inline optional<Value> toValue(v8::Local<v8::Value> value) {
+ if (value->IsFalse()) {
+ return { false };
+ } else if (value->IsTrue()) {
+ return { true };
+ } else if (value->IsString()) {
+ return { std::string(*Nan::Utf8String(value)) };
+ } else if (value->IsUint32()) {
+ return { std::uint64_t(value->Uint32Value()) };
+ } else if (value->IsInt32()) {
+ return { std::int64_t(value->Int32Value()) };
+ } else if (value->IsNumber()) {
+ return { value->NumberValue() };
+ } else {
+ return {};
+ }
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/platform/node/src/node_feature.cpp b/platform/node/src/node_feature.cpp
index 1a5e31fe97..6620cf1852 100644
--- a/platform/node/src/node_feature.cpp
+++ b/platform/node/src/node_feature.cpp
@@ -6,9 +6,10 @@ using namespace mapbox::geometry;
using Value = mbgl::Value;
using Feature = mbgl::Feature;
+using FeatureIdentifier = mbgl::FeatureIdentifier;
using Geometry = mbgl::Feature::geometry_type;
using GeometryCollection = mapbox::geometry::geometry_collection<double>;
-using Properties = mbgl::Feature::property_map;
+using Properties = mbgl::PropertyMap;
template <class T>
struct ToType {
@@ -76,7 +77,7 @@ public:
};
struct ToValue {
- v8::Local<v8::Value> operator()(std::nullptr_t) {
+ v8::Local<v8::Value> operator()(mbgl::NullValue) {
Nan::EscapableHandleScope scope;
return scope.Escape(Nan::Null());
}
@@ -159,7 +160,7 @@ v8::Local<v8::Object> toJS(const Feature& feature) {
Nan::Set(result, Nan::New("properties").ToLocalChecked(), toJS(feature.properties));
if (feature.id) {
- Nan::Set(result, Nan::New("id").ToLocalChecked(), Nan::New(double(*feature.id)));
+ Nan::Set(result, Nan::New("id").ToLocalChecked(), FeatureIdentifier::visit(*feature.id, ToValue()));
}
return scope.Escape(result);
diff --git a/platform/node/src/node_feature.hpp b/platform/node/src/node_feature.hpp
index 7973ee19d4..8d1eceba38 100644
--- a/platform/node/src/node_feature.hpp
+++ b/platform/node/src/node_feature.hpp
@@ -13,6 +13,6 @@ namespace node_mbgl {
v8::Local<v8::Value> toJS(const mbgl::Value&);
v8::Local<v8::Object> toJS(const mbgl::Feature&);
v8::Local<v8::Object> toJS(const mbgl::Feature::geometry_type&);
-v8::Local<v8::Object> toJS(const mbgl::Feature::property_map&);
+v8::Local<v8::Object> toJS(const mbgl::PropertyMap&);
}
diff --git a/platform/node/src/node_geojson.hpp b/platform/node/src/node_geojson.hpp
new file mode 100644
index 0000000000..ace4c91426
--- /dev/null
+++ b/platform/node/src/node_geojson.hpp
@@ -0,0 +1,14 @@
+#include <mbgl/style/conversion/geojson.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+Result<GeoJSON> convertGeoJSON(const v8::Local<v8::Value>&) {
+ return Error { "not implemented" };
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/platform/node/src/node_log.cpp b/platform/node/src/node_log.cpp
index a741109b27..9872376904 100644
--- a/platform/node/src/node_log.cpp
+++ b/platform/node/src/node_log.cpp
@@ -1,6 +1,8 @@
#include "node_log.hpp"
#include "util/async_queue.hpp"
+#include <mbgl/util/enum.hpp>
+
namespace node_mbgl {
struct NodeLogObserver::LogMessage {
@@ -23,10 +25,10 @@ NodeLogObserver::NodeLogObserver(v8::Local<v8::Object> target)
auto msg = Nan::New<v8::Object>();
Nan::Set(msg, Nan::New("class").ToLocalChecked(),
- Nan::New(mbgl::EventClass(message.event).c_str()).ToLocalChecked());
+ Nan::New(mbgl::Enum<mbgl::Event>::toString(message.event)).ToLocalChecked());
Nan::Set(msg, Nan::New("severity").ToLocalChecked(),
- Nan::New(mbgl::EventSeverityClass(message.severity).c_str()).ToLocalChecked());
+ Nan::New(mbgl::Enum<mbgl::EventSeverity>::toString(message.severity)).ToLocalChecked());
if (message.code != -1) {
Nan::Set(msg, Nan::New("code").ToLocalChecked(),
@@ -40,7 +42,7 @@ NodeLogObserver::NodeLogObserver(v8::Local<v8::Object> target)
v8::Local<v8::Value> argv[] = { Nan::New("message").ToLocalChecked(), msg };
auto handle = Nan::New<v8::Object>(module);
- auto emit = Nan::Get(handle, Nan::New("emit").ToLocalChecked()).ToLocalChecked()->ToObject();
+ auto emit = Nan::To<v8::Object>(Nan::Get(handle, Nan::New("emit").ToLocalChecked()).ToLocalChecked()).ToLocalChecked();
Nan::CallAsFunction(emit, handle, 2, argv);
})) {
Nan::HandleScope scope;
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index 165e27b669..e89089ecbb 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -1,9 +1,14 @@
#include "node_map.hpp"
#include "node_request.hpp"
#include "node_feature.hpp"
+#include "node_conversion.hpp"
+#include "node_geojson.hpp"
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/util/exception.hpp>
+#include <mbgl/style/conversion/source.hpp>
+#include <mbgl/style/conversion/layer.hpp>
+#include <mbgl/style/conversion/filter.hpp>
#include <unistd.h>
@@ -27,9 +32,6 @@ struct NodeMap::RenderOptions {
mbgl::MapDebugOptions debugOptions = mbgl::MapDebugOptions::NoDebug;
};
-////////////////////////////////////////////////////////////////////////////////////////////////
-// Static Node Methods
-
Nan::Persistent<v8::Function> NodeMap::constructor;
static std::shared_ptr<mbgl::HeadlessDisplay> sharedDisplay() {
@@ -48,8 +50,18 @@ NAN_MODULE_INIT(NodeMap::Init) {
tpl->SetClassName(Nan::New("Map").ToLocalChecked());
Nan::SetPrototypeMethod(tpl, "load", Load);
+ Nan::SetPrototypeMethod(tpl, "loaded", Loaded);
Nan::SetPrototypeMethod(tpl, "render", Render);
Nan::SetPrototypeMethod(tpl, "release", Release);
+
+ Nan::SetPrototypeMethod(tpl, "addClass", AddClass);
+ Nan::SetPrototypeMethod(tpl, "addSource", AddSource);
+ Nan::SetPrototypeMethod(tpl, "addLayer", AddLayer);
+ Nan::SetPrototypeMethod(tpl, "removeLayer", RemoveLayer);
+ Nan::SetPrototypeMethod(tpl, "setLayoutProperty", SetLayoutProperty);
+ Nan::SetPrototypeMethod(tpl, "setPaintProperty", SetPaintProperty);
+ Nan::SetPrototypeMethod(tpl, "setFilter", SetFilter);
+
Nan::SetPrototypeMethod(tpl, "dumpDebugLogs", DumpDebugLogs);
Nan::SetPrototypeMethod(tpl, "queryRenderedFeatures", QueryRenderedFeatures);
@@ -99,7 +111,7 @@ NAN_MODULE_INIT(NodeMap::Init) {
* fs.writeFileSync('image.png', image);
* });
*/
-NAN_METHOD(NodeMap::New) {
+void NodeMap::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (!info.IsConstructCall()) {
return Nan::ThrowTypeError("Use the new operator to create new Map objects");
}
@@ -108,7 +120,7 @@ NAN_METHOD(NodeMap::New) {
return Nan::ThrowTypeError("Requires an options object as first argument");
}
- auto options = info[0]->ToObject();
+ auto options = Nan::To<v8::Object>(info[0]).ToLocalChecked();
// Check that 'request' is set. If 'cancel' is set it must be a
// function and if 'ratio' is set it must be a number.
@@ -142,9 +154,12 @@ NAN_METHOD(NodeMap::New) {
std::string StringifyStyle(v8::Local<v8::Value> styleHandle) {
Nan::HandleScope scope;
- v8::Local<v8::Object> JSON = Nan::Get(
- Nan::GetCurrentContext()->Global(),
- Nan::New("JSON").ToLocalChecked()).ToLocalChecked()->ToObject();
+ 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));
}
@@ -164,10 +179,9 @@ std::string StringifyStyle(v8::Local<v8::Value> styleHandle) {
* // providing a string
* map.load(fs.readFileSync('./test/fixtures/style.json', 'utf8'));
*/
-NAN_METHOD(NodeMap::Load) {
+void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
-
- if (!nodeMap->isValid()) return Nan::ThrowError(releasedMessage());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
// Reset the flag as this could be the second time
// we are calling this (being the previous successful).
@@ -198,6 +212,21 @@ NAN_METHOD(NodeMap::Load) {
info.GetReturnValue().SetUndefined();
}
+void NodeMap::Loaded(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ bool loaded = false;
+
+ try {
+ loaded = nodeMap->map->isFullyLoaded();
+ } catch (const std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+
+ info.GetReturnValue().Set(Nan::New(loaded));
+}
+
NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
Nan::HandleScope scope;
@@ -233,16 +262,16 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
}
if (Nan::Has(obj, Nan::New("classes").ToLocalChecked()).FromJust()) {
- auto classes = Nan::Get(obj, Nan::New("classes").ToLocalChecked()).ToLocalChecked()->ToObject().As<v8::Array>();
+ auto classes = Nan::To<v8::Object>(Nan::Get(obj, Nan::New("classes").ToLocalChecked()).ToLocalChecked()).ToLocalChecked().As<v8::Array>();
const int length = classes->Length();
options.classes.reserve(length);
for (int i = 0; i < length; i++) {
- options.classes.push_back(std::string { *Nan::Utf8String(Nan::Get(classes, i).ToLocalChecked()->ToString()) });
+ options.classes.push_back(std::string { *Nan::Utf8String(Nan::To<v8::String>(Nan::Get(classes, i).ToLocalChecked()).ToLocalChecked()) });
}
}
if (Nan::Has(obj, Nan::New("debug").ToLocalChecked()).FromJust()) {
- auto debug = Nan::Get(obj, Nan::New("debug").ToLocalChecked()).ToLocalChecked()->ToObject().As<v8::Object>();
+ auto debug = Nan::To<v8::Object>(Nan::Get(obj, Nan::New("debug").ToLocalChecked()).ToLocalChecked()).ToLocalChecked();
if (Nan::Has(debug, Nan::New("tileBorders").ToLocalChecked()).FromJust()) {
if (Nan::Get(debug, Nan::New("tileBorders").ToLocalChecked()).ToLocalChecked()->BooleanValue()) {
options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::TileBorders;
@@ -263,6 +292,11 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::Collision;
}
}
+ if (Nan::Has(debug, Nan::New("overdraw").ToLocalChecked()).FromJust()) {
+ if (Nan::Get(debug, Nan::New("overdraw").ToLocalChecked()).ToLocalChecked()->BooleanValue()) {
+ options.debugOptions = mbgl::MapDebugOptions::Overdraw;
+ }
+ }
}
return options;
@@ -279,15 +313,14 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
* @param {Array<number>} [options.center=[0,0]] longitude, latitude center
* of the map
* @param {number} [options.bearing=0] rotation
- * @param {Array<string>} [options.classes=[]] GL Style Classes
+ * @param {Array<string>} [options.classes=[]] style classes
* @param {Function} callback
* @returns {undefined} calls callback
* @throws {Error} if stylesheet is not loaded or if map is already rendering
*/
-NAN_METHOD(NodeMap::Render) {
+void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) {
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
-
- if (!nodeMap->isValid()) return Nan::ThrowError(releasedMessage());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
if (info.Length() <= 0 || !info[0]->IsObject()) {
return Nan::ThrowTypeError("First argument must be an options object");
@@ -297,7 +330,7 @@ NAN_METHOD(NodeMap::Render) {
return Nan::ThrowTypeError("Second argument must be a callback function");
}
- if (!nodeMap->isLoaded()) {
+ if (!nodeMap->loaded) {
return Nan::ThrowTypeError("Style is not loaded");
}
@@ -305,7 +338,7 @@ NAN_METHOD(NodeMap::Render) {
return Nan::ThrowError("Map is currently rendering an image");
}
- auto options = ParseOptions(info[0]->ToObject());
+ auto options = ParseOptions(Nan::To<v8::Object>(info[0]).ToLocalChecked());
assert(!nodeMap->callback);
assert(!nodeMap->image.data);
@@ -415,10 +448,9 @@ void NodeMap::renderFinished() {
* @name release
* @returns {undefined}
*/
-NAN_METHOD(NodeMap::Release) {
+void NodeMap::Release(const Nan::FunctionCallbackInfo<v8::Value>& info) {
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
-
- if (!nodeMap->isValid()) return Nan::ThrowError(releasedMessage());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
try {
nodeMap->release();
@@ -430,31 +462,231 @@ NAN_METHOD(NodeMap::Release) {
}
void NodeMap::release() {
- if (!isValid()) throw mbgl::util::Exception(releasedMessage());
-
- valid = false;
+ if (!map) throw mbgl::util::Exception(releasedMessage());
uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *h) {
delete reinterpret_cast<uv_async_t *>(h);
});
- map.reset(nullptr);
+ map.reset();
}
-NAN_METHOD(NodeMap::DumpDebugLogs) {
+void NodeMap::AddClass(const Nan::FunctionCallbackInfo<v8::Value>& info) {
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
- if (!nodeMap->isValid()) return Nan::ThrowError(releasedMessage());
+ if (info.Length() <= 0 || !info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ try {
+ nodeMap->map->addClass(*Nan::Utf8String(info[0]));
+ } catch (const std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
- nodeMap->map->dumpDebugLogs();
info.GetReturnValue().SetUndefined();
}
-NAN_METHOD(NodeMap::QueryRenderedFeatures) {
+void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
- Nan::HandleScope scope;
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
- if (!nodeMap->isValid()) return Nan::ThrowError(releasedMessage());
+ if (info.Length() != 2) {
+ return Nan::ThrowTypeError("Two argument required");
+ }
+
+ if (!info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ Result<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(info[1], *Nan::Utf8String(info[0]));
+ if (!source) {
+ Nan::ThrowTypeError(source.error().message.c_str());
+ return;
+ }
+
+ nodeMap->map->addSource(std::move(*source));
+}
+
+void NodeMap::AddLayer(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");
+ }
+
+ Result<std::unique_ptr<Layer>> layer = convert<std::unique_ptr<Layer>>(info[0]);
+ if (!layer) {
+ Nan::ThrowTypeError(layer.error().message.c_str());
+ return;
+ }
+
+ nodeMap->map->addLayer(std::move(*layer));
+}
+
+void NodeMap::RemoveLayer(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->removeLayer(*Nan::Utf8String(info[0]));
+}
+
+void NodeMap::SetLayoutProperty(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() < 3) {
+ return Nan::ThrowTypeError("Three arguments required");
+ }
+
+ if (!info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ if (!layer) {
+ return Nan::ThrowTypeError("layer not found");
+ }
+
+ if (!info[1]->IsString()) {
+ return Nan::ThrowTypeError("Second argument must be a string");
+ }
+
+ mbgl::optional<Error> error = setLayoutProperty(*layer, *Nan::Utf8String(info[1]), info[2]);
+ if (error) {
+ return Nan::ThrowTypeError(error->message.c_str());
+ }
+
+ nodeMap->map->update(mbgl::Update::RecalculateStyle);
+ info.GetReturnValue().SetUndefined();
+}
+
+void NodeMap::SetPaintProperty(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() < 3) {
+ return Nan::ThrowTypeError("Three arguments required");
+ }
+
+ if (!info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ if (!layer) {
+ return Nan::ThrowTypeError("layer not found");
+ }
+
+ if (!info[1]->IsString()) {
+ return Nan::ThrowTypeError("Second argument must be a string");
+ }
+
+ mbgl::optional<std::string> klass;
+ if (info.Length() == 4 && info[3]->IsString()) {
+ klass = std::string(*Nan::Utf8String(info[3]));
+ }
+
+ mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), info[2], klass);
+ if (error) {
+ return Nan::ThrowTypeError(error->message.c_str());
+ }
+
+ nodeMap->map->update(mbgl::Update::RecalculateStyle | mbgl::Update::Classes);
+ info.GetReturnValue().SetUndefined();
+}
+
+void NodeMap::SetFilter(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() < 2) {
+ return Nan::ThrowTypeError("Two arguments required");
+ }
+
+ if (!info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ if (!layer) {
+ return Nan::ThrowTypeError("layer not found");
+ }
+
+ Filter filter;
+
+ if (!info[1]->IsNull() && !info[1]->IsUndefined()) {
+ Result<Filter> converted = convert<Filter>(info[1]);
+ if (!converted) {
+ Nan::ThrowTypeError(converted.error().message.c_str());
+ return;
+ }
+ filter = std::move(*converted);
+ }
+
+ if (layer->is<FillLayer>()) {
+ layer->as<FillLayer>()->setFilter(filter);
+ info.GetReturnValue().SetUndefined();
+ return;
+ }
+ if (layer->is<LineLayer>()) {
+ layer->as<LineLayer>()->setFilter(filter);
+ info.GetReturnValue().SetUndefined();
+ return;
+ }
+ if (layer->is<SymbolLayer>()) {
+ layer->as<SymbolLayer>()->setFilter(filter);
+ info.GetReturnValue().SetUndefined();
+ return;
+ }
+ if (layer->is<CircleLayer>()) {
+ layer->as<CircleLayer>()->setFilter(filter);
+ info.GetReturnValue().SetUndefined();
+ return;
+ }
+
+ Nan::ThrowTypeError("layer doesn't support filters");
+}
+
+void NodeMap::DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ nodeMap->map->dumpDebugLogs();
+ info.GetReturnValue().SetUndefined();
+}
+
+void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
if (info.Length() <= 0 || !info[0]->IsArray()) {
return Nan::ThrowTypeError("First argument must be an array");
@@ -500,9 +732,6 @@ NAN_METHOD(NodeMap::QueryRenderedFeatures) {
}
}
-////////////////////////////////////////////////////////////////////////////////////////////////
-// Instance
-
NodeMap::NodeMap(v8::Local<v8::Object> options) :
view(sharedDisplay(), [&] {
Nan::HandleScope scope;
@@ -521,20 +750,26 @@ NodeMap::NodeMap(v8::Local<v8::Object> options) :
}
NodeMap::~NodeMap() {
- if (valid) release();
+ if (map) release();
}
-std::unique_ptr<mbgl::AsyncRequest> NodeMap::request(const mbgl::Resource& resource, Callback callback_) {
+std::unique_ptr<mbgl::AsyncRequest> NodeMap::request(const mbgl::Resource& resource, mbgl::FileSource::Callback callback_) {
Nan::HandleScope scope;
- auto requestHandle = NodeRequest::Create(resource, callback_)->ToObject();
- auto request = Nan::ObjectWrap::Unwrap<NodeRequest>(requestHandle);
- auto callbackHandle = Nan::New<v8::Function>(NodeRequest::Respond, requestHandle);
+ v8::Local<v8::Value> argv[] = {
+ Nan::New<v8::External>(this),
+ Nan::New<v8::External>(&callback_)
+ };
- v8::Local<v8::Value> argv[] = { requestHandle, callbackHandle };
- Nan::MakeCallback(handle()->GetInternalField(1)->ToObject(), "request", 2, argv);
+ auto instance = Nan::New(NodeRequest::constructor)->NewInstance(2, argv);
+
+ Nan::Set(instance, Nan::New("url").ToLocalChecked(), Nan::New(resource.url).ToLocalChecked());
+ Nan::Set(instance, Nan::New("kind").ToLocalChecked(), Nan::New<v8::Integer>(resource.kind));
+
+ auto request = Nan::ObjectWrap::Unwrap<NodeRequest>(instance);
+ request->Execute();
return std::make_unique<NodeRequest::NodeAsyncRequest>(request);
}
-} // namespace node_mbgl
+}
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index 6e28eb541e..3673b5f2ba 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -10,8 +10,6 @@
#include <nan.h>
#pragma GCC diagnostic pop
-#include <queue>
-
namespace node_mbgl {
class NodeMap : public Nan::ObjectWrap,
@@ -20,30 +18,36 @@ public:
struct RenderOptions;
class RenderWorker;
+ NodeMap(v8::Local<v8::Object>);
+ ~NodeMap();
+
+ static Nan::Persistent<v8::Function> constructor;
+
static NAN_MODULE_INIT(Init);
- static NAN_METHOD(New);
- static NAN_METHOD(Load);
- static NAN_METHOD(Render);
- static NAN_METHOD(Release);
- static NAN_METHOD(DumpDebugLogs);
- static NAN_METHOD(QueryRenderedFeatures);
+ static void New(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void Load(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void Loaded(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void Render(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void Release(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void AddClass(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void AddSource(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void AddLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void SetFilter(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&);
void startRender(RenderOptions options);
void renderFinished();
void release();
- inline bool isLoaded() { return loaded; }
- inline bool isValid() { return valid; }
-
static RenderOptions ParseOptions(v8::Local<v8::Object>);
- static Nan::Persistent<v8::Function> constructor;
-
- NodeMap(v8::Local<v8::Object>);
- ~NodeMap();
- std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, Callback);
+ std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback);
mbgl::HeadlessView view;
std::unique_ptr<mbgl::Map> map;
@@ -56,7 +60,6 @@ public:
uv_async_t *async;
bool loaded = false;
- bool valid = true;
};
}
diff --git a/platform/node/src/node_mapbox_gl_native.cpp b/platform/node/src/node_mapbox_gl_native.cpp
index 26c49918be..7c5a959553 100644
--- a/platform/node/src/node_mapbox_gl_native.cpp
+++ b/platform/node/src/node_mapbox_gl_native.cpp
@@ -61,10 +61,14 @@ void RegisterModule(v8::Local<v8::Object> target, v8::Local<v8::Object> module)
Nan::New("require").ToLocalChecked()).ToLocalChecked().As<v8::Function>();
v8::Local<v8::Value> eventsString = Nan::New("events").ToLocalChecked();
- v8::Local<v8::Object> events = Nan::Call(require, module, 1, &eventsString).ToLocalChecked()->ToObject();
-
- v8::Local<v8::Object> EventEmitter = Nan::Get(events,
- Nan::New("EventEmitter").ToLocalChecked()).ToLocalChecked()->ToObject();
+ v8::Local<v8::Object> events = Nan::To<v8::Object>(Nan::Call(require, module, 1, &eventsString).ToLocalChecked()).ToLocalChecked();
+
+ v8::Local<v8::Object> EventEmitter = Nan::To<v8::Object>(
+ Nan::Get(
+ events,
+ Nan::New("EventEmitter").ToLocalChecked()
+ ).ToLocalChecked()
+ ).ToLocalChecked();
Nan::SetPrototype(target,
Nan::Get(EventEmitter, Nan::New("prototype").ToLocalChecked()).ToLocalChecked());
diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp
index fa560ed4e7..56015726cb 100644
--- a/platform/node/src/node_request.cpp
+++ b/platform/node/src/node_request.cpp
@@ -1,14 +1,28 @@
#include "node_request.hpp"
+#include "node_map.hpp"
#include <mbgl/storage/response.hpp>
#include <mbgl/util/chrono.hpp>
#include <cmath>
-#include <iostream>
namespace node_mbgl {
-////////////////////////////////////////////////////////////////////////////////////////////////
-// Static Node Methods
+NodeRequest::NodeRequest(
+ NodeMap* target_,
+ mbgl::FileSource::Callback callback_)
+ : AsyncWorker(nullptr),
+ target(target_),
+ callback(callback_) {
+}
+
+NodeRequest::~NodeRequest() {
+ // When this object gets garbage collected, make sure that the
+ // AsyncRequest can no longer attempt to remove the callback function
+ // this object was holding (it can't be fired anymore).
+ if (asyncRequest) {
+ asyncRequest->request = nullptr;
+ }
+}
Nan::Persistent<v8::Function> NodeRequest::constructor;
@@ -19,73 +33,50 @@ NAN_MODULE_INIT(NodeRequest::Init) {
tpl->SetClassName(Nan::New("Request").ToLocalChecked());
constructor.Reset(tpl->GetFunction());
- Nan::Set(target, Nan::New("Request").ToLocalChecked(), tpl->GetFunction());
-}
-NAN_METHOD(NodeRequest::New) {
- auto req = new NodeRequest(*reinterpret_cast<mbgl::FileSource::Callback*>(info[0].As<v8::External>()->Value()));
- req->Wrap(info.This());
- info.GetReturnValue().Set(info.This());
+ // TODO: Remove this from the public JavaScript API
+ Nan::Set(target, Nan::New("Request").ToLocalChecked(), tpl->GetFunction());
}
-v8::Handle<v8::Object> NodeRequest::Create(const mbgl::Resource& resource, mbgl::FileSource::Callback callback) {
- Nan::EscapableHandleScope scope;
+void NodeRequest::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ auto target = reinterpret_cast<NodeMap*>(info[0].As<v8::External>()->Value());
+ auto callback = reinterpret_cast<mbgl::FileSource::Callback*>(info[1].As<v8::External>()->Value());
- v8::Local<v8::Value> argv[] = {
- Nan::New<v8::External>(const_cast<mbgl::FileSource::Callback*>(&callback))
- };
- auto instance = Nan::New(constructor)->NewInstance(1, argv);
+ auto request = new NodeRequest(target, *callback);
- Nan::Set(instance, Nan::New("url").ToLocalChecked(), Nan::New(resource.url).ToLocalChecked());
- Nan::Set(instance, Nan::New("kind").ToLocalChecked(), Nan::New<v8::Integer>(int(resource.kind)));
-
- return scope.Escape(instance);
+ request->Wrap(info.This());
+ info.GetReturnValue().Set(info.This());
}
-NAN_METHOD(NodeRequest::Respond) {
- using Error = mbgl::Response::Error;
-
+void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>& info) {
// Move out of the object so callback() can only be fired once.
auto request = Nan::ObjectWrap::Unwrap<NodeRequest>(info.Data().As<v8::Object>());
auto callback = std::move(request->callback);
if (!callback) {
- info.GetReturnValue().SetUndefined();
- return;
+ return info.GetReturnValue().SetUndefined();
}
mbgl::Response response;
if (info.Length() < 1) {
response.noContent = true;
+ } else if (info[0]->IsObject()) {
+ auto err = Nan::To<v8::Object>(info[0]).ToLocalChecked();
+ auto msg = Nan::New("message").ToLocalChecked();
- } else if (info[0]->BooleanValue()) {
- std::unique_ptr<Nan::Utf8String> message;
-
- // Store the error string.
- if (info[0]->IsObject()) {
- auto err = info[0]->ToObject();
- if (Nan::Has(err, Nan::New("message").ToLocalChecked()).FromJust()) {
- message = std::make_unique<Nan::Utf8String>(
- Nan::Get(err, Nan::New("message").ToLocalChecked())
- .ToLocalChecked()
- ->ToString());
- }
- }
-
- if (!message) {
- message = std::make_unique<Nan::Utf8String>(info[0]->ToString());
+ if (Nan::Has(err, msg).FromJust()) {
+ request->SetErrorMessage(*Nan::Utf8String(
+ Nan::Get(err, msg).ToLocalChecked()));
}
- response.error = std::make_unique<Error>(
- Error::Reason::Other, std::string{ **message, size_t(message->length()) });
-
+ } else if (info[0]->IsString()) {
+ request->SetErrorMessage(*Nan::Utf8String(info[0]));
} else if (info.Length() < 2 || !info[1]->IsObject()) {
return Nan::ThrowTypeError("Second argument must be a response object");
-
} else {
- auto res = info[1]->ToObject();
+ auto res = Nan::To<v8::Object>(info[1]).ToLocalChecked();
if (Nan::Has(res, Nan::New("modified").ToLocalChecked()).FromJust()) {
- const double modified = Nan::Get(res, Nan::New("modified").ToLocalChecked()).ToLocalChecked()->ToNumber()->Value();
+ const double modified = Nan::To<double>(Nan::Get(res, Nan::New("modified").ToLocalChecked()).ToLocalChecked()).FromJust();
if (!std::isnan(modified)) {
response.modified = mbgl::Timestamp{ mbgl::Seconds(
static_cast<mbgl::Seconds::rep>(modified / 1000)) };
@@ -93,7 +84,7 @@ NAN_METHOD(NodeRequest::Respond) {
}
if (Nan::Has(res, Nan::New("expires").ToLocalChecked()).FromJust()) {
- const double expires = Nan::Get(res, Nan::New("expires").ToLocalChecked()).ToLocalChecked()->ToNumber()->Value();
+ const double expires = Nan::To<double>(Nan::Get(res, Nan::New("expires").ToLocalChecked()).ToLocalChecked()).FromJust();
if (!std::isnan(expires)) {
response.expires = mbgl::Timestamp{ mbgl::Seconds(
static_cast<mbgl::Seconds::rep>(expires / 1000)) };
@@ -101,19 +92,16 @@ NAN_METHOD(NodeRequest::Respond) {
}
if (Nan::Has(res, Nan::New("etag").ToLocalChecked()).FromJust()) {
- auto etagHandle = Nan::Get(res, Nan::New("etag").ToLocalChecked()).ToLocalChecked();
- if (etagHandle->BooleanValue()) {
- const Nan::Utf8String etag { etagHandle->ToString() };
- response.etag = std::string { *etag, size_t(etag.length()) };
- }
+ const Nan::Utf8String etag(Nan::Get(res, Nan::New("etag").ToLocalChecked()).ToLocalChecked());
+ response.etag = std::string { *etag, size_t(etag.length()) };
}
if (Nan::Has(res, Nan::New("data").ToLocalChecked()).FromJust()) {
- auto dataHandle = Nan::Get(res, Nan::New("data").ToLocalChecked()).ToLocalChecked();
- if (node::Buffer::HasInstance(dataHandle)) {
+ auto data = Nan::Get(res, Nan::New("data").ToLocalChecked()).ToLocalChecked();
+ if (node::Buffer::HasInstance(data)) {
response.data = std::make_shared<std::string>(
- node::Buffer::Data(dataHandle),
- node::Buffer::Length(dataHandle)
+ node::Buffer::Data(data),
+ node::Buffer::Length(data)
);
} else {
return Nan::ThrowTypeError("Response data must be a Buffer");
@@ -121,40 +109,44 @@ NAN_METHOD(NodeRequest::Respond) {
}
}
+ if (request->ErrorMessage()) {
+ response.error = std::make_unique<mbgl::Response::Error>(
+ mbgl::Response::Error::Reason::Other,
+ request->ErrorMessage()
+ );
+ }
+
// Send the response object to the NodeFileSource object
callback(response);
info.GetReturnValue().SetUndefined();
}
-////////////////////////////////////////////////////////////////////////////////////////////////
-// Instance
+void NodeRequest::Execute() {
+ // Enter a new v8::Context to avoid leaking v8::FunctionTemplate
+ // from Nan::New<v8::Function>
+ v8::Local<v8::Context> context = v8::Context::New(v8::Isolate::GetCurrent());
+ v8::Context::Scope scope(context);
+
+ v8::Local<v8::Value> argv[] = { handle(), Nan::New<v8::Function>(NodeRequest::HandleCallback, handle()) };
+
+ Nan::MakeCallback(Nan::To<v8::Object>(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 2, argv);
+}
NodeRequest::NodeAsyncRequest::NodeAsyncRequest(NodeRequest* request_) : request(request_) {
assert(request);
- // Make sure the JS object has a pointer to this so that it can remove its pointer in the
- // destructor
+
+ // Make sure the JS object has a pointer to this so that it can remove
+ // its pointer in the destructor
request->asyncRequest = this;
}
NodeRequest::NodeAsyncRequest::~NodeAsyncRequest() {
if (request) {
- // Remove the callback function because the AsyncRequest was canceled and we are no longer
- // interested in the result.
+ // Remove the callback function because the AsyncRequest was
+ // canceled and we are no longer interested in the result.
request->callback = {};
request->asyncRequest = nullptr;
}
}
-NodeRequest::NodeRequest(mbgl::FileSource::Callback callback_)
- : callback(callback_) {
-}
-
-NodeRequest::~NodeRequest() {
- // When this object gets garbage collected, make sure that the AsyncRequest can no longer
- // attempt to remove the callback function this object was holding (it can't be fired anymore).
- if (asyncRequest) {
- asyncRequest->request = nullptr;
- }
}
-
-} // namespace node_mbgl
diff --git a/platform/node/src/node_request.hpp b/platform/node/src/node_request.hpp
index 2d307a3f19..f7fce91726 100644
--- a/platform/node/src/node_request.hpp
+++ b/platform/node/src/node_request.hpp
@@ -11,29 +11,31 @@
namespace node_mbgl {
-class NodeFileSource;
-class NodeRequest;
+class NodeMap;
-class NodeRequest : public Nan::ObjectWrap {
+class NodeRequest : public Nan::ObjectWrap,
+ public Nan::AsyncWorker {
public:
- static NAN_MODULE_INIT(Init);
-
- static NAN_METHOD(New);
- static NAN_METHOD(Respond);
-
- static v8::Handle<v8::Object> Create(const mbgl::Resource&, mbgl::FileSource::Callback);
- static Nan::Persistent<v8::Function> constructor;
-
- NodeRequest(mbgl::FileSource::Callback);
- ~NodeRequest();
-
struct NodeAsyncRequest : public mbgl::AsyncRequest {
NodeAsyncRequest(NodeRequest*);
~NodeAsyncRequest() override;
NodeRequest* request;
};
+ NodeRequest(NodeMap*, mbgl::FileSource::Callback);
+ ~NodeRequest();
+
+ static Nan::Persistent<v8::Function> constructor;
+
+ static NAN_MODULE_INIT(Init);
+
+ static void New(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>&);
+
+ void Execute();
+
private:
+ NodeMap* target;
mbgl::FileSource::Callback callback;
NodeAsyncRequest* asyncRequest = nullptr;
};
diff --git a/platform/node/test/memory.test.js b/platform/node/test/memory.test.js
new file mode 100644
index 0000000000..e958f8a29b
--- /dev/null
+++ b/platform/node/test/memory.test.js
@@ -0,0 +1,108 @@
+'use strict';
+
+var fs = require('fs');
+var mbgl = require('../../../lib/mapbox-gl-native');
+var path = require('path');
+var test = require('tape');
+
+var testParams = {
+ mapPoolSize: 10,
+ numRenderings: 100,
+ heapGrowthThreshold: 0,
+ ratio: 2
+};
+
+function readFixture(file) {
+ return fs.readFileSync(path.join('test/fixtures/resources', file));
+}
+
+var style_raster = readFixture('style_raster.json').toString('utf8');
+var style_vector = readFixture('style_vector.json').toString('utf8');
+var sprite_json = readFixture('sprite.json');
+var sprite_png = readFixture('sprite.png');
+var glyph = readFixture('glyphs.pbf');
+var source_raster = readFixture('source_raster.json');
+var source_vector = readFixture('source_vector.json');
+var tile_raster = readFixture('raster.tile');
+var tile_vector = readFixture('vector.tile');
+
+test('Memory', function(t) {
+ // Trigger garbage collection before starting test, then initialize
+ // heap size
+ if (typeof gc === 'function') gc();
+ var lastHeapSize = process.memoryUsage()['heapUsed'];
+
+ var options = {
+ request: function(req, callback) {
+ if (req.url == null) {
+ t.fail('invalid file request');
+ } else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('json')) {
+ callback(null, { data: sprite_json });
+ } else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('png')) {
+ callback(null, { data: sprite_png });
+ } else if (req.url.indexOf('fonts') > -1 && req.url.endsWith('pbf')) {
+ callback(null, { data: glyph });
+ } else if (req.url.endsWith('mapbox.satellite')) {
+ callback(null, { data: source_raster });
+ } else if (req.url.indexOf('satellite') > -1 && (req.url.endsWith('png') || req.url.endsWith('webp'))) {
+ callback(null, { data: tile_raster });
+ } else if (req.url.endsWith('mapbox.mapbox-streets-v7')) {
+ callback(null, { data: source_vector });
+ } else if (req.url.indexOf('streets') > -1 && req.url.endsWith('pbf')) {
+ callback(null, { data: tile_vector });
+ } else {
+ t.fail('unhandled file request: ' + req.url);
+ }
+ },
+ ratio: testParams.ratio,
+ };
+
+ var mapPool = []
+
+ for (var i = 0; i < testParams.mapPoolSize; ++i) {
+ var map = new mbgl.Map(options);
+ mapPool.push(map);
+ }
+
+ var renderCount = 0;
+ var heapGrowth = 0;
+
+ var interval = setInterval(function () {
+ if (mapPool.length == 0) {
+ return;
+ }
+
+ var map = mapPool.shift();
+
+ if (Math.floor(Math.random() * 2)) {
+ map.load(style_raster);
+ } else {
+ map.load(style_vector);
+ }
+
+ map.render({ zoom: 16 }, function(err, pixels) {
+ mapPool.push(map);
+
+ if (renderCount % (testParams.numRenderings / 10) == 0) {
+ // Manually trigger garbage collection
+ if (typeof gc === 'function') gc();
+
+ var currentHeapSize = process.memoryUsage()['heapUsed'];
+
+ // Print some progress, so slow build bots don't timeout.
+ t.comment("Rendering (" + renderCount.toString() +
+ "/" + testParams.numRenderings.toString() + ")");
+
+ heapGrowth = heapGrowth + currentHeapSize - lastHeapSize;
+ lastHeapSize = currentHeapSize;
+ }
+
+ if (++renderCount == testParams.numRenderings) {
+ t.ok(heapGrowth < testParams.heapGrowthThreshold, heapGrowth);
+ t.end();
+
+ clearInterval(interval);
+ }
+ });
+ }, 1);
+});
diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js
index da226a68f4..05c2da0a6d 100644
--- a/platform/node/test/suite_implementation.js
+++ b/platform/node/test/suite_implementation.js
@@ -36,21 +36,45 @@ module.exports = function (style, options, callback) {
options.pitch = style.pitch;
options.debug = {
tileBorders: options.debug,
- collision: options.collisionDebug
+ collision: options.collisionDebug,
+ overdraw: options.showOverdrawInspector,
};
map.load(style);
- map.render(options, function (err, pixels) {
- var results = options.queryGeometry ?
- map.queryRenderedFeatures(options.queryGeometry) :
- [];
- map.release();
- if (timedOut) return;
- clearTimeout(watchdog);
- callback(err, pixels, results.map(prepareFeatures));
+ applyOperations(options.operations, function() {
+ map.render(options, function (err, pixels) {
+ var results = options.queryGeometry ?
+ map.queryRenderedFeatures(options.queryGeometry) :
+ [];
+ map.release();
+ if (timedOut) return;
+ clearTimeout(watchdog);
+ callback(err, pixels, results.map(prepareFeatures));
+ });
});
+ function applyOperations(operations, callback) {
+ var operation = operations && operations[0];
+ if (!operations || operations.length === 0) {
+ callback();
+
+ } else if (operation[0] === 'wait') {
+ var wait = function() {
+ if (map.loaded()) {
+ applyOperations(operations.slice(1), callback);
+ } else {
+ map.render(options, wait);
+ }
+ };
+ wait();
+
+ } else {
+ map[operation[0]].apply(map, operation.slice(1));
+ applyOperations(operations.slice(1), callback);
+ }
+ }
+
function prepareFeatures(r) {
delete r.layer;
return r;
diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp
index 35f56a0bef..bc83e2d972 100644
--- a/platform/qt/app/mapwindow.cpp
+++ b/platform/qt/app/mapwindow.cpp
@@ -1,7 +1,9 @@
#include "mapwindow.hpp"
#include <QApplication>
+#include <QColor>
#include <QDebug>
+#include <QFile>
#include <QIcon>
#include <QKeyEvent>
#include <QMouseEvent>
@@ -67,13 +69,49 @@ void MapWindow::changeStyle()
void MapWindow::keyPressEvent(QKeyEvent *ev)
{
+ static const QMapbox::TransitionOptions transition { 300, {} };
+
switch (ev->key()) {
case Qt::Key_S:
changeStyle();
break;
+ case Qt::Key_L: {
+ m_map.setPaintProperty("water", "fill-color", QColor(255, 0, 0));
+ m_map.setPaintProperty("building", "fill-color", "red");
+ m_map.setPaintProperty("road-secondary-tertiary", "line-color", "red");
+
+ m_map.setLayoutProperty("road-label-small", "symbol-placement", "point");
+ m_map.setLayoutProperty("road-label-medium", "symbol-placement", "point");
+ m_map.setLayoutProperty("road-label-large", "symbol-placement", "point");
+
+ QFile geojson(":source.geojson");
+ geojson.open(QIODevice::ReadOnly);
+
+ QVariantMap testSource;
+ testSource["type"] = "geojson";
+ testSource["data"] = geojson.readAll();
+
+ m_map.addSource("testSource", testSource);
+
+ QVariantMap testLayer;
+ testLayer["id"] = "testLayer";
+ testLayer["type"] = "fill";
+ testLayer["source"] = "testSource";
+
+ m_map.addLayer(testLayer);
+ m_map.setPaintProperty("testLayer", "fill-color", QColor("blue"));
+ }
+ break;
case Qt::Key_Tab:
m_map.cycleDebugOptions();
break;
+ case Qt::Key_R:
+ if (m_map.hasClass("night")) {
+ m_map.removeClass("night", transition);
+ } else {
+ m_map.addClass("night", transition);
+ }
+ break;
default:
break;
}
diff --git a/platform/qt/app/qmapboxgl.gypi b/platform/qt/app/qmapboxgl.gypi
index 62a2939ad2..22b4e1281e 100644
--- a/platform/qt/app/qmapboxgl.gypi
+++ b/platform/qt/app/qmapboxgl.gypi
@@ -17,6 +17,7 @@
'main.cpp',
'mapwindow.cpp',
'mapwindow.hpp',
+ 'source.qrc',
],
'include_dirs': [
@@ -30,6 +31,8 @@
'<@(qt_gui_cflags)',
'<@(qt_opengl_cflags)',
'-fPIC',
+ # Qt4 generates code with unused variables.
+ '-Wno-unused-variable',
],
'ldflags': [
'<@(opengl_ldflags)',
diff --git a/platform/qt/app/source.geojson b/platform/qt/app/source.geojson
new file mode 100644
index 0000000000..f639c1913e
--- /dev/null
+++ b/platform/qt/app/source.geojson
@@ -0,0 +1,253 @@
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 69.2578125,
+ -40.17887331434695
+ ],
+ [
+ 69.2578125,
+ -10.141931686131018
+ ],
+ [
+ 110.74218749999999,
+ -10.141931686131018
+ ],
+ [
+ 110.74218749999999,
+ -40.17887331434695
+ ],
+ [
+ 69.2578125,
+ -40.17887331434695
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ -71.71875,
+ -33.43144133557529
+ ],
+ [
+ -71.71875,
+ 20.3034175184893
+ ],
+ [
+ -0.703125,
+ 20.3034175184893
+ ],
+ [
+ -0.703125,
+ -33.43144133557529
+ ],
+ [
+ -71.71875,
+ -33.43144133557529
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 97.3828125,
+ 38.548165423046584
+ ],
+ [
+ 97.3828125,
+ 65.2198939361321
+ ],
+ [
+ 155.0390625,
+ 65.2198939361321
+ ],
+ [
+ 155.0390625,
+ 38.548165423046584
+ ],
+ [
+ 97.3828125,
+ 38.548165423046584
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 24.93617534637451,
+ 60.17058143435276
+ ],
+ [
+ 24.93617534637451,
+ 60.17149933938796
+ ],
+ [
+ 24.93808507919311,
+ 60.17149933938796
+ ],
+ [
+ 24.93808507919311,
+ 60.17058143435276
+ ],
+ [
+ 24.93617534637451,
+ 60.17058143435276
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 24.94398593902588,
+ 60.170346617317186
+ ],
+ [
+ 24.94398593902588,
+ 60.171456646699184
+ ],
+ [
+ 24.947032928466797,
+ 60.171456646699184
+ ],
+ [
+ 24.947032928466797,
+ 60.170346617317186
+ ],
+ [
+ 24.94398593902588,
+ 60.170346617317186
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 24.938321113586426,
+ 60.16800902895082
+ ],
+ [
+ 24.938321113586426,
+ 60.169204528711674
+ ],
+ [
+ 24.941797256469727,
+ 60.169204528711674
+ ],
+ [
+ 24.941797256469727,
+ 60.16800902895082
+ ],
+ [
+ 24.938321113586426,
+ 60.16800902895082
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 24.939115047454834,
+ 60.17280143974311
+ ],
+ [
+ 24.939115047454834,
+ 60.17387936902026
+ ],
+ [
+ 24.94175434112549,
+ 60.17387936902026
+ ],
+ [
+ 24.94175434112549,
+ 60.17280143974311
+ ],
+ [
+ 24.939115047454834,
+ 60.17280143974311
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {},
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 24.944372177124023,
+ 60.16034398818875
+ ],
+ [
+ 24.944372177124023,
+ 60.16837195311943
+ ],
+ [
+ 24.963769912719727,
+ 60.16837195311943
+ ],
+ [
+ 24.963769912719727,
+ 60.16034398818875
+ ],
+ [
+ 24.944372177124023,
+ 60.16034398818875
+ ]
+ ]
+ ]
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/platform/qt/app/source.qrc b/platform/qt/app/source.qrc
new file mode 100644
index 0000000000..60282b7178
--- /dev/null
+++ b/platform/qt/app/source.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>source.geojson</file>
+ </qresource>
+</RCC>
diff --git a/platform/qt/bitrise-qt4.yml b/platform/qt/bitrise-qt4.yml
index 24e0f44932..aeff150ee7 100644
--- a/platform/qt/bitrise-qt4.yml
+++ b/platform/qt/bitrise-qt4.yml
@@ -34,7 +34,7 @@ workflows:
brew linkapps qt
export BUILDTYPE=Debug
make qt-app
- make test-qt
+ make test-qt-*
- is_debug: 'yes'
- slack:
title: Post to Slack
diff --git a/platform/qt/bitrise-qt5.yml b/platform/qt/bitrise-qt5.yml
index 18fa44dd2c..a612263e48 100644
--- a/platform/qt/bitrise-qt5.yml
+++ b/platform/qt/bitrise-qt5.yml
@@ -35,7 +35,7 @@ workflows:
export BUILDTYPE=Debug
make qt-app
make qt-qml-app
- make test-qt
+ make test-qt-*
- is_debug: 'yes'
- slack:
title: Post to Slack
diff --git a/platform/qt/include/QQuickMapboxGLStyleProperty b/platform/qt/include/QQuickMapboxGLStyleProperty
new file mode 100644
index 0000000000..8e61ec1d4a
--- /dev/null
+++ b/platform/qt/include/QQuickMapboxGLStyleProperty
@@ -0,0 +1 @@
+#include "qquickmapboxglstyleproperty.hpp"
diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp
index 3200da2729..60ef0a306f 100644
--- a/platform/qt/include/qmapbox.hpp
+++ b/platform/qt/include/qmapbox.hpp
@@ -11,8 +11,7 @@
namespace QMapbox {
typedef QPair<double, double> Coordinate;
-typedef QList<Coordinate> Coordinates;
-typedef QList<Coordinates> CoordinateSegments;
+typedef QList<Coordinate> LineString;
typedef QPair<Coordinate, double> CoordinateZoom;
@@ -22,7 +21,7 @@ typedef QList<AnnotationID> AnnotationIDs;
typedef QPair<Coordinate, QString> PointAnnotation;
// FIXME: We need to add support for custom style properties
-typedef QPair<CoordinateSegments, QString> ShapeAnnotation;
+typedef QPair<LineString, QString> ShapeAnnotation;
enum NetworkMode {
Online, // Default
@@ -54,6 +53,11 @@ struct Q_DECL_EXPORT CustomLayerRenderParameters {
double altitude;
};
+struct Q_DECL_EXPORT TransitionOptions {
+ QVariant duration; // qint64
+ QVariant delay; // qint64
+};
+
typedef void (*CustomLayerInitializeFunction)(void* context) ;
typedef void (*CustomLayerRenderFunction)(void* context, const CustomLayerRenderParameters&);
typedef void (*CustomLayerDeinitializeFunction)(void* context);
diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp
index 8ce6b02f47..e95dc84550 100644
--- a/platform/qt/include/qmapboxgl.hpp
+++ b/platform/qt/include/qmapboxgl.hpp
@@ -159,9 +159,12 @@ public:
void setGestureInProgress(bool inProgress);
void addClass(const QString &);
+ void addClass(const QString &, const QMapbox::TransitionOptions &);
void removeClass(const QString &);
+ void removeClass(const QString &, const QMapbox::TransitionOptions &);
bool hasClass(const QString &) const;
void setClasses(const QStringList &);
+ void setClasses(const QStringList &, const QMapbox::TransitionOptions &);
QStringList getClasses() const;
QMapbox::AnnotationID addPointAnnotation(const QMapbox::PointAnnotation &);
@@ -171,6 +174,9 @@ public:
void removeAnnotation(QMapbox::AnnotationID);
+ void setLayoutProperty(const QString &layer, const QString &property, const QVariant &value);
+ void setPaintProperty(const QString &layer, const QString &property, const QVariant &value, const QString &klass = QString());
+
bool isRotating() const;
bool isScaling() const;
bool isPanning() const;
@@ -193,13 +199,19 @@ public:
void setMargins(const QMargins &margins);
QMargins margins() const;
+ void addSource(const QString& sourceID, const QVariant& value);
+ void removeSource(const QString& sourceID);
+
void addCustomLayer(const QString &id,
QMapbox::CustomLayerInitializeFunction,
QMapbox::CustomLayerRenderFunction,
QMapbox::CustomLayerDeinitializeFunction,
void* context,
char* before = NULL);
- void removeCustomLayer(const QString& id);
+ void addLayer(const QVariant &value);
+ void removeLayer(const QString &id);
+
+ void setFilter(const QString &layer, const QVariant &filter);
public slots:
void render();
diff --git a/platform/qt/include/qquickmapboxgl.hpp b/platform/qt/include/qquickmapboxgl.hpp
index 6c2847fe59..c651afef00 100644
--- a/platform/qt/include/qquickmapboxgl.hpp
+++ b/platform/qt/include/qquickmapboxgl.hpp
@@ -34,6 +34,19 @@ class Q_DECL_EXPORT QQuickMapboxGL : public QQuickFramebufferObject
Q_PROPERTY(qreal pitch READ pitch WRITE setPitch NOTIFY pitchChanged)
public:
+ struct LayoutPropertyChange {
+ QString layer;
+ QString property;
+ QVariant value;
+ };
+
+ struct PaintPropertyChange {
+ QString layer;
+ QString property;
+ QVariant value;
+ QString klass;
+ };
+
QQuickMapboxGL(QQuickItem *parent = 0);
virtual ~QQuickMapboxGL();
@@ -69,6 +82,9 @@ public:
Q_INVOKABLE void pan(int dx, int dy);
+ QList<LayoutPropertyChange>& layoutPropertyChanges() { return m_layoutChanges; }
+ QList<PaintPropertyChange>& paintPropertyChanges() { return m_paintChanges; }
+
// MapboxGL QML Type interface.
void setStyle(const QString &style);
QString style() const;
@@ -81,6 +97,9 @@ public:
QPointF swapPan();
+ void setLayoutProperty(const QString &layer, const QString &property, const QVariant &value);
+ void setPaintProperty(const QString &layer, const QString &property, const QVariant &value, const QString &klass = QString());
+
enum SyncState {
NothingNeedsSync = 0,
ZoomNeedsSync = 1 << 0,
@@ -122,6 +141,9 @@ private:
QGeoCoordinate m_center;
QGeoShape m_visibleRegion;
+ QColor m_color;
+ QList<LayoutPropertyChange> m_layoutChanges;
+ QList<PaintPropertyChange> m_paintChanges;
QString m_style;
qreal m_bearing = 0;
diff --git a/platform/qt/include/qquickmapboxglstyleproperty.hpp b/platform/qt/include/qquickmapboxglstyleproperty.hpp
new file mode 100644
index 0000000000..99832eed85
--- /dev/null
+++ b/platform/qt/include/qquickmapboxglstyleproperty.hpp
@@ -0,0 +1,75 @@
+#ifndef QQUICKMAPBOXGLSTYLEPROPERTY_H
+#define QQUICKMAPBOXGLSTYLEPROPERTY_H
+
+#include <QQuickItem>
+
+class Q_DECL_EXPORT QQuickMapboxGLStyleProperty : public QQuickItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString layer READ layer WRITE setLayer NOTIFY layerChanged)
+ Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged)
+ Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged)
+
+public:
+ virtual ~QQuickMapboxGLStyleProperty() {}
+
+ // QQuickItem implementation
+ virtual void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &);
+
+ void setLayer(const QString &);
+ QString layer() const;
+
+ void setProperty(const QString &);
+ QString property() const;
+
+ void setValue(const QVariant &);
+ QVariant value() const;
+
+signals:
+ void layerChanged(const QString &);
+ void propertyChanged(const QString &);
+ void valueChanged(const QVariant &);
+
+protected:
+ QQuickMapboxGLStyleProperty(QQuickItem *parent);
+ virtual void updateParent() = 0;
+
+ QVariant m_layer;
+ QVariant m_property;
+ QVariant m_value;
+};
+
+class Q_DECL_EXPORT QQuickMapboxGLLayoutStyleProperty : public QQuickMapboxGLStyleProperty
+{
+public:
+ QQuickMapboxGLLayoutStyleProperty(QQuickItem *parent = 0);
+ virtual ~QQuickMapboxGLLayoutStyleProperty() {}
+
+protected:
+ virtual void updateParent();
+};
+
+class Q_DECL_EXPORT QQuickMapboxGLPaintStyleProperty : public QQuickMapboxGLStyleProperty
+{
+ Q_OBJECT
+ Q_PROPERTY(QString styleClass READ styleClass WRITE setStyleClass NOTIFY classChanged)
+
+public:
+ QQuickMapboxGLPaintStyleProperty(QQuickItem *parent = 0);
+ virtual ~QQuickMapboxGLPaintStyleProperty() {}
+
+ void setStyleClass(const QString &);
+ QString styleClass() const;
+
+signals:
+ void classChanged(const QString &);
+
+protected:
+ virtual void updateParent();
+
+private:
+ QVariant m_class;
+};
+
+#endif // QQUICKMAPBOXGLSTYLEPROPERTY_H
diff --git a/platform/qt/platform.gyp b/platform/qt/platform.gyp
index 2a0e1c57bc..cb46aab07e 100644
--- a/platform/qt/platform.gyp
+++ b/platform/qt/platform.gyp
@@ -46,11 +46,10 @@
'../default/mbgl/storage/offline_download.cpp',
'../default/online_file_source.cpp',
'../default/sqlite3.cpp',
- '../default/string_stdlib.cpp',
- '../default/thread.cpp',
'include/qmapbox.hpp',
'include/qmapboxgl.hpp',
'include/qquickmapboxgl.hpp',
+ 'include/qquickmapboxglstyleproperty.hpp',
'qmapbox.qrc',
'src/async_task.cpp',
'src/async_task_impl.hpp',
@@ -63,10 +62,12 @@
'src/qmapboxgl.cpp',
'src/qmapboxgl_p.hpp',
'src/qquickmapboxgl.cpp',
+ 'src/qquickmapboxglstyleproperty.cpp',
'src/qquickmapboxglrenderer.cpp',
'src/qquickmapboxglrenderer.hpp',
'src/run_loop.cpp',
'src/run_loop_impl.hpp',
+ 'src/string_stdlib.cpp',
'src/timer.cpp',
'src/timer_impl.hpp',
],
@@ -74,6 +75,8 @@
'variables': {
'cflags': [
'<@(boost_cflags)',
+ '<@(geojson_cflags)',
+ '<@(libjpeg-turbo_cflags)',
'<@(nunicode_cflags)',
'<@(opengl_cflags)',
'<@(qt_core_cflags)',
@@ -93,6 +96,8 @@
'<@(zlib_ldflags)',
],
'libraries': [
+ '<@(geojson_static_libs)',
+ '<@(libjpeg-turbo_static_libs)',
'<@(nunicode_static_libs)',
'<@(sqlite_static_libs)',
'<@(zlib_static_libs)',
@@ -126,6 +131,12 @@
'<@(webp_static_libs)',
],
},
+ }, {
+ 'variables': {
+ 'cflags': [
+ '-DQT_IMAGE_DECODERS',
+ ],
+ },
}],
['<(qt_version_major) == 4', {
'variables': {
@@ -158,16 +169,25 @@
['OS == "mac"', {
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags)' ],
- }
+ },
+ 'sources': [
+ '../darwin/src/nsthread.mm',
+ ],
}, {
'cflags_cc': [ '<@(cflags)' ],
+ 'sources': [
+ '../default/thread.cpp',
+ ],
}]
],
'link_settings': {
'conditions': [
['OS == "mac"', {
- 'libraries': [ '<@(libraries)' ],
+ 'libraries': [
+ '<@(libraries)',
+ '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+ ],
'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
}, {
'libraries': [ '<@(libraries)', '<@(ldflags)' ],
diff --git a/platform/qt/qmlapp/main.cpp b/platform/qt/qmlapp/main.cpp
index 1b07c699cc..0a376c6bdd 100644
--- a/platform/qt/qmlapp/main.cpp
+++ b/platform/qt/qmlapp/main.cpp
@@ -4,6 +4,7 @@
#include <qqml.h>
#include <QQuickMapboxGL>
+#include <QQuickMapboxGLStyleProperty>
int main(int argc, char *argv[])
{
@@ -13,7 +14,9 @@ int main(int argc, char *argv[])
app.setWindowIcon(QIcon(":icon.png"));
#endif
- qmlRegisterType<QQuickMapboxGL>("QQuickMapboxGL", 1, 0, "QQuickMapboxGL");
+ qmlRegisterType<QQuickMapboxGL>("QQuickMapboxGL", 1, 0, "MapboxMap");
+ qmlRegisterType<QQuickMapboxGLLayoutStyleProperty>("QQuickMapboxGL", 1, 0, "MapboxLayoutStyleProperty");
+ qmlRegisterType<QQuickMapboxGLPaintStyleProperty>("QQuickMapboxGL", 1, 0, "MapboxPaintStyleProperty");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
diff --git a/platform/qt/qmlapp/main.qml b/platform/qt/qmlapp/main.qml
index a8629e94f6..6edfd7490e 100644
--- a/platform/qt/qmlapp/main.qml
+++ b/platform/qt/qmlapp/main.qml
@@ -3,6 +3,7 @@ import QtPositioning 5.0
import QtQuick 2.0
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
+import QtQuick.Dialogs 1.0
import QQuickMapboxGL 1.0
@@ -12,6 +13,39 @@ ApplicationWindow {
height: 768
visible: true
+ ColorDialog {
+ id: landColorDialog
+ title: "Land color"
+ onCurrentColorChanged: { mapStreets.color = currentColor }
+ }
+
+ ColorDialog {
+ id: waterColorDialog
+ title: "Water color"
+ onCurrentColorChanged: { waterColor.value = currentColor }
+ }
+
+ MapboxLayoutStyleProperty {
+ parent: mapStreets
+ layer: "road-label-large"
+ property: "visibility"
+ value: roadLabel.checked ? "visible" : "none"
+ }
+
+ MapboxLayoutStyleProperty {
+ parent: mapStreets
+ layer: "road-label-medium"
+ property: "visibility"
+ value: roadLabel.checked ? "visible" : "none"
+ }
+
+ MapboxLayoutStyleProperty {
+ parent: mapStreets
+ layer: "road-label-small"
+ property: "visibility"
+ value: roadLabel.checked ? "visible" : "none"
+ }
+
RowLayout {
anchors.fill: parent
anchors.margins: 50
@@ -35,7 +69,7 @@ ApplicationWindow {
front: Rectangle {
anchors.fill: parent
- QQuickMapboxGL {
+ MapboxMap {
id: mapStreets
anchors.fill: parent
@@ -51,9 +85,15 @@ ApplicationWindow {
bearing: bearingSlider.value
pitch: pitchSlider.value
- color: "red"
+ color: landColorDialog.color
copyrightsVisible: true
+ MapboxPaintStyleProperty {
+ id: waterColor
+ layer: "water"
+ property: "fill-color"
+ }
+
Image {
id: logo
@@ -113,7 +153,7 @@ ApplicationWindow {
back: Rectangle {
anchors.fill: parent
- QQuickMapboxGL {
+ MapboxMap {
id: mapSatellite
anchors.fill: parent
@@ -184,37 +224,66 @@ ApplicationWindow {
}
}
- Slider {
- id: bearingSlider
+ ColumnLayout {
+ RowLayout {
+ anchors.margins: 50
+ spacing: anchors.margins
- Layout.fillHeight: true
- orientation: Qt.Vertical
+ Slider {
+ id: bearingSlider
- value: 0
- minimumValue: 0
- maximumValue: 180
- }
+ Layout.fillHeight: true
+ orientation: Qt.Vertical
- Slider {
- id: pitchSlider
+ value: 0
+ minimumValue: 0
+ maximumValue: 180
+ }
- Layout.fillHeight: true
- orientation: Qt.Vertical
+ Slider {
+ id: pitchSlider
- value: 0
- minimumValue: 0
- maximumValue: 60
- }
+ Layout.fillHeight: true
+ orientation: Qt.Vertical
- Slider {
- id: flipSlider
+ value: 0
+ minimumValue: 0
+ maximumValue: 60
+ }
- Layout.fillHeight: true
- orientation: Qt.Vertical
+ Slider {
+ id: flipSlider
+
+ Layout.fillHeight: true
+ orientation: Qt.Vertical
+
+ value: 0
+ minimumValue: 0
+ maximumValue: 180
+ }
+ }
+
+ Button {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ text: "Select land color"
+ onClicked: landColorDialog.open()
+ }
- value: 0
- minimumValue: 0
- maximumValue: 180
+ Button {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ text: "Select water color"
+ onClicked: waterColorDialog.open()
+ }
+
+ CheckBox {
+ id: roadLabel
+ anchors.left: parent.left
+ anchors.right: parent.right
+ text: "Toggle road label"
+ checked: true
+ }
}
}
}
diff --git a/platform/qt/scripts/configure.sh b/platform/qt/scripts/configure.sh
index 4b855f7c58..23f4b0bf52 100644
--- a/platform/qt/scripts/configure.sh
+++ b/platform/qt/scripts/configure.sh
@@ -1,15 +1,17 @@
#!/usr/bin/env bash
-CXX11ABI=$(scripts/check-cxx11abi.sh)
+CXX11ABI=${CXX11ABI:-$(scripts/check-cxx11abi.sh)}
UNIQUE_RESOURCE_VERSION=dev
PROTOZERO_VERSION=1.3.0
BOOST_VERSION=1.60.0
-GEOMETRY_VERSION=0.5.0
-GEOJSONVT_VERSION=4.1.2${CXX11ABI:-}
+GEOMETRY_VERSION=0.8.0
+GEOJSON_VERSION=0.1.4${CXX11ABI:-}
+GEOJSONVT_VERSION=6.1.2
+SUPERCLUSTER_VERSION=0.2.0
+KDBUSH_VERSION=0.1.1
GTEST_VERSION=1.7.0${CXX11ABI:-}
LIBJPEG_TURBO_VERSION=1.4.2
-NUNICODE_VERSION=1.6
PIXELMATCH_VERSION=0.9.0
RAPIDJSON_VERSION=1.0.2
SQLITE_VERSION=3.9.1
@@ -19,12 +21,15 @@ WEBP_VERSION=0.5.0
EARCUT_VERSION=0.11
function print_default_flags {
- CONFIG+=" 'cflags': $(quote_flags -fvisibility=hidden),"$LN
+ CONFIG+=" 'cflags': $(quote_flags -fvisibility=hidden -D__QT__),"$LN
}
if [ "$MASON_PLATFORM" == "osx" ]; then
+ # XXX: Argh, adding the __QT__ flag here because GYP for OSX does
+ # not respect the `cflags` variable above and we need it to reach
+ # the utests somehow. Gonna be fixed properly when we move to CMake.
function print_opengl_flags {
- CONFIG+=" 'opengl_cflags%': [],"$LN
+ CONFIG+=" 'opengl_cflags%': ['-D__QT__'],"$LN
CONFIG+=" 'opengl_ldflags%': ['-framework OpenGL', '-framework CoreFoundation'],"$LN
}
else
@@ -37,7 +42,12 @@ fi
function print_qt_flags {
mason install Qt system
- QT_VERSION_MAJOR=$(qmake -query QT_VERSION | cut -d. -f1)
+ QMAKE="qmake"
+ if [ ! $(which ${QMAKE} 2>/dev/null) ]; then
+ QMAKE="qmake-qt5"
+ fi
+
+ QT_VERSION_MAJOR=$(${QMAKE} -query QT_VERSION | cut -d. -f1)
CONFIG+=" 'qt_version_major%': ['${QT_VERSION_MAJOR}'],"$LN
CONFIG+=" 'qt_image_decoders%': [0],"$LN
diff --git a/platform/qt/src/async_task_impl.hpp b/platform/qt/src/async_task_impl.hpp
index 171990b15c..ea86079a90 100644
--- a/platform/qt/src/async_task_impl.hpp
+++ b/platform/qt/src/async_task_impl.hpp
@@ -16,7 +16,7 @@ class AsyncTask::Impl : public QObject {
Q_OBJECT
public:
- Impl(std::function<void()>&& fn);
+ Impl(std::function<void()> &&);
void maySend();
@@ -34,5 +34,5 @@ private:
};
-}
-}
+} // namespace util
+} // namespace mbgl
diff --git a/platform/qt/src/http_file_source.hpp b/platform/qt/src/http_file_source.hpp
index 274b464026..777228d191 100644
--- a/platform/qt/src/http_file_source.hpp
+++ b/platform/qt/src/http_file_source.hpp
@@ -24,14 +24,14 @@ public:
Impl();
virtual ~Impl() = default;
- void request(HTTPRequest*);
- void cancel(HTTPRequest*);
+ void request(HTTPRequest *);
+ void cancel(HTTPRequest *);
public slots:
- void replyFinish(QNetworkReply* reply);
+ void replyFinish(QNetworkReply *);
private:
- QMap<QUrl, QPair<QNetworkReply*, QVector<HTTPRequest*>>> m_pending;
+ QMap<QUrl, QPair<QNetworkReply *, QVector<HTTPRequest *>>> m_pending;
QNetworkAccessManager *m_manager;
QSslConfiguration m_ssl;
};
diff --git a/platform/qt/src/http_request.cpp b/platform/qt/src/http_request.cpp
index 7290f5a974..ac7d969e36 100644
--- a/platform/qt/src/http_request.cpp
+++ b/platform/qt/src/http_request.cpp
@@ -39,7 +39,8 @@ QNetworkRequest HTTPRequest::networkRequest() const
req.setRawHeader("User-Agent", "MapboxGL/1.0 [Qt]");
if (m_resource.priorEtag) {
- req.setRawHeader("If-None-Match", QByteArray(m_resource.priorEtag->data(), m_resource.priorEtag->size()));
+ const auto etag = m_resource.priorEtag;
+ req.setRawHeader("If-None-Match", QByteArray(etag->data(), etag->size()));
} else if (m_resource.priorModified) {
req.setRawHeader("If-Modified-Since", util::rfc1123(*m_resource.priorModified).c_str());
}
@@ -89,7 +90,7 @@ void HTTPRequest::handleNetworkReply(QNetworkReply *reply)
if (bytes.isEmpty()) {
response.data = std::make_shared<std::string>();
} else {
- response.data = std::make_shared<std::string>(bytes.data(), bytes.size());
+ response.data = std::make_shared<std::string>(bytes.constData(), bytes.size());
}
break;
}
diff --git a/platform/qt/src/http_request.hpp b/platform/qt/src/http_request.hpp
index 9af219f3c9..29a10467b3 100644
--- a/platform/qt/src/http_request.hpp
+++ b/platform/qt/src/http_request.hpp
@@ -15,13 +15,13 @@ class Response;
class HTTPRequest : public AsyncRequest
{
public:
- HTTPRequest(HTTPFileSource::Impl*, const Resource&, FileSource::Callback);
+ HTTPRequest(HTTPFileSource::Impl *, const Resource&, FileSource::Callback);
virtual ~HTTPRequest();
QUrl requestUrl() const;
QNetworkRequest networkRequest() const;
- void handleNetworkReply(QNetworkReply *reply);
+ void handleNetworkReply(QNetworkReply *);
private:
HTTPFileSource::Impl* m_context;
diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp
index 144d545d3c..d9eb43cf05 100644
--- a/platform/qt/src/qmapboxgl.cpp
+++ b/platform/qt/src/qmapboxgl.cpp
@@ -1,10 +1,17 @@
#include "qmapboxgl_p.hpp"
+#include "qt_conversion.hpp"
+#include "qt_geojson.hpp"
+
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/map.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/layer.hpp>
+#include <mbgl/style/conversion/source.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/style/transition_options.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/constants.hpp>
@@ -19,6 +26,7 @@
#include <QCoreApplication>
#endif
+#include <QDebug>
#include <QImage>
#include <QMapboxGL>
#include <QMargins>
@@ -73,6 +81,41 @@ namespace {
QThreadStorage<std::shared_ptr<mbgl::util::RunLoop>> loop;
+// Convertion helper functions.
+
+auto fromQMapboxGLShapeAnnotation(const ShapeAnnotation &shapeAnnotation) {
+ const LineString &lineString = shapeAnnotation.first;
+ const QString &styleLayer = shapeAnnotation.second;
+
+ mbgl::LineString<double> mbglLineString;
+ mbglLineString.reserve(lineString.size());
+
+ for (const Coordinate &coordinate : lineString) {
+ mbglLineString.emplace_back(mbgl::Point<double> { coordinate.first, coordinate.second });
+ }
+
+ return mbgl::StyleSourcedAnnotation { std::move(mbglLineString), styleLayer.toStdString() };
+}
+
+auto fromQMapboxTransitionOptions(const QMapbox::TransitionOptions &options) {
+ auto convert = [](auto& value) -> mbgl::optional<mbgl::Duration> {
+ if (value.isValid()) {
+ return std::chrono::duration_cast<mbgl::Duration>(mbgl::Milliseconds(value.template value<qint64>()));
+ };
+ return {};
+ };
+ return mbgl::style::TransitionOptions { convert(options.duration), convert(options.delay) };
+}
+
+auto fromQStringList(const QStringList &list)
+{
+ std::vector<std::string> strings(list.size());
+ for (const QString &string : list) {
+ strings.emplace_back(string.toStdString());
+ }
+ return strings;
+}
+
}
QMapboxGLSettings::QMapboxGLSettings()
@@ -345,11 +388,21 @@ void QMapboxGL::addClass(const QString &className)
d_ptr->mapObj->addClass(className.toStdString());
}
+void QMapboxGL::addClass(const QString &className, const QMapbox::TransitionOptions &options)
+{
+ d_ptr->mapObj->addClass(className.toStdString(), fromQMapboxTransitionOptions(options));
+}
+
void QMapboxGL::removeClass(const QString &className)
{
d_ptr->mapObj->removeClass(className.toStdString());
}
+void QMapboxGL::removeClass(const QString &className, const QMapbox::TransitionOptions &options)
+{
+ d_ptr->mapObj->removeClass(className.toStdString(), fromQMapboxTransitionOptions(options));
+}
+
bool QMapboxGL::hasClass(const QString &className) const
{
return d_ptr->mapObj->hasClass(className.toStdString());
@@ -357,14 +410,12 @@ bool QMapboxGL::hasClass(const QString &className) const
void QMapboxGL::setClasses(const QStringList &classNames)
{
- std::vector<std::string> mbglClassNames;
- mbglClassNames.reserve(classNames.size());
-
- for (const QString &className : classNames) {
- mbglClassNames.emplace_back(className.toStdString());
- }
+ d_ptr->mapObj->setClasses(fromQStringList(classNames));
+}
- d_ptr->mapObj->setClasses(mbglClassNames);
+void QMapboxGL::setClasses(const QStringList &classNames, const QMapbox::TransitionOptions &options)
+{
+ d_ptr->mapObj->setClasses(fromQStringList(classNames), fromQMapboxTransitionOptions(options));
}
QStringList QMapboxGL::getClasses() const
@@ -392,35 +443,55 @@ void QMapboxGL::updatePointAnnotation(AnnotationID id, const PointAnnotation &po
d_ptr->mapObj->updateAnnotation(id, fromPointAnnotation(pointAnnotation));
}
-mbgl::Annotation fromQMapboxGLShapeAnnotation(const ShapeAnnotation &shapeAnnotation) {
- const CoordinateSegments &segments = shapeAnnotation.first;
- const QString &styleLayer = shapeAnnotation.second;
+AnnotationID QMapboxGL::addShapeAnnotation(const ShapeAnnotation &shapeAnnotation)
+{
+ return d_ptr->mapObj->addAnnotation(fromQMapboxGLShapeAnnotation(shapeAnnotation));
+}
- mbgl::Polygon<double> polygon;
- polygon.reserve(segments.size());
+void QMapboxGL::removeAnnotation(AnnotationID annotationID)
+{
+ d_ptr->mapObj->removeAnnotation(annotationID);
+}
- for (const Coordinates &coordinates : segments) {
- mbgl::LinearRing<double> linearRing;
- linearRing.reserve(coordinates.size());
+void QMapboxGL::setLayoutProperty(const QString& layer_, const QString& property, const QVariant& value)
+{
+ using namespace mbgl::style;
- for (const Coordinate &coordinate : coordinates) {
- linearRing.emplace_back(mbgl::Point<double>(coordinate.first, coordinate.second));
- }
+ Layer* layer = d_ptr->mapObj->getLayer(layer_.toStdString());
+ if (!layer) {
+ qWarning() << "Layer not found:" << layer_;
+ return;
+ }
- polygon.emplace_back(linearRing);
+ if (conversion::setLayoutProperty(*layer, property.toStdString(), value)) {
+ qWarning() << "Error setting layout property:" << layer_ << "-" << property;
+ return;
}
- return mbgl::StyleSourcedAnnotation { polygon, styleLayer.toStdString() };
+ d_ptr->mapObj->update(mbgl::Update::RecalculateStyle);
}
-AnnotationID QMapboxGL::addShapeAnnotation(const ShapeAnnotation &shapeAnnotation)
+void QMapboxGL::setPaintProperty(const QString& layer_, const QString& property, const QVariant& value, const QString& klass_)
{
- return d_ptr->mapObj->addAnnotation(fromQMapboxGLShapeAnnotation(shapeAnnotation));
-}
+ using namespace mbgl::style;
-void QMapboxGL::removeAnnotation(AnnotationID annotationID)
-{
- d_ptr->mapObj->removeAnnotation(annotationID);
+ Layer* layer = d_ptr->mapObj->getLayer(layer_.toStdString());
+ if (!layer) {
+ qWarning() << "Layer not found:" << layer_;
+ return;
+ }
+
+ mbgl::optional<std::string> klass;
+ if (!klass_.isEmpty()) {
+ klass = klass_.toStdString();
+ }
+
+ if (conversion::setPaintProperty(*layer, property.toStdString(), value, klass)) {
+ qWarning() << "Error setting paint property:" << layer_ << "-" << property;
+ return;
+ }
+
+ d_ptr->mapObj->update(mbgl::Update::RecalculateStyle | mbgl::Update::Classes);
}
bool QMapboxGL::isRotating() const
@@ -550,6 +621,25 @@ QMargins QMapboxGL::margins() const
);
}
+void QMapboxGL::addSource(const QString& sourceID, const QVariant& value)
+{
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
+ Result<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(value, sourceID.toStdString());
+ if (!source) {
+ qWarning() << "Unable to add source:" << source.error().message.c_str();
+ return;
+ }
+
+ d_ptr->mapObj->addSource(std::move(*source));
+}
+
+void QMapboxGL::removeSource(const QString& sourceID)
+{
+ d_ptr->mapObj->removeSource(sourceID.toStdString());
+}
+
void QMapboxGL::addCustomLayer(const QString &id,
QMapbox::CustomLayerInitializeFunction initFn,
QMapbox::CustomLayerRenderFunction renderFn,
@@ -568,11 +658,65 @@ void QMapboxGL::addCustomLayer(const QString &id,
before ? mbgl::optional<std::string>(before) : mbgl::optional<std::string>());
}
-void QMapboxGL::removeCustomLayer(const QString& id)
+void QMapboxGL::addLayer(const QVariant& value)
+{
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
+ Result<std::unique_ptr<Layer>> layer = convert<std::unique_ptr<Layer>>(value);
+ if (!layer) {
+ qWarning() << "Unable to add layer:" << layer.error().message.c_str();
+ return;
+ }
+
+ d_ptr->mapObj->addLayer(std::move(*layer));
+}
+
+void QMapboxGL::removeLayer(const QString& id)
{
d_ptr->mapObj->removeLayer(id.toStdString());
}
+void QMapboxGL::setFilter(const QString& layer_, const QVariant& filter_)
+{
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
+ Layer* layer = d_ptr->mapObj->getLayer(layer_.toStdString());
+ if (!layer) {
+ qWarning() << "Layer not found:" << layer_;
+ return;
+ }
+
+ Filter filter;
+
+ Result<Filter> converted = convert<Filter>(filter_);
+ if (!converted) {
+ qWarning() << "Error parsing filter:" << converted.error().message.c_str();
+ return;
+ }
+ filter = std::move(*converted);
+
+ if (layer->is<FillLayer>()) {
+ layer->as<FillLayer>()->setFilter(filter);
+ return;
+ }
+ if (layer->is<LineLayer>()) {
+ layer->as<LineLayer>()->setFilter(filter);
+ return;
+ }
+ if (layer->is<SymbolLayer>()) {
+ layer->as<SymbolLayer>()->setFilter(filter);
+ return;
+ }
+ if (layer->is<CircleLayer>()) {
+ layer->as<CircleLayer>()->setFilter(filter);
+ return;
+ }
+
+ qWarning() << "Layer doesn't support filters";
+}
+
void QMapboxGL::render()
{
d_ptr->dirty = false;
@@ -586,7 +730,6 @@ void QMapboxGL::connectionEstablished()
QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings)
: QObject(q)
- , size(0, 0)
, q_ptr(q)
, fileSourceObj(std::make_unique<mbgl::DefaultFileSource>(
settings.cacheDatabasePath().toStdString(),
diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp
index fdbfb7f2c1..fb8a5df4e7 100644
--- a/platform/qt/src/qmapboxgl_p.hpp
+++ b/platform/qt/src/qmapboxgl_p.hpp
@@ -1,5 +1,4 @@
-#ifndef QMAPBOXGL_P_H
-#define QMAPBOXGL_P_H
+#pragma once
#include <mbgl/map/map.hpp>
#include <mbgl/map/view.hpp>
@@ -15,7 +14,7 @@ class QMapboxGLPrivate : public QObject, public mbgl::View
Q_OBJECT
public:
- explicit QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &);
+ explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &);
virtual ~QMapboxGLPrivate();
// mbgl::View implementation.
@@ -26,17 +25,17 @@ public:
void activate() final {}
void deactivate() final {}
void invalidate() final;
- void notifyMapChange(mbgl::MapChange change) final;
+ void notifyMapChange(mbgl::MapChange) final;
mbgl::EdgeInsets margins;
- QSize size;
+ QSize size { 0, 0 };
- QMapboxGL *q_ptr = nullptr;
+ QMapboxGL *q_ptr { nullptr };
std::unique_ptr<mbgl::DefaultFileSource> fileSourceObj;
std::unique_ptr<mbgl::Map> mapObj;
- bool dirty = false;
+ bool dirty { false };
public slots:
void connectionEstablished();
@@ -45,5 +44,3 @@ signals:
void needsRendering();
void mapChanged(QMapboxGL::MapChange);
};
-
-#endif // QMAPBOXGL_P_H
diff --git a/platform/qt/src/qquickmapboxgl.cpp b/platform/qt/src/qquickmapboxgl.cpp
index 2961925c8b..d8492c5218 100644
--- a/platform/qt/src/qquickmapboxgl.cpp
+++ b/platform/qt/src/qquickmapboxgl.cpp
@@ -148,16 +148,22 @@ bool QQuickMapboxGL::copyrightsVisible() const
return false;
}
-void QQuickMapboxGL::setColor(const QColor &)
+void QQuickMapboxGL::setColor(const QColor &color)
{
- // TODO: can be made functional after landing #837
- qWarning() << __PRETTY_FUNCTION__
- << "Use Mapbox Studio to change the map background color.";
+ if (m_color == color) {
+ return;
+ }
+
+ m_color = color;
+
+ setPaintProperty("background", "background-color", color);
+
+ emit colorChanged(m_color);
}
QColor QQuickMapboxGL::color() const
{
- return QColor();
+ return m_color;
}
void QQuickMapboxGL::pan(int dx, int dy)
@@ -168,6 +174,18 @@ void QQuickMapboxGL::pan(int dx, int dy)
update();
}
+void QQuickMapboxGL::setLayoutProperty(const QString &layer, const QString &property, const QVariant &value)
+{
+ m_layoutChanges.append(LayoutPropertyChange { layer, property, value });
+ update();
+}
+
+void QQuickMapboxGL::setPaintProperty(const QString &layer, const QString &property, const QVariant &value, const QString &klass)
+{
+ m_paintChanges.append(PaintPropertyChange { layer, property, value, klass });
+ update();
+}
+
void QQuickMapboxGL::setStyle(const QString &styleUrl)
{
if (m_style == styleUrl) {
diff --git a/platform/qt/src/qquickmapboxglrenderer.cpp b/platform/qt/src/qquickmapboxglrenderer.cpp
index 6212c4f128..326eafa928 100644
--- a/platform/qt/src/qquickmapboxglrenderer.cpp
+++ b/platform/qt/src/qquickmapboxglrenderer.cpp
@@ -19,12 +19,21 @@ QQuickMapboxGLRenderer::QQuickMapboxGLRenderer()
settings.setViewportMode(QMapboxGLSettings::FlippedYViewport);
m_map.reset(new QMapboxGL(nullptr, settings));
+ connect(m_map.data(), SIGNAL(mapChanged(QMapboxGL::MapChange)), this, SLOT(onMapChanged(QMapboxGL::MapChange)));
}
QQuickMapboxGLRenderer::~QQuickMapboxGLRenderer()
{
}
+void QQuickMapboxGLRenderer::onMapChanged(QMapboxGL::MapChange change)
+{
+ if (change == QMapboxGL::MapChangeDidFinishLoadingMap) {
+ m_styleLoaded = true;
+ update();
+ }
+}
+
QOpenGLFramebufferObject* QQuickMapboxGLRenderer::createFramebufferObject(const QSize &size)
{
m_map->resize(size);
@@ -61,6 +70,7 @@ void QQuickMapboxGLRenderer::synchronize(QQuickFramebufferObject *item)
if (syncStatus & QQuickMapboxGL::StyleNeedsSync) {
m_map->setStyleURL(quickMap->style());
+ m_styleLoaded = false;
}
if (syncStatus & QQuickMapboxGL::PanNeedsSync) {
@@ -75,4 +85,20 @@ void QQuickMapboxGLRenderer::synchronize(QQuickFramebufferObject *item)
if (syncStatus & QQuickMapboxGL::PitchNeedsSync) {
m_map->setPitch(quickMap->pitch());
}
+
+ if (m_styleLoaded) {
+ if (!quickMap->layoutPropertyChanges().empty()) {
+ for (const auto& change: quickMap->layoutPropertyChanges()) {
+ m_map->setLayoutProperty(change.layer, change.property, change.value);
+ }
+ quickMap->layoutPropertyChanges().clear();
+ }
+
+ if (!quickMap->paintPropertyChanges().empty()) {
+ for (const auto& change: quickMap->paintPropertyChanges()) {
+ m_map->setPaintProperty(change.layer, change.property, change.value, change.klass);
+ }
+ quickMap->paintPropertyChanges().clear();
+ }
+ }
}
diff --git a/platform/qt/src/qquickmapboxglrenderer.hpp b/platform/qt/src/qquickmapboxglrenderer.hpp
index 80a5cb3278..d7ca7430ab 100644
--- a/platform/qt/src/qquickmapboxglrenderer.hpp
+++ b/platform/qt/src/qquickmapboxglrenderer.hpp
@@ -1,12 +1,12 @@
-#ifndef QQUICKMAPBOXGLRENDERER_H
-#define QQUICKMAPBOXGLRENDERER_H
+#pragma once
#include <QObject>
#include <QQuickFramebufferObject>
#include <QScopedPointer>
+#include <QMapboxGL>
+
class QGeoCoordinate;
-class QMapboxGL;
class QOpenGLFramebufferObject;
class QSize;
@@ -18,18 +18,20 @@ public:
QQuickMapboxGLRenderer();
virtual ~QQuickMapboxGLRenderer();
- virtual QOpenGLFramebufferObject* createFramebufferObject(const QSize& size);
+ virtual QOpenGLFramebufferObject * createFramebufferObject(const QSize &);
virtual void render();
- virtual void synchronize(QQuickFramebufferObject *item);
+ virtual void synchronize(QQuickFramebufferObject *);
signals:
- void centerChanged(const QGeoCoordinate &coordinate);
+ void centerChanged(const QGeoCoordinate &);
+
+public slots:
+ void onMapChanged(QMapboxGL::MapChange);
private:
bool m_initialized = false;
+ bool m_styleLoaded = false;
QScopedPointer<QMapboxGL> m_map;
};
-
-#endif // QQUICKMAPBOXGLRENDERER_H
diff --git a/platform/qt/src/qquickmapboxglstyleproperty.cpp b/platform/qt/src/qquickmapboxglstyleproperty.cpp
new file mode 100644
index 0000000000..77e6a00583
--- /dev/null
+++ b/platform/qt/src/qquickmapboxglstyleproperty.cpp
@@ -0,0 +1,118 @@
+#include <QQuickMapboxGLStyleProperty>
+#include <QQuickMapboxGL>
+
+QQuickMapboxGLStyleProperty::QQuickMapboxGLStyleProperty(QQuickItem *parent_)
+ : QQuickItem(parent_)
+{
+}
+
+QQuickMapboxGLLayoutStyleProperty::QQuickMapboxGLLayoutStyleProperty(QQuickItem *parent_)
+ : QQuickMapboxGLStyleProperty(parent_)
+{
+}
+
+QQuickMapboxGLPaintStyleProperty::QQuickMapboxGLPaintStyleProperty(QQuickItem *parent_)
+ : QQuickMapboxGLStyleProperty(parent_)
+{
+}
+
+void QQuickMapboxGLLayoutStyleProperty::updateParent()
+{
+ if (m_layer.isNull() || m_property.isNull() || m_value.isNull()) {
+ return;
+ }
+
+ QQuickMapboxGL *map = qobject_cast<QQuickMapboxGL *>(parentItem());
+ if (map) {
+ map->setLayoutProperty(layer(), property(), m_value);
+ } else {
+ qWarning() << "Style property requires QQuickMapboxGL as parent item.";
+ }
+}
+
+void QQuickMapboxGLPaintStyleProperty::updateParent()
+{
+ if (m_layer.isNull() || m_property.isNull() || m_value.isNull()) {
+ return;
+ }
+
+ QQuickMapboxGL *map = qobject_cast<QQuickMapboxGL *>(parentItem());
+ if (map) {
+ map->setPaintProperty(layer(), property(), m_value, styleClass());
+ } else {
+ qWarning() << "Style property requires QQuickMapboxGL as parent item.";
+ }
+}
+
+void QQuickMapboxGLStyleProperty::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
+{
+ QQuickItem::itemChange(change, data);
+
+ if (change == QQuickItem::ItemParentHasChanged) {
+ updateParent();
+ }
+}
+
+void QQuickMapboxGLStyleProperty::setLayer(const QString &layer)
+{
+ if (layer == m_layer.toString()) {
+ return;
+ }
+
+ m_layer = layer;
+ emit layerChanged(layer);
+ updateParent();
+}
+
+QString QQuickMapboxGLStyleProperty::layer() const
+{
+ return m_layer.toString();
+}
+
+void QQuickMapboxGLStyleProperty::setProperty(const QString &property)
+{
+ if (property == m_property.toString()) {
+ return;
+ }
+
+ m_property = property;
+ emit propertyChanged(property);
+ updateParent();
+}
+
+QString QQuickMapboxGLStyleProperty::property() const
+{
+ return m_property.toString();
+}
+
+void QQuickMapboxGLStyleProperty::setValue(const QVariant &value)
+{
+ if (value == m_value) {
+ return;
+ }
+
+ m_value = value;
+ emit valueChanged(value);
+ updateParent();
+}
+
+QVariant QQuickMapboxGLStyleProperty::value() const
+{
+ return m_value;
+}
+
+void QQuickMapboxGLPaintStyleProperty::setStyleClass(const QString &styleClass)
+{
+ if (styleClass == m_class.toString()) {
+ return;
+ }
+
+ m_class = styleClass;
+ emit classChanged(styleClass);
+ updateParent();
+}
+
+QString QQuickMapboxGLPaintStyleProperty::styleClass() const
+{
+ return m_class.toString();
+}
diff --git a/platform/qt/src/qt_conversion.hpp b/platform/qt/src/qt_conversion.hpp
new file mode 100644
index 0000000000..dc20aa4753
--- /dev/null
+++ b/platform/qt/src/qt_conversion.hpp
@@ -0,0 +1,106 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <QColor>
+#include <QVariant>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+inline bool isUndefined(const QVariant& value) {
+ return value.isNull() || !value.isValid();
+}
+
+inline bool isArray(const QVariant& value) {
+ return value.canConvert(QVariant::List);
+}
+
+inline std::size_t arrayLength(const QVariant& value) {
+ return value.toList().size();
+}
+
+inline QVariant arrayMember(const QVariant& value, std::size_t i) {
+ return value.toList()[i];
+}
+
+inline bool isObject(const QVariant& value) {
+ return value.canConvert(QVariant::Map) || value.type() == QVariant::ByteArray;
+}
+
+inline optional<QVariant> objectMember(const QVariant& value, const char* key) {
+ auto map = value.toMap();
+ auto iter = map.constFind(key);
+
+ if (iter != map.constEnd()) {
+ return iter.value();
+ } else {
+ return {};
+ }
+}
+
+template <class Fn>
+optional<Error> eachMember(const QVariant& value, Fn&& fn) {
+ auto map = value.toMap();
+ auto iter = map.constBegin();
+
+ while (iter != map.constEnd()) {
+ optional<Error> result = fn(iter.key().toStdString(), iter.value());
+ if (result) {
+ return result;
+ }
+
+ ++iter;
+ }
+
+ return {};
+}
+
+inline optional<bool> toBool(const QVariant& value) {
+ if (value.type() == QVariant::Bool) {
+ return value.toBool();
+ } else {
+ return {};
+ }
+}
+
+inline optional<float> toNumber(const QVariant& value) {
+ if (value.type() == QVariant::Double) {
+ return value.toFloat();
+ } else {
+ return {};
+ }
+}
+
+inline optional<std::string> toString(const QVariant& value) {
+ if (value.type() == QVariant::String) {
+ return value.toString().toStdString();
+ } else if (value.type() == QVariant::Color) {
+ return value.value<QColor>().name().toStdString();
+ } else {
+ return {};
+ }
+}
+
+inline optional<Value> toValue(const QVariant& value) {
+ if (value.type() == QVariant::Bool) {
+ return { value.toBool() };
+ } else if (value.type() == QVariant::String) {
+ return { value.toString().toStdString() };
+ } else if (value.type() == QVariant::Color) {
+ return { value.value<QColor>().name().toStdString() };
+ } else if (value.type() == QVariant::Int) {
+ return { value.toInt() };
+ } else if (value.canConvert(QVariant::Double)) {
+ return { value.toFloat() };
+ } else {
+ return {};
+ }
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/platform/qt/src/qt_geojson.hpp b/platform/qt/src/qt_geojson.hpp
new file mode 100644
index 0000000000..fd2b689fed
--- /dev/null
+++ b/platform/qt/src/qt_geojson.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <mapbox/geojson.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/util/rapidjson.hpp>
+
+#include <sstream>
+#include <string>
+
+#include <QByteArray>
+#include <QVariant>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+Result<GeoJSON> convertGeoJSON(const QVariant& value) {
+ if (value.type() != QVariant::ByteArray) {
+ return Error { "JSON data must be in QByteArray" };
+ }
+
+ auto data = value.toByteArray();
+
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> d;
+ if (data.endsWith(char(0))) {
+ d.Parse<0>(value.toByteArray().data());
+ } else {
+ d.Parse<0>(value.toByteArray().constData());
+ }
+
+ if (d.HasParseError()) {
+ std::stringstream message;
+ message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError());
+
+ return Error { message.str() };
+ }
+
+ conversion::Result<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d);
+ if (!geoJSON) {
+ return Error { geoJSON.error().message };
+ }
+
+ return geoJSON;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/platform/qt/src/run_loop_impl.hpp b/platform/qt/src/run_loop_impl.hpp
index 6cabbbb425..ea64e4b977 100644
--- a/platform/qt/src/run_loop_impl.hpp
+++ b/platform/qt/src/run_loop_impl.hpp
@@ -35,5 +35,5 @@ public slots:
void onWriteEvent(int fd);
};
-}
-}
+} // namespace util
+} // namespace mbgl
diff --git a/platform/qt/src/string_stdlib.cpp b/platform/qt/src/string_stdlib.cpp
new file mode 100644
index 0000000000..c4adecea06
--- /dev/null
+++ b/platform/qt/src/string_stdlib.cpp
@@ -0,0 +1,24 @@
+#include <mbgl/platform/platform.hpp>
+
+#include <QByteArray>
+#include <QString>
+
+#include <string>
+
+namespace mbgl {
+namespace platform {
+
+std::string uppercase(const std::string& str) {
+ auto upper = QString::fromUtf8(str.c_str()).toUpper().toUtf8();
+
+ return std::string(upper.constData(), upper.size());
+}
+
+std::string lowercase(const std::string& str) {
+ auto lower = QString::fromUtf8(str.c_str()).toLower().toUtf8();
+
+ return std::string(lower.constData(), lower.size());
+}
+
+} // namespace platform
+} // namespace mbgl
diff --git a/platform/qt/src/timer_impl.hpp b/platform/qt/src/timer_impl.hpp
index a54abfd8b5..ea22b20974 100644
--- a/platform/qt/src/timer_impl.hpp
+++ b/platform/qt/src/timer_impl.hpp
@@ -14,7 +14,7 @@ class Timer::Impl : public QObject {
public:
Impl();
- void start(uint64_t timeout, uint64_t repeat, std::function<void ()>&& cb);
+ void start(uint64_t timeout, uint64_t repeat, std::function<void ()> &&);
void stop();
public slots:
@@ -27,5 +27,5 @@ private:
QTimer timer;
};
-}
-}
+} // namespace util
+} // namespace mbgl
diff --git a/scripts/build-shaders.py b/scripts/build-shaders.py
index 3b02cdc886..d679172554 100755
--- a/scripts/build-shaders.py
+++ b/scripts/build-shaders.py
@@ -50,6 +50,11 @@ namespace mbgl {{
namespace shaders {{
namespace {name} {{
+#ifndef MBGL_SHADER_NAME_{NAME}
+#define MBGL_SHADER_NAME_{NAME}
+constexpr const char* name = "{name}";
+#endif // MBGL_SHADER_NAME_{NAME}
+
#ifdef GL_ES_VERSION_2_0
constexpr const char* {type} = R"MBGL_SHADER(precision highp float;\n{data})MBGL_SHADER";
#else
diff --git a/scripts/build-version.py b/scripts/build-version.py
index 800d3f6220..f4a8abb88e 100755
--- a/scripts/build-version.py
+++ b/scripts/build-version.py
@@ -67,8 +67,8 @@ extern const char *revision;
extern const char *string;
extern const unsigned int number;
-}} // namespace mbgl
}} // namespace version
+}} // namespace mbgl
""".format(
major = tag[0],
minor = tag[1],
diff --git a/scripts/clang-tidy.sh b/scripts/clang-tidy.sh
deleted file mode 100755
index 9420518747..0000000000
--- a/scripts/clang-tidy.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-
-directory=$1
-
-export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
-
-command -v ${CLANG_TIDY:-clang-tidy} >/dev/null 2>&1 || {
- echo "Can't find ${CLANG_TIDY:-clang-tidy} in PATH."
- if [ -z ${CLANG_TIDY} ]; then
- echo "Alternatively, you can set CLANG_TIDY to point to clang-tidy."
- fi
- exit 1
-}
-
-cd "${directory}"
-
-git ls-files '../../../src/mbgl/*.cpp' '../../../platform/*.cpp' '../../../test/*.cpp' | \
- xargs -I{} -P ${JOBS} ${CLANG_TIDY:-clang-tidy} -header-filter='\/mbgl\/' {}
diff --git a/scripts/clang-tools.sh b/scripts/clang-tools.sh
new file mode 100755
index 0000000000..f26b065263
--- /dev/null
+++ b/scripts/clang-tools.sh
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+export PATH="`pwd`/.mason:${PATH}" MASON_DIR="`pwd`/.mason"
+
+CLANG_TIDY=${CLANG_TIDY:-$(mason prefix clang-tidy 3.8.0)/bin/clang-tidy}
+CLANG_FORMAT=${CLANG_FORMAT:-$(mason prefix clang-format 3.8.0)/bin/clang-format}
+
+command -v ${CLANG_TIDY} >/dev/null 2>&1 || {
+ echo "Can't find ${CLANG_TIDY} in PATH."
+ if [ -z ${CLANG_TIDY} ]; then
+ echo "Alternatively, you can set CLANG_TIDY to point to clang-tidy."
+ fi
+ exit 1
+}
+
+cd $1
+
+function check_tidy() {
+ echo "Running clang-tidy on $0..."
+ if [[ -n $1 ]] && [[ $1 == "--fix" ]]; then
+ OUTPUT=$(${CLANG_TIDY} -p=$PWD -fix -fix-errors ${0} 2>/dev/null)
+ else
+ OUTPUT=$(${CLANG_TIDY} -p=$PWD ${0} 2>/dev/null)
+ fi
+ if [[ -n $OUTPUT ]]; then
+ echo "Caught clang-tidy warning/error:"
+ echo -e "$OUTPUT"
+ exit 1
+ fi
+}
+
+function check_format() {
+ echo "Running clang-format on $0..."
+ ${CLANG_FORMAT} -i ../../../$0
+}
+
+export CLANG_TIDY CLANG_FORMAT
+export -f check_tidy check_format
+
+git diff-index --quiet HEAD || {
+ echo "Your repository contains unstaged and/or uncommitted changes."
+ echo "Please commit all changes before proceeding."
+ exit 1
+}
+
+echo "Running clang checks... (this might take a while)"
+
+if [ -n $2 ] && [ $2 == "--diff" ]; then
+ DIFF_FILES=$(for file in `git diff origin/master..HEAD --name-only | grep "pp$"`; do echo $file; done)
+ if [[ -n $DIFF_FILES ]]; then
+ echo "${DIFF_FILES}" | xargs -I{} -P ${JOBS} bash -c 'check_tidy --fix' {}
+ # XXX disabled until we run clang-format over the entire codebase.
+ #echo "${DIFF_FILES}" | xargs -I{} -P ${JOBS} bash -c 'check_format' {}
+ git diff-index --quiet HEAD || {
+ echo "Changes were made to source files - please review them before committing."
+ exit 1
+ }
+ fi
+ echo "All looks good!"
+else
+ git ls-files '../../../src/mbgl/*.cpp' '../../../platform/*.cpp' '../../../test/*.cpp' | \
+ xargs -I{} -P ${JOBS} bash -c 'check_tidy' {}
+fi
diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js
index 6379598446..2a25f37112 100644
--- a/scripts/generate-style-code.js
+++ b/scripts/generate-style-code.js
@@ -3,7 +3,14 @@
const fs = require('fs');
const ejs = require('ejs');
const spec = require('mapbox-gl-style-spec').latest;
-var parseCSSColor = require('csscolorparser').parseCSSColor;
+var colorParser = require('csscolorparser');
+
+function parseCSSColor(str) {
+ var color = colorParser.parseCSSColor(str);
+ return [
+ color[0] / 255 * color[3], color[1] / 255 * color[3], color[2] / 255 * color[3], color[3]
+ ];
+}
global.camelize = function (str) {
return str.replace(/(?:^|-)(.)/g, function (_, x) {
@@ -21,8 +28,8 @@ global.propertyType = function (property) {
if (/-translate-anchor$/.test(property.name)) {
return 'TranslateAnchorType';
}
- if (/-rotation-alignment$/.test(property.name)) {
- return 'RotationAlignmentType';
+ if (/-(rotation|pitch)-alignment$/.test(property.name)) {
+ return 'AlignmentType';
}
switch (property.type) {
case 'boolean':
@@ -57,9 +64,13 @@ global.defaultValue = function (property) {
case 'string':
return JSON.stringify(property.default || "");
case 'enum':
- return `${propertyType(property)}::${camelize(property.default)}`;
+ if (property.default === undefined) {
+ return `${propertyType(property)}::Undefined`;
+ } else {
+ return `${propertyType(property)}::${camelize(property.default)}`;
+ }
case 'color':
- return `{{ ${parseCSSColor(property.default).join(', ')} }}`
+ return `{ ${parseCSSColor(property.default).join(', ')} }`
case 'array':
const defaults = (property.default || []).map((e) => defaultValue({ type: property.value, default: e }));
if (property.length) {
@@ -72,283 +83,12 @@ global.defaultValue = function (property) {
}
}
-const layerHpp = ejs.compile(`<%
- const type = locals.type;
- const layoutProperties = locals.layoutProperties;
- const paintProperties = locals.paintProperties;
--%>
-// This file is generated. Do not edit.
-
-#pragma once
-
-#include <mbgl/style/layer.hpp>
-#include <mbgl/style/filter.hpp>
-#include <mbgl/style/property_value.hpp>
-
-#include <mbgl/util/color.hpp>
-
-<% if (type === 'line' || type === 'symbol') { -%>
-#include <vector>
-
-<% } -%>
-namespace mbgl {
-namespace style {
-
-class <%- camelize(type) %>Layer : public Layer {
-public:
- <%- camelize(type) %>Layer(const std::string& layerID);
- ~<%- camelize(type) %>Layer() final;
-
-<% if (type === 'raster') { -%>
- // Source
-
- void setSource(const std::string& sourceID);
- const std::string& getSourceID() const;
-
-<% } else if (type !== 'background') { -%>
- // Source
-
- void setSource(const std::string& sourceID, const std::string& sourceLayer);
- const std::string& getSourceID() const;
- const std::string& getSourceLayer() const;
-
- void setFilter(const Filter&);
- const Filter& getFilter() const;
-
-<% } -%>
-<% if (layoutProperties.length) { -%>
- // Layout properties
-
-<% for (const property of layoutProperties) { -%>
- PropertyValue<<%- propertyType(property) %>> get<%- camelize(property.name) %>() const;
- void set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>>);
-
-<% } -%>
-<% } -%>
- // Paint properties
-
-<% for (const property of paintProperties) { -%>
- PropertyValue<<%- propertyType(property) %>> get<%- camelize(property.name) %>() const;
- void set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>>);
-
-<% } -%>
- // Private implementation
-
- class Impl;
- Impl* const impl;
-
- <%- camelize(type) %>Layer(const Impl&);
- <%- camelize(type) %>Layer(const <%- camelize(type) %>Layer&) = delete;
-};
-
-template <>
-inline bool Layer::is<<%- camelize(type) %>Layer>() const {
- return type == Type::<%- camelize(type) %>;
-}
-
-} // namespace style
-} // namespace mbgl
-`, {strict: true});
-
-const layerCpp = ejs.compile(`<%
- const type = locals.type;
- const layoutProperties = locals.layoutProperties;
- const paintProperties = locals.paintProperties;
--%>
-// This file is generated. Edit scripts/generate-style-code.js, then run \`make style-code\`.
-
-#include <mbgl/style/layers/<%- type %>_layer.hpp>
-#include <mbgl/style/layers/<%- type %>_layer_impl.hpp>
-
-namespace mbgl {
-namespace style {
-
-<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const std::string& layerID)
- : Layer(Type::<%- camelize(type) %>, std::make_unique<Impl>())
- , impl(static_cast<Impl*>(baseImpl.get())) {
- impl->id = layerID;
-}
-
-<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const Impl& other)
- : Layer(Type::<%- camelize(type) %>, std::make_unique<Impl>(other))
- , impl(static_cast<Impl*>(baseImpl.get())) {
-}
-
-<%- camelize(type) %>Layer::~<%- camelize(type) %>Layer() = default;
-
-std::unique_ptr<Layer> <%- camelize(type) %>Layer::Impl::clone() const {
- return std::make_unique<<%- camelize(type) %>Layer>(*this);
-}
-
-<% if (type === 'raster') { -%>
-// Source
-
-void <%- camelize(type) %>Layer::setSource(const std::string& sourceID) {
- impl->source = sourceID;
-}
-
-const std::string& <%- camelize(type) %>Layer::getSourceID() const {
- return impl->source;
-}
-<% } else if (type !== 'background') { -%>
-// Source
-
-void <%- camelize(type) %>Layer::setSource(const std::string& sourceID, const std::string& sourceLayer) {
- impl->source = sourceID;
- impl->sourceLayer = sourceLayer;
-}
-
-const std::string& <%- camelize(type) %>Layer::getSourceID() const {
- return impl->source;
-}
-
-const std::string& <%- camelize(type) %>Layer::getSourceLayer() const {
- return impl->sourceLayer;
-}
-
-// Filter
-
-void <%- camelize(type) %>Layer::setFilter(const Filter& filter) {
- impl->filter = filter;
-}
-
-const Filter& <%- camelize(type) %>Layer::getFilter() const {
- return impl->filter;
-}
-<% } -%>
-
-// Layout properties
-
-<% for (const property of layoutProperties) { -%>
-PropertyValue<<%- propertyType(property) %>> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const {
- return impl->layout.<%- camelizeWithLeadingLowercase(property.name) %>.get();
-}
-
-void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>> value) {
- impl->layout.<%- camelizeWithLeadingLowercase(property.name) %>.set(value);
-}
-<% } -%>
-
-// Paint properties
-<% for (const property of paintProperties) { %>
-PropertyValue<<%- propertyType(property) %>> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const {
- return impl->paint.<%- camelizeWithLeadingLowercase(property.name) %>.get();
-}
-
-void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>> value) {
- impl->paint.<%- camelizeWithLeadingLowercase(property.name) %>.set(value);
-}
-<% } -%>
-
-} // namespace style
-} // namespace mbgl
-`, {strict: true});
-
-const propertiesHpp = ejs.compile(`<%
- const type = locals.type;
- const layoutProperties = locals.layoutProperties;
- const paintProperties = locals.paintProperties;
--%>
-// This file is generated. Edit scripts/generate-style-code.js, then run \`make style-code\`.
-
-#pragma once
-
-#include <mbgl/style/layout_property.hpp>
-#include <mbgl/style/paint_property.hpp>
-#include <mbgl/util/rapidjson.hpp>
-
-namespace mbgl {
-namespace style {
-
-class CascadeParameters;
-class CalculationParameters;
-
-<% if (layoutProperties.length) { -%>
-class <%- camelize(type) %>LayoutProperties {
-public:
- void parse(const JSValue&);
- void recalculate(const CalculationParameters&);
-
-<% for (const property of layoutProperties) { -%>
- LayoutProperty<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %> { <%- defaultValue(property) %> };
-<% } -%>
-};
-
-<% } -%>
-class <%- camelize(type) %>PaintProperties {
-public:
- void parse(const JSValue&);
- void cascade(const CascadeParameters&);
- bool recalculate(const CalculationParameters&);
-
-<% for (const property of paintProperties) { -%>
-<% if (/-pattern$/.test(property.name) || property.name === 'line-dasharray') { -%>
- PaintProperty<<%- propertyType(property) %>, CrossFadedPropertyEvaluator> <%- camelizeWithLeadingLowercase(property.name) %> { <%- defaultValue(property) %> };
-<% } else if (property.name === 'fill-outline-color') { -%>
- PaintProperty<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %> { {{ 0, 0, 0, -1 }} };
-<% } else { -%>
- PaintProperty<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %> { <%- defaultValue(property) %> };
-<% } -%>
-<% } -%>
-};
-
-} // namespace style
-} // namespace mbgl
-`, {strict: true});
-
-const propertiesCpp = ejs.compile(`<%
- const type = locals.type;
- const layoutProperties = locals.layoutProperties;
- const paintProperties = locals.paintProperties;
--%>
-// This file is generated. Edit scripts/generate-style-code.js, then run \`make style-code\`.
-
-#include <mbgl/style/layers/<%- type %>_layer_properties.hpp>
-
-namespace mbgl {
-namespace style {
-
-<% if (layoutProperties.length) { -%>
-void <%- camelize(type) %>LayoutProperties::parse(const JSValue& value) {
-<% for (const property of layoutProperties) { -%>
- <%- camelizeWithLeadingLowercase(property.name) %>.parse(<%- JSON.stringify(property.name) %>, value);
-<% } -%>
-}
-
-void <%- camelize(type) %>LayoutProperties::recalculate(const CalculationParameters& parameters) {
-<% for (const property of layoutProperties) { -%>
- <%- camelizeWithLeadingLowercase(property.name) %>.calculate(parameters);
-<% } -%>
-}
-
-<% } -%>
-void <%- camelize(type) %>PaintProperties::parse(const JSValue& value) {
-<% for (const property of paintProperties) { -%>
- <%- camelizeWithLeadingLowercase(property.name) %>.parse(<%- JSON.stringify(property.name) %>, value);
-<% } -%>
-}
-
-void <%- camelize(type) %>PaintProperties::cascade(const CascadeParameters& parameters) {
-<% for (const property of paintProperties) { -%>
- <%- camelizeWithLeadingLowercase(property.name) %>.cascade(parameters);
-<% } -%>
-}
-
-bool <%- camelize(type) %>PaintProperties::recalculate(const CalculationParameters& parameters) {
- bool hasTransitions = false;
+const layerHpp = ejs.compile(fs.readFileSync('include/mbgl/style/layers/layer.hpp.ejs', 'utf8'), {strict: true});
+const layerCpp = ejs.compile(fs.readFileSync('src/mbgl/style/layers/layer.cpp.ejs', 'utf8'), {strict: true});
+const propertiesHpp = ejs.compile(fs.readFileSync('src/mbgl/style/layers/layer_properties.hpp.ejs', 'utf8'), {strict: true});
+const propertiesCpp = ejs.compile(fs.readFileSync('src/mbgl/style/layers/layer_properties.cpp.ejs', 'utf8'), {strict: true});
-<% for (const property of paintProperties) { -%>
- hasTransitions |= <%- camelizeWithLeadingLowercase(property.name) %>.calculate(parameters);
-<% } -%>
-
- return hasTransitions;
-}
-
-} // namespace style
-} // namespace mbgl
-`, {strict: true});
-
-for (const type of spec.layer.type.values) {
+const layers = spec.layer.type.values.map((type) => {
const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
if (name !== 'visibility') {
spec[`layout_${type}`][name].name = name;
@@ -363,15 +103,20 @@ for (const type of spec.layer.type.values) {
return memo;
}, []);
- const layer = {
+ return {
type: type,
layoutProperties: layoutProperties,
paintProperties: paintProperties,
};
+});
- fs.writeFileSync(`include/mbgl/style/layers/${type}_layer.hpp`, layerHpp(layer));
- fs.writeFileSync(`src/mbgl/style/layers/${type}_layer.cpp`, layerCpp(layer));
+for (const layer of layers) {
+ fs.writeFileSync(`include/mbgl/style/layers/${layer.type}_layer.hpp`, layerHpp(layer));
+ fs.writeFileSync(`src/mbgl/style/layers/${layer.type}_layer.cpp`, layerCpp(layer));
- fs.writeFileSync(`src/mbgl/style/layers/${type}_layer_properties.hpp`, propertiesHpp(layer));
- fs.writeFileSync(`src/mbgl/style/layers/${type}_layer_properties.cpp`, propertiesCpp(layer));
+ fs.writeFileSync(`src/mbgl/style/layers/${layer.type}_layer_properties.hpp`, propertiesHpp(layer));
+ fs.writeFileSync(`src/mbgl/style/layers/${layer.type}_layer_properties.cpp`, propertiesCpp(layer));
}
+
+const propertySettersHpp = ejs.compile(fs.readFileSync('include/mbgl/style/conversion/make_property_setters.hpp.ejs', 'utf8'), {strict: true});
+fs.writeFileSync('include/mbgl/style/conversion/make_property_setters.hpp', propertySettersHpp({layers: layers}));
diff --git a/scripts/travis_setup.sh b/scripts/travis_setup.sh
index e9fa87811f..dfd045224a 100755
--- a/scripts/travis_setup.sh
+++ b/scripts/travis_setup.sh
@@ -45,8 +45,8 @@ git submodule update --init .mason
# Install and set up to load a more recent version of mesa
mapbox_time "install_mesa" \
-mason install mesa 10.4.3
-export LD_LIBRARY_PATH="`mason prefix mesa 10.4.3`/lib:${LD_LIBRARY_PATH:-}"
+mason install mesa 11.2.2
+export LD_LIBRARY_PATH="`mason prefix mesa 11.2.2`/lib:${LD_LIBRARY_PATH:-}"
# Install and set up to load awscli
pip install --user awscli
diff --git a/scripts/valgrind.sh b/scripts/valgrind.sh
new file mode 100755
index 0000000000..009e18c432
--- /dev/null
+++ b/scripts/valgrind.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+set -e
+set -o pipefail
+
+PARAMS="\
+ --leak-check=full \
+ --show-leak-kinds=definite \
+ --errors-for-leak-kinds=definite \
+ --error-exitcode=1 \
+ --gen-suppressions=all \
+ --suppressions=scripts/valgrind.sup"
+
+export VALGRIND_LIB=$(.mason/mason prefix valgrind latest)/lib/valgrind
+
+$(.mason/mason prefix valgrind latest)/bin/valgrind $PARAMS $@
diff --git a/scripts/valgrind.sup b/scripts/valgrind.sup
index fcde049d9c..f21f54be54 100644
--- a/scripts/valgrind.sup
+++ b/scripts/valgrind.sup
@@ -1,91 +1,182 @@
{
- Valgrind gets confused with buffers managed by the graphics driver
+ Graphics driver buffers
Memcheck:Leak
...
- obj:/usr/*/dri/r600_dri.so
+ obj:*/r600_dri.*
+ obj:*/r600_dri.*
+ obj:*/r600_dri.*
...
}
{
- GLFW _glfwCreateCursor()
+ Graphics driver buffers
Memcheck:Leak
- match-leak-kinds: reachable
...
- obj:/usr/*/libX11.so.6.3.0
+ obj:*/i965_dri.*
+ obj:*/i965_dri.*
+ obj:*/i965_dri.*
...
- fun:_glfwCreateCursor
+}
+{
+ Graphics driver buffers
+ Memcheck:Leak
+ ...
+ obj:*/nouveau_dri.*
+ obj:*/nouveau_dri.*
+ obj:*/nouveau_dri.*
...
}
{
- GLFW _glfwPlatform*()
+ Ubuntu 16.04 - GLFW
Memcheck:Leak
- match-leak-kinds: reachable
...
- fun:_glfwPlatform*
+ fun:glfwInit
...
}
{
- Buffer managed by OpenGL?
+ Ubuntu 16.04 - GLFW
Memcheck:Cond
...
- fun:_ZN4mbgl*Uniform*
+ fun:glfwInit
...
- fun:_ZN8GLFWView10invalidateEv
+}
+{
+ Ubuntu 16.04 - Qt + glib
+ Memcheck:Cond
+ fun:g_utf8_offset_to_pointer
...
}
{
- Buffer managed by OpenGL?
+ Ubuntu 16.04 - Qt + glib
Memcheck:Cond
- fun:_ZNSt7__equalILb0EE5equalIPKfS3_EEbT_S4_T0_
...
- fun:_ZN4mbgl7Painter11renderLayerERKNS_10StyleLayerEPKNS_4Tile2IDEPKSt5arrayIfLm16EE
+ fun:g_signal_emit_valist
+ ...
+ fun:*QApplicationPrivate*
+ ...
+}
+{
+ Ubuntu 16.04 - Qt + X11
+ Memcheck:Param
+ writev(vector[...])
+ ...
+ obj:*/libxcb.*
+ fun:xcb_flush
+ ...
}
{
- Valgrind doesn't like our make_unique (C++14 polyfill)
+ Ubuntu 16.04 - Qt + X11
Memcheck:Leak
- match-leak-kinds: reachable
- fun:malloc
...
- fun:_ZN4mbgl4util11make_unique*
+ fun:_XmbTextListToTextProperty
+ ...
+ fun:*QWidget*setVisible*
...
}
{
- dlopen doing its magic
+ Ubuntu 16.04 - Qt + Fontconfig
Memcheck:Leak
- match-leak-kinds: reachable
...
- fun:_dl_open
+ obj:*/libfontconfig.*
+ obj:*/libfontconfig.*
+ ...
+ fun:FcFontRenderPrepare
...
}
{
- X11 false positive
+ Ubuntu 16.04 - Qt + Fontconfig
Memcheck:Leak
- match-leak-kinds: reachable
...
- obj:/usr/*/libX11.so.6.3.0
+ obj:*/libfontconfig.*
+ obj:*/libfontconfig.*
...
- fun:_XrmInitParseInfo
+ fun:FcPatternAddInteger
...
}
{
- OpenSSL false positive
+ Ubuntu 16.04 - Qt + Fontconfig
Memcheck:Leak
...
- fun:CRYPTO_malloc
+ obj:*/libfontconfig.*
+ obj:*/libfontconfig.*
+ ...
+ fun:FcConfigParseAndLoad
...
}
{
- OpenSSL false positive
+ Ubuntu 16.04 - Qt + Dbus
Memcheck:Leak
...
- fun:CRYPTO_realloc
+ fun:dbus_connection_send_with_reply
...
+ fun:*QWidget*
}
{
- Libcurl false positive
+ Ubuntu 16.04 - Qt + Dbus
+ Memcheck:Leak
+ ...
+ fun:px_proxy_factory_get_proxies
+ obj:*/libQt5Network.*
+}
+{
+ Ubuntu 16.04 - Qt
Memcheck:Leak
- fun:malloc
...
- obj:/usr/*/libcurl.so.4.3.0
- fun:curl_multi_socket_action
+ fun:*QGuiApplicationPrivate*createEventDispatcher*
+ fun:*QCoreApplication*init*
+ ...
+}
+{
+ Ubuntu 16.04 - Qt
+ Memcheck:Leak
+ ...
+ fun:*QWidget*setWindowTitle*
+ ...
+}
+{
+ Ubuntu 16.04 - Qt
+ Memcheck:Leak
+ ...
+ fun:*QNetworkConfigurationManagerPrivate*updateConfigurations*
+ ...
+}
+{
+ Ubuntu 16.04 - Mysterious leak when running utests
+ Memcheck:Leak
+ fun:calloc
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+}
+{
+ Ubuntu 16.04 - Mysterious leak when running utests
+ Memcheck:Leak
+ fun:malloc
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+ obj:*
+}
+{
+ Ubuntu 14.04 - Travis CI bot using swrast
+ Memcheck:Cond
+ fun:do_stencil_test
+ fun:_swrast_stencil_and_ztest_span
+ fun:_swrast_write_rgba_span
+ fun:draw_wide_line
...
}
diff --git a/src/csscolorparser/csscolorparser.cpp b/src/csscolorparser/csscolorparser.cpp
index 8281eb4202..592f10f025 100644
--- a/src/csscolorparser/csscolorparser.cpp
+++ b/src/csscolorparser/csscolorparser.cpp
@@ -30,7 +30,7 @@
#include <cmath>
#include <algorithm>
-using namespace CSSColorParser;
+namespace CSSColorParser {
// http://www.w3.org/TR/css3-color/
struct NamedColor { const char *const name; const Color color; };
@@ -180,7 +180,7 @@ std::vector<std::string> split(const std::string& s, char delim) {
return elems;
}
-Color CSSColorParser::parse(const std::string& css_str) {
+Color parse(const std::string& css_str) {
std::string str = css_str;
// Remove all whitespace, not compliant, but should just be more accepting.
@@ -288,3 +288,5 @@ Color CSSColorParser::parse(const std::string& css_str) {
return {};
}
+
+} // namespace CSSColorParser
diff --git a/src/mbgl/algorithm/generate_clip_ids.cpp b/src/mbgl/algorithm/generate_clip_ids.cpp
index 208db797d0..74e0ee242f 100644
--- a/src/mbgl/algorithm/generate_clip_ids.cpp
+++ b/src/mbgl/algorithm/generate_clip_ids.cpp
@@ -2,7 +2,6 @@
#include <mbgl/algorithm/covered_by_children.hpp>
#include <mbgl/util/std.hpp>
-#include <mbgl/tile/tile.hpp>
#include <list>
#include <vector>
diff --git a/src/mbgl/algorithm/update_renderables.hpp b/src/mbgl/algorithm/update_renderables.hpp
index 22d4205d90..d47a919bae 100644
--- a/src/mbgl/algorithm/update_renderables.hpp
+++ b/src/mbgl/algorithm/update_renderables.hpp
@@ -1,24 +1,24 @@
#pragma once
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/range.hpp>
#include <set>
namespace mbgl {
namespace algorithm {
-template <typename GetTileDataFn,
- typename CreateTileDataFn,
- typename RetainTileDataFn,
+template <typename GetTileFn,
+ typename CreateTileFn,
+ typename RetainTileFn,
typename RenderTileFn,
- typename IdealTileIDs,
- typename SourceInfo>
-void updateRenderables(GetTileDataFn getTileData,
- CreateTileDataFn createTileData,
- RetainTileDataFn retainTileData,
+ typename IdealTileIDs>
+void updateRenderables(GetTileFn getTile,
+ CreateTileFn createTile,
+ RetainTileFn retainTile,
RenderTileFn renderTile,
const IdealTileIDs& idealTileIDs,
- const SourceInfo& info,
+ const Range<uint8_t>& zoomRange,
const uint8_t dataTileZoom) {
std::set<UnwrappedTileID> checked;
bool covered;
@@ -26,32 +26,34 @@ void updateRenderables(GetTileDataFn getTileData,
// for (all in the set of ideal tiles of the source) {
for (const auto& idealRenderTileID : idealTileIDs) {
- assert(idealRenderTileID.canonical.z >= info.minZoom);
- assert(idealRenderTileID.canonical.z <= info.maxZoom);
+ assert(idealRenderTileID.canonical.z >= zoomRange.min);
+ assert(idealRenderTileID.canonical.z <= zoomRange.max);
assert(dataTileZoom >= idealRenderTileID.canonical.z);
const OverscaledTileID idealDataTileID(dataTileZoom, idealRenderTileID.canonical);
- auto data = getTileData(idealDataTileID);
+ auto data = getTile(idealDataTileID);
if (!data) {
- data = createTileData(idealDataTileID);
+ data = createTile(idealDataTileID);
assert(data);
}
// if (source has the tile and bucket is loaded) {
if (data->isRenderable()) {
- retainTileData(*data);
+ retainTile(*data, true);
renderTile(idealRenderTileID, *data);
} else {
+ bool triedPrevious = data->hasTriedOptional();
+
// The tile isn't loaded yet, but retain it anyway because it's an ideal tile.
- retainTileData(*data);
+ retainTile(*data, true);
covered = true;
overscaledZ = dataTileZoom + 1;
- if (overscaledZ > info.maxZoom) {
+ if (overscaledZ > zoomRange.max) {
// We're looking for an overzoomed child tile.
const auto childDataTileID = idealDataTileID.scaledTo(overscaledZ);
- data = getTileData(childDataTileID);
+ data = getTile(childDataTileID);
if (data && data->isRenderable()) {
- retainTileData(*data);
+ retainTile(*data, false);
renderTile(idealRenderTileID, *data);
} else {
covered = false;
@@ -60,9 +62,9 @@ void updateRenderables(GetTileDataFn getTileData,
// Check all four actual child tiles.
for (const auto& childTileID : idealDataTileID.canonical.children()) {
const OverscaledTileID childDataTileID(overscaledZ, childTileID);
- data = getTileData(childDataTileID);
+ data = getTile(childDataTileID);
if (data && data->isRenderable()) {
- retainTileData(*data);
+ retainTile(*data, false);
renderTile(childDataTileID.unwrapTo(idealRenderTileID.wrap), *data);
} else {
// At least one child tile doesn't exist, so we are going to look for
@@ -74,7 +76,7 @@ void updateRenderables(GetTileDataFn getTileData,
if (!covered) {
// We couldn't find child tiles that entirely cover the ideal tile.
- for (overscaledZ = dataTileZoom - 1; overscaledZ >= info.minZoom; --overscaledZ) {
+ for (overscaledZ = dataTileZoom - 1; overscaledZ >= zoomRange.min; --overscaledZ) {
const auto parentDataTileID = idealDataTileID.scaledTo(overscaledZ);
const auto parentRenderTileID =
parentDataTileID.unwrapTo(idealRenderTileID.wrap);
@@ -87,12 +89,20 @@ void updateRenderables(GetTileDataFn getTileData,
checked.emplace(parentRenderTileID);
}
- data = getTileData(parentDataTileID);
- if (data && data->isRenderable()) {
- retainTileData(*data);
- renderTile(parentRenderTileID, *data);
- // Break parent tile ascent, since we found one.
- break;
+ data = getTile(parentDataTileID);
+ if (!data && triedPrevious) {
+ data = createTile(parentDataTileID);
+ }
+
+ if (data) {
+ triedPrevious = data->hasTriedOptional();
+ retainTile(*data, false);
+
+ if (data->isRenderable()) {
+ renderTile(parentRenderTileID, *data);
+ // Break parent tile ascent, since we found one.
+ break;
+ }
}
}
}
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp
index 9a4f0a21ce..0cd6bdf231 100644
--- a/src/mbgl/annotation/annotation_manager.cpp
+++ b/src/mbgl/annotation/annotation_manager.cpp
@@ -1,10 +1,10 @@
#include <mbgl/annotation/annotation_manager.hpp>
+#include <mbgl/annotation/annotation_source.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/annotation/line_annotation_impl.hpp>
#include <mbgl/annotation/fill_annotation_impl.hpp>
#include <mbgl/annotation/style_sourced_annotation_impl.hpp>
-#include <mbgl/style/source.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
@@ -31,10 +31,9 @@ AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation, cons
return id;
}
-void AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
- removeAnnotation(id);
- Annotation::visit(annotation, [&] (const auto& annotation_) {
- this->add(id, annotation_, maxZoom);
+Update AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
+ return Annotation::visit(annotation, [&] (const auto& annotation_) {
+ return this->update(id, annotation_, maxZoom);
});
}
@@ -69,6 +68,53 @@ void AnnotationManager::add(const AnnotationID& id, const StyleSourcedAnnotation
std::make_unique<StyleSourcedAnnotationImpl>(id, annotation, maxZoom));
}
+Update AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) {
+ auto it = symbolAnnotations.find(id);
+ if (it == symbolAnnotations.end()) {
+ removeAndAdd(id, annotation, maxZoom);
+ return Update::AnnotationData | Update::AnnotationStyle;
+ }
+
+ Update result = Update::Nothing;
+ const SymbolAnnotation& existing = it->second->annotation;
+
+ if (existing.geometry != annotation.geometry) {
+ result |= Update::AnnotationData;
+ }
+
+ if (existing.icon != annotation.icon) {
+ result |= Update::AnnotationData | Update::AnnotationStyle;
+ }
+
+ if (result != Update::Nothing) {
+ removeAndAdd(id, annotation, maxZoom);
+ }
+
+ return result;
+}
+
+Update AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
+ removeAndAdd(id, annotation, maxZoom);
+ return Update::AnnotationData | Update::AnnotationStyle;
+}
+
+Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
+ removeAndAdd(id, annotation, maxZoom);
+ return Update::AnnotationData | Update::AnnotationStyle;
+}
+
+Update AnnotationManager::update(const AnnotationID& id, const StyleSourcedAnnotation& annotation, const uint8_t maxZoom) {
+ removeAndAdd(id, annotation, maxZoom);
+ return Update::AnnotationData | Update::AnnotationStyle;
+}
+
+void AnnotationManager::removeAndAdd(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
+ removeAnnotation(id);
+ Annotation::visit(annotation, [&] (const auto& annotation_) {
+ this->add(id, annotation_, maxZoom);
+ });
+}
+
AnnotationIDs AnnotationManager::getPointAnnotationsInBounds(const LatLngBounds& bounds) const {
AnnotationIDs result;
@@ -80,13 +126,13 @@ AnnotationIDs AnnotationManager::getPointAnnotationsInBounds(const LatLngBounds&
return result;
}
-std::unique_ptr<AnnotationTile> AnnotationManager::getTile(const CanonicalTileID& tileID) {
+std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const CanonicalTileID& tileID) {
if (symbolAnnotations.empty() && shapeAnnotations.empty())
return nullptr;
- auto tile = std::make_unique<AnnotationTile>();
+ auto tileData = std::make_unique<AnnotationTileData>();
- AnnotationTileLayer& pointLayer = *tile->layers.emplace(
+ AnnotationTileLayer& pointLayer = *tileData->layers.emplace(
PointLayerID,
std::make_unique<AnnotationTileLayer>(PointLayerID)).first->second;
@@ -98,24 +144,22 @@ std::unique_ptr<AnnotationTile> AnnotationManager::getTile(const CanonicalTileID
}));
for (const auto& shape : shapeAnnotations) {
- shape.second->updateTile(tileID, *tile);
+ shape.second->updateTileData(tileID, *tileData);
}
- return tile;
+ return tileData;
}
void AnnotationManager::updateStyle(Style& style) {
// Create annotation source, point layer, and point bucket
if (!style.getSource(SourceID)) {
- auto tileset = std::make_unique<Tileset>();
- tileset->maxZoom = 18;
- std::unique_ptr<Source> source = std::make_unique<Source>(SourceType::Annotations, SourceID, "", util::tileSize, std::move(tileset), nullptr);
- source->enabled = true;
+ std::unique_ptr<Source> source = std::make_unique<AnnotationSource>();
+ source->baseImpl->enabled = true;
style.addSource(std::move(source));
- std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>(PointLayerID);
+ std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>(PointLayerID, SourceID);
- layer->setSource(SourceID, PointLayerID);
+ layer->setSourceLayer(PointLayerID);
layer->setIconImage({"{sprite}"});
layer->setIconAllowOverlap(true);
@@ -135,26 +179,28 @@ void AnnotationManager::updateStyle(Style& style) {
}
obsoleteShapeAnnotationLayers.clear();
+}
- for (auto& monitor : monitors) {
- monitor->update(getTile(monitor->tileID.canonical));
+void AnnotationManager::updateData() {
+ for (auto& tile : tiles) {
+ tile->setData(getTileData(tile->id.canonical));
}
}
-void AnnotationManager::addTileMonitor(AnnotationTileMonitor& monitor) {
- monitors.insert(&monitor);
- monitor.update(getTile(monitor.tileID.canonical));
+void AnnotationManager::addTile(AnnotationTile& tile) {
+ tiles.insert(&tile);
+ tile.setData(getTileData(tile.id.canonical));
}
-void AnnotationManager::removeTileMonitor(AnnotationTileMonitor& monitor) {
- monitors.erase(&monitor);
+void AnnotationManager::removeTile(AnnotationTile& tile) {
+ tiles.erase(&tile);
}
void AnnotationManager::addIcon(const std::string& name, std::shared_ptr<const SpriteImage> sprite) {
spriteStore.setSprite(name, sprite);
spriteAtlas.updateDirty();
}
-
+
void AnnotationManager::removeIcon(const std::string& name) {
spriteStore.removeSprite(name);
spriteAtlas.updateDirty();
diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp
index 7e0cf3a394..ffe6cd163a 100644
--- a/src/mbgl/annotation/annotation_manager.hpp
+++ b/src/mbgl/annotation/annotation_manager.hpp
@@ -4,7 +4,7 @@
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/util/geo.hpp>
+#include <mbgl/map/update.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <string>
@@ -14,14 +14,15 @@
namespace mbgl {
+class LatLngBounds;
class AnnotationTile;
-class AnnotationTileMonitor;
+class AnnotationTileData;
class SymbolAnnotationImpl;
class ShapeAnnotationImpl;
namespace style {
class Style;
-}
+} // namespace style
class AnnotationManager : private util::noncopyable {
public:
@@ -29,7 +30,7 @@ public:
~AnnotationManager();
AnnotationID addAnnotation(const Annotation&, const uint8_t maxZoom);
- void updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom);
+ Update updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom);
void removeAnnotation(const AnnotationID&);
AnnotationIDs getPointAnnotationsInBounds(const LatLngBounds&) const;
@@ -40,9 +41,10 @@ public:
SpriteAtlas& getSpriteAtlas() { return spriteAtlas; }
void updateStyle(style::Style&);
+ void updateData();
- void addTileMonitor(AnnotationTileMonitor&);
- void removeTileMonitor(AnnotationTileMonitor&);
+ void addTile(AnnotationTile&);
+ void removeTile(AnnotationTile&);
static const std::string SourceID;
static const std::string PointLayerID;
@@ -53,7 +55,14 @@ private:
void add(const AnnotationID&, const FillAnnotation&, const uint8_t);
void add(const AnnotationID&, const StyleSourcedAnnotation&, const uint8_t);
- std::unique_ptr<AnnotationTile> getTile(const CanonicalTileID&);
+ Update update(const AnnotationID&, const SymbolAnnotation&, const uint8_t);
+ Update update(const AnnotationID&, const LineAnnotation&, const uint8_t);
+ Update update(const AnnotationID&, const FillAnnotation&, const uint8_t);
+ Update update(const AnnotationID&, const StyleSourcedAnnotation&, const uint8_t);
+
+ void removeAndAdd(const AnnotationID&, const Annotation&, const uint8_t);
+
+ std::unique_ptr<AnnotationTileData> getTileData(const CanonicalTileID&);
AnnotationID nextID = 0;
@@ -67,7 +76,7 @@ private:
SymbolAnnotationMap symbolAnnotations;
ShapeAnnotationMap shapeAnnotations;
std::vector<std::string> obsoleteShapeAnnotationLayers;
- std::set<AnnotationTileMonitor*> monitors;
+ std::set<AnnotationTile*> tiles;
SpriteStore spriteStore;
SpriteAtlas spriteAtlas;
diff --git a/src/mbgl/annotation/annotation_source.cpp b/src/mbgl/annotation/annotation_source.cpp
new file mode 100644
index 0000000000..61fc4ca2e4
--- /dev/null
+++ b/src/mbgl/annotation/annotation_source.cpp
@@ -0,0 +1,30 @@
+#include <mbgl/annotation/annotation_source.hpp>
+#include <mbgl/annotation/annotation_manager.hpp>
+#include <mbgl/annotation/annotation_tile.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+AnnotationSource::AnnotationSource()
+ : Source(SourceType::Annotations, std::make_unique<Impl>(*this)) {
+}
+
+AnnotationSource::Impl::Impl(Source& base_)
+ : Source::Impl(SourceType::Annotations, AnnotationManager::SourceID, base_) {
+}
+
+Range<uint8_t> AnnotationSource::Impl::getZoomRange() {
+ return { 0, 22 };
+}
+
+void AnnotationSource::Impl::load(FileSource&) {
+ loaded = true;
+}
+
+std::unique_ptr<Tile> AnnotationSource::Impl::createTile(const OverscaledTileID& tileID,
+ const style::UpdateParameters& parameters) {
+ return std::make_unique<AnnotationTile>(tileID, parameters);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_source.hpp b/src/mbgl/annotation/annotation_source.hpp
new file mode 100644
index 0000000000..db9221788f
--- /dev/null
+++ b/src/mbgl/annotation/annotation_source.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+#include <mbgl/style/source_impl.hpp>
+
+namespace mbgl {
+
+class AnnotationSource : public style::Source {
+public:
+ AnnotationSource();
+
+ class Impl;
+};
+
+class AnnotationSource::Impl : public style::Source::Impl {
+public:
+ Impl(Source&);
+
+ void load(FileSource&) final;
+
+private:
+ uint16_t getTileSize() const final { return util::tileSize; }
+ Range<uint8_t> getZoomRange() final;
+
+ std::unique_ptr<Tile> createTile(const OverscaledTileID&, const style::UpdateParameters&) final;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp
index ba4b69108c..91b7f7ddc1 100644
--- a/src/mbgl/annotation/annotation_tile.cpp
+++ b/src/mbgl/annotation/annotation_tile.cpp
@@ -2,11 +2,25 @@
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/storage/file_source.hpp>
+#include <mbgl/style/update_parameters.hpp>
#include <utility>
namespace mbgl {
+AnnotationTile::AnnotationTile(const OverscaledTileID& overscaledTileID,
+ const style::UpdateParameters& parameters)
+ : GeometryTile(overscaledTileID, AnnotationManager::SourceID, parameters.style, parameters.mode),
+ annotationManager(parameters.annotationManager) {
+ annotationManager.addTile(*this);
+}
+
+AnnotationTile::~AnnotationTile() {
+ annotationManager.removeTile(*this);
+}
+
+void AnnotationTile::setNecessity(Necessity) {}
+
AnnotationTileFeature::AnnotationTileFeature(FeatureType type_, GeometryCollection geometries_,
std::unordered_map<std::string, std::string> properties_)
: type(type_),
@@ -21,10 +35,10 @@ optional<Value> AnnotationTileFeature::getValue(const std::string& key) const {
return optional<Value>();
}
-AnnotationTileLayer::AnnotationTileLayer(const std::string &name_)
- : name(name_) {}
+AnnotationTileLayer::AnnotationTileLayer(std::string name_)
+ : name(std::move(name_)) {}
-util::ptr<GeometryTileLayer> AnnotationTile::getLayer(const std::string& name) const {
+util::ptr<GeometryTileLayer> AnnotationTileData::getLayer(const std::string& name) const {
auto it = layers.find(name);
if (it != layers.end()) {
return it->second;
@@ -32,23 +46,4 @@ util::ptr<GeometryTileLayer> AnnotationTile::getLayer(const std::string& name) c
return nullptr;
}
-AnnotationTileMonitor::AnnotationTileMonitor(const OverscaledTileID& tileID_, AnnotationManager& annotationManager_)
- : tileID(tileID_),
- annotationManager(annotationManager_) {
-}
-
-AnnotationTileMonitor::~AnnotationTileMonitor() {
- annotationManager.removeTileMonitor(*this);
-}
-
-std::unique_ptr<AsyncRequest> AnnotationTileMonitor::monitorTile(const GeometryTileMonitor::Callback& callback_) {
- callback = callback_;
- annotationManager.addTileMonitor(*this);
- return nullptr;
-}
-
-void AnnotationTileMonitor::update(std::unique_ptr<GeometryTile> tile) {
- callback(nullptr, std::move(tile), {}, {});
-}
-
} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp
index d607d563d4..3e7c2c447f 100644
--- a/src/mbgl/annotation/annotation_tile.hpp
+++ b/src/mbgl/annotation/annotation_tile.hpp
@@ -1,13 +1,28 @@
#pragma once
#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/tile/tile_id.hpp>
-
-#include <map>
-#include <unordered_map>
+#include <mbgl/tile/geometry_tile_data.hpp>
namespace mbgl {
+class AnnotationManager;
+
+namespace style {
+class UpdateParameters;
+} // namespace style
+
+class AnnotationTile : public GeometryTile {
+public:
+ AnnotationTile(const OverscaledTileID&,
+ const style::UpdateParameters&);
+ ~AnnotationTile() override;
+
+ void setNecessity(Necessity) final;
+
+private:
+ AnnotationManager& annotationManager;
+};
+
class AnnotationTileFeature : public GeometryTileFeature {
public:
AnnotationTileFeature(FeatureType, GeometryCollection,
@@ -24,7 +39,7 @@ public:
class AnnotationTileLayer : public GeometryTileLayer {
public:
- AnnotationTileLayer(const std::string&);
+ AnnotationTileLayer(std::string);
std::size_t featureCount() const override { return features.size(); }
util::ptr<const GeometryTileFeature> getFeature(std::size_t i) const override { return features[i]; }
@@ -36,28 +51,11 @@ private:
std::string name;
};
-class AnnotationTile : public GeometryTile {
+class AnnotationTileData : public GeometryTileData {
public:
util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
- std::map<std::string, util::ptr<AnnotationTileLayer>> layers;
-};
-
-class AnnotationManager;
-
-class AnnotationTileMonitor : public GeometryTileMonitor {
-public:
- AnnotationTileMonitor(const OverscaledTileID&, AnnotationManager&);
- ~AnnotationTileMonitor();
-
- void update(std::unique_ptr<GeometryTile>);
- std::unique_ptr<AsyncRequest> monitorTile(const GeometryTileMonitor::Callback&) override;
-
- OverscaledTileID tileID;
-
-private:
- AnnotationManager& annotationManager;
- GeometryTileMonitor::Callback callback;
+ std::unordered_map<std::string, util::ptr<AnnotationTileLayer>> layers;
};
} // namespace mbgl
diff --git a/src/mbgl/annotation/fill_annotation_impl.cpp b/src/mbgl/annotation/fill_annotation_impl.cpp
index 31c9f6d720..fe520451f7 100644
--- a/src/mbgl/annotation/fill_annotation_impl.cpp
+++ b/src/mbgl/annotation/fill_annotation_impl.cpp
@@ -7,18 +7,17 @@ namespace mbgl {
using namespace style;
-FillAnnotationImpl::FillAnnotationImpl(const AnnotationID id_, const FillAnnotation& annotation_, const uint8_t maxZoom_)
+FillAnnotationImpl::FillAnnotationImpl(AnnotationID id_, FillAnnotation annotation_, uint8_t maxZoom_)
: ShapeAnnotationImpl(id_, maxZoom_),
- annotation(annotation_) {
+ annotation({ ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.color, annotation_.outlineColor }) {
}
void FillAnnotationImpl::updateStyle(Style& style) const {
if (style.getLayer(layerID))
return;
- std::unique_ptr<FillLayer> layer = std::make_unique<FillLayer>(layerID);
- layer->setSource(AnnotationManager::SourceID, layerID);
-
+ std::unique_ptr<FillLayer> layer = std::make_unique<FillLayer>(layerID, AnnotationManager::SourceID);
+ layer->setSourceLayer(layerID);
layer->setFillOpacity(annotation.opacity);
layer->setFillColor(annotation.color);
layer->setFillOutlineColor(annotation.outlineColor);
diff --git a/src/mbgl/annotation/fill_annotation_impl.hpp b/src/mbgl/annotation/fill_annotation_impl.hpp
index b879860c08..6376eee880 100644
--- a/src/mbgl/annotation/fill_annotation_impl.hpp
+++ b/src/mbgl/annotation/fill_annotation_impl.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class FillAnnotationImpl : public ShapeAnnotationImpl {
public:
- FillAnnotationImpl(const AnnotationID, const FillAnnotation&, const uint8_t maxZoom);
+ FillAnnotationImpl(AnnotationID, FillAnnotation, uint8_t maxZoom);
void updateStyle(style::Style&) const final;
const ShapeAnnotationGeometry& geometry() const final;
diff --git a/src/mbgl/annotation/line_annotation_impl.cpp b/src/mbgl/annotation/line_annotation_impl.cpp
index 85177591f4..f18ca9fadc 100644
--- a/src/mbgl/annotation/line_annotation_impl.cpp
+++ b/src/mbgl/annotation/line_annotation_impl.cpp
@@ -7,17 +7,17 @@ namespace mbgl {
using namespace style;
-LineAnnotationImpl::LineAnnotationImpl(const AnnotationID id_, const LineAnnotation& annotation_, const uint8_t maxZoom_)
+LineAnnotationImpl::LineAnnotationImpl(AnnotationID id_, LineAnnotation annotation_, uint8_t maxZoom_)
: ShapeAnnotationImpl(id_, maxZoom_),
- annotation(annotation_) {
+ annotation({ ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.width, annotation_.color }) {
}
void LineAnnotationImpl::updateStyle(Style& style) const {
if (style.getLayer(layerID))
return;
- std::unique_ptr<LineLayer> layer = std::make_unique<LineLayer>(layerID);
- layer->setSource(AnnotationManager::SourceID, layerID);
+ std::unique_ptr<LineLayer> layer = std::make_unique<LineLayer>(layerID, AnnotationManager::SourceID);
+ layer->setSourceLayer(layerID);
layer->setLineJoin(LineJoinType::Round);
layer->setLineOpacity(annotation.opacity);
layer->setLineWidth(annotation.width);
diff --git a/src/mbgl/annotation/line_annotation_impl.hpp b/src/mbgl/annotation/line_annotation_impl.hpp
index c9a37dd390..7945da5d97 100644
--- a/src/mbgl/annotation/line_annotation_impl.hpp
+++ b/src/mbgl/annotation/line_annotation_impl.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class LineAnnotationImpl : public ShapeAnnotationImpl {
public:
- LineAnnotationImpl(const AnnotationID, const LineAnnotation&, const uint8_t maxZoom);
+ LineAnnotationImpl(AnnotationID, LineAnnotation, uint8_t maxZoom);
void updateStyle(style::Style&) const final;
const ShapeAnnotationGeometry& geometry() const final;
diff --git a/src/mbgl/annotation/shape_annotation_impl.cpp b/src/mbgl/annotation/shape_annotation_impl.cpp
index be6e12558d..445ee76511 100644
--- a/src/mbgl/annotation/shape_annotation_impl.cpp
+++ b/src/mbgl/annotation/shape_annotation_impl.cpp
@@ -1,5 +1,3 @@
-#include <mapbox/geojsonvt/convert.hpp>
-
#include <mbgl/annotation/shape_annotation_impl.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/tile/tile_id.hpp>
@@ -7,6 +5,7 @@
#include <mbgl/math/clamp.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/constants.hpp>
+#include <mbgl/util/geometry.hpp>
namespace mbgl {
@@ -19,83 +18,14 @@ ShapeAnnotationImpl::ShapeAnnotationImpl(const AnnotationID id_, const uint8_t m
layerID("com.mapbox.annotations.shape." + util::toString(id)) {
}
-struct ToGeoJSONVT {
- const double tolerance;
-
- ToGeoJSONVT(const double tolerance_)
- : tolerance(tolerance_) {
- }
-
- geojsonvt::ProjectedFeature operator()(const LineString<double>& line) const {
- geojsonvt::ProjectedRings converted;
- converted.push_back(convertPoints(geojsonvt::ProjectedFeatureType::LineString, line));
- return convertFeature(geojsonvt::ProjectedFeatureType::LineString, converted);
- }
-
- geojsonvt::ProjectedFeature operator()(const Polygon<double>& polygon) const {
- geojsonvt::ProjectedRings converted;
- for (const auto& ring : polygon) {
- converted.push_back(convertPoints(geojsonvt::ProjectedFeatureType::Polygon, ring));
- }
- return convertFeature(geojsonvt::ProjectedFeatureType::Polygon, converted);
- }
-
- geojsonvt::ProjectedFeature operator()(const MultiLineString<double>& lines) const {
- geojsonvt::ProjectedRings converted;
- for (const auto& line : lines) {
- converted.push_back(convertPoints(geojsonvt::ProjectedFeatureType::LineString, line));
- }
- return convertFeature(geojsonvt::ProjectedFeatureType::LineString, converted);
- }
-
- geojsonvt::ProjectedFeature operator()(const MultiPolygon<double>& polygons) const {
- geojsonvt::ProjectedRings converted;
- for (const auto& polygon : polygons) {
- for (const auto& ring : polygon) {
- converted.push_back(convertPoints(geojsonvt::ProjectedFeatureType::Polygon, ring));
- }
- }
- return convertFeature(geojsonvt::ProjectedFeatureType::Polygon, converted);
- }
-
-private:
- geojsonvt::LonLat convertPoint(const Point<double>& p) const {
- return {
- util::wrap(p.x, -util::LONGITUDE_MAX, util::LONGITUDE_MAX),
- util::clamp(p.y, -util::LATITUDE_MAX, util::LATITUDE_MAX)
- };
- }
-
- geojsonvt::ProjectedRing convertPoints(geojsonvt::ProjectedFeatureType type, const std::vector<Point<double>>& points) const {
- std::vector<geojsonvt::LonLat> converted;
-
- for (const auto& p : points) {
- converted.push_back(convertPoint(p));
- }
-
- if (type == geojsonvt::ProjectedFeatureType::Polygon && points.front() != points.back()) {
- converted.push_back(converted.front());
- }
-
- return geojsonvt::Convert::projectRing(converted, tolerance);
- }
-
- geojsonvt::ProjectedFeature convertFeature(geojsonvt::ProjectedFeatureType type, const geojsonvt::ProjectedRings& rings) const {
- return geojsonvt::Convert::create(geojsonvt::Tags(), type, rings);
- }
-};
-
-void ShapeAnnotationImpl::updateTile(const CanonicalTileID& tileID, AnnotationTile& tile) {
+void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, AnnotationTileData& data) {
static const double baseTolerance = 4;
if (!shapeTiler) {
- const uint64_t maxAmountOfTiles = 1 << maxZoom;
- const double tolerance = baseTolerance / (maxAmountOfTiles * util::EXTENT);
-
- std::vector<geojsonvt::ProjectedFeature> features = {
- ShapeAnnotationGeometry::visit(geometry(), ToGeoJSONVT(tolerance))
- };
-
+ mapbox::geometry::feature_collection<double> features;
+ features.emplace_back(ShapeAnnotationGeometry::visit(geometry(), [] (auto&& geom) {
+ return Feature { std::move(geom) };
+ }));
mapbox::geojsonvt::Options options;
options.maxZoom = maxZoom;
options.buffer = 255u;
@@ -105,34 +35,20 @@ void ShapeAnnotationImpl::updateTile(const CanonicalTileID& tileID, AnnotationTi
}
const auto& shapeTile = shapeTiler->getTile(tileID.z, tileID.x, tileID.y);
- if (!shapeTile)
+ if (shapeTile.features.empty())
return;
- AnnotationTileLayer& layer = *tile.layers.emplace(layerID,
+ AnnotationTileLayer& layer = *data.layers.emplace(layerID,
std::make_unique<AnnotationTileLayer>(layerID)).first->second;
- for (auto& shapeFeature : shapeTile.features) {
- FeatureType featureType = FeatureType::Unknown;
-
- if (shapeFeature.type == geojsonvt::TileFeatureType::LineString) {
- featureType = FeatureType::LineString;
- } else if (shapeFeature.type == geojsonvt::TileFeatureType::Polygon) {
- featureType = FeatureType::Polygon;
- }
+ ToGeometryCollection toGeometryCollection;
+ ToFeatureType toFeatureType;
+ for (const auto& shapeFeature : shapeTile.features) {
+ FeatureType featureType = apply_visitor(toFeatureType, shapeFeature.geometry);
+ GeometryCollection renderGeometry = apply_visitor(toGeometryCollection, shapeFeature.geometry);
assert(featureType != FeatureType::Unknown);
- GeometryCollection renderGeometry;
- for (auto& shapeRing : shapeFeature.tileGeometry.get<geojsonvt::TileRings>()) {
- GeometryCoordinates renderLine;
-
- for (auto& shapePoint : shapeRing) {
- renderLine.emplace_back(shapePoint.x, shapePoint.y);
- }
-
- renderGeometry.push_back(renderLine);
- }
-
// https://github.com/mapbox/geojson-vt-cpp/issues/44
if (featureType == FeatureType::Polygon) {
renderGeometry = fixupPolygons(renderGeometry);
diff --git a/src/mbgl/annotation/shape_annotation_impl.hpp b/src/mbgl/annotation/shape_annotation_impl.hpp
index e6ba9a4bd7..800b4ec313 100644
--- a/src/mbgl/annotation/shape_annotation_impl.hpp
+++ b/src/mbgl/annotation/shape_annotation_impl.hpp
@@ -3,18 +3,19 @@
#include <mapbox/geojsonvt.hpp>
#include <mbgl/annotation/annotation.hpp>
+#include <mbgl/util/geometry.hpp>
#include <string>
#include <memory>
namespace mbgl {
-class AnnotationTile;
+class AnnotationTileData;
class CanonicalTileID;
namespace style {
class Style;
-}
+} // namespace style
class ShapeAnnotationImpl {
public:
@@ -24,7 +25,7 @@ public:
virtual void updateStyle(style::Style&) const = 0;
virtual const ShapeAnnotationGeometry& geometry() const = 0;
- void updateTile(const CanonicalTileID&, AnnotationTile&);
+ void updateTileData(const CanonicalTileID&, AnnotationTileData&);
const AnnotationID id;
const uint8_t maxZoom;
@@ -32,4 +33,33 @@ public:
std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> shapeTiler;
};
+struct CloseShapeAnnotation {
+ ShapeAnnotationGeometry operator()(const mbgl::LineString<double> &geom) const {
+ return geom;
+ }
+ ShapeAnnotationGeometry operator()(const mbgl::MultiLineString<double> &geom) const {
+ return geom;
+ }
+ ShapeAnnotationGeometry operator()(const mbgl::Polygon<double> &geom) const {
+ mbgl::Polygon<double> closed = geom;
+ for (auto &ring : closed) {
+ if (!ring.empty() && ring.front() != ring.back()) {
+ ring.emplace_back(ring.front());
+ }
+ }
+ return closed;
+ }
+ ShapeAnnotationGeometry operator()(const mbgl::MultiPolygon<double> &geom) const {
+ mbgl::MultiPolygon<double> closed = geom;
+ for (auto &polygon : closed) {
+ for (auto &ring : polygon) {
+ if (!ring.empty() && ring.front() != ring.back()) {
+ ring.emplace_back(ring.front());
+ }
+ }
+ }
+ return closed;
+ }
+};
+
} // namespace mbgl
diff --git a/src/mbgl/annotation/style_sourced_annotation_impl.cpp b/src/mbgl/annotation/style_sourced_annotation_impl.cpp
index 43a27c8aac..d3212da12d 100644
--- a/src/mbgl/annotation/style_sourced_annotation_impl.cpp
+++ b/src/mbgl/annotation/style_sourced_annotation_impl.cpp
@@ -2,6 +2,7 @@
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/layer.hpp>
+#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
@@ -9,9 +10,9 @@ namespace mbgl {
using namespace style;
-StyleSourcedAnnotationImpl::StyleSourcedAnnotationImpl(const AnnotationID id_, const StyleSourcedAnnotation& annotation_, const uint8_t maxZoom_)
+StyleSourcedAnnotationImpl::StyleSourcedAnnotationImpl(AnnotationID id_, StyleSourcedAnnotation annotation_, uint8_t maxZoom_)
: ShapeAnnotationImpl(id_, maxZoom_),
- annotation(annotation_) {
+ annotation(std::move(annotation_)) {
}
void StyleSourcedAnnotationImpl::updateStyle(Style& style) const {
@@ -23,13 +24,13 @@ void StyleSourcedAnnotationImpl::updateStyle(Style& style) const {
return;
if (sourceLayer->is<LineLayer>()) {
- std::unique_ptr<Layer> layer = sourceLayer->copy(layerID, "");
- layer->as<LineLayer>()->setSource(AnnotationManager::SourceID, layerID);
+ std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, "", AnnotationManager::SourceID);
+ layer->as<LineLayer>()->setSourceLayer(layerID);
layer->as<LineLayer>()->setVisibility(VisibilityType::Visible);
style.addLayer(std::move(layer), sourceLayer->getID());
} else if (sourceLayer->is<FillLayer>()) {
- std::unique_ptr<Layer> layer = sourceLayer->copy(layerID, "");
- layer->as<FillLayer>()->setSource(AnnotationManager::SourceID, layerID);
+ std::unique_ptr<Layer> layer = sourceLayer->baseImpl->copy(layerID, "", AnnotationManager::SourceID);
+ layer->as<FillLayer>()->setSourceLayer(layerID);
layer->as<FillLayer>()->setVisibility(VisibilityType::Visible);
style.addLayer(std::move(layer), sourceLayer->getID());
}
diff --git a/src/mbgl/annotation/style_sourced_annotation_impl.hpp b/src/mbgl/annotation/style_sourced_annotation_impl.hpp
index 09ef474fc0..82b947302d 100644
--- a/src/mbgl/annotation/style_sourced_annotation_impl.hpp
+++ b/src/mbgl/annotation/style_sourced_annotation_impl.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class StyleSourcedAnnotationImpl : public ShapeAnnotationImpl {
public:
- StyleSourcedAnnotationImpl(const AnnotationID, const StyleSourcedAnnotation&, const uint8_t maxZoom);
+ StyleSourcedAnnotationImpl(AnnotationID, StyleSourcedAnnotation, uint8_t maxZoom);
void updateStyle(style::Style&) const final;
const ShapeAnnotationGeometry& geometry() const final;
diff --git a/src/mbgl/annotation/symbol_annotation_impl.cpp b/src/mbgl/annotation/symbol_annotation_impl.cpp
index 44a89576bb..5ac2581949 100644
--- a/src/mbgl/annotation/symbol_annotation_impl.cpp
+++ b/src/mbgl/annotation/symbol_annotation_impl.cpp
@@ -1,12 +1,13 @@
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
+#include <mbgl/tile/tile_id.hpp>
#include <mbgl/math/clamp.hpp>
namespace mbgl {
-SymbolAnnotationImpl::SymbolAnnotationImpl(const AnnotationID id_, const SymbolAnnotation& annotation_)
+SymbolAnnotationImpl::SymbolAnnotationImpl(AnnotationID id_, SymbolAnnotation annotation_)
: id(id_),
- annotation(annotation_) {
+ annotation(std::move(annotation_)) {
}
void SymbolAnnotationImpl::updateLayer(const CanonicalTileID& tileID, AnnotationTileLayer& layer) const {
diff --git a/src/mbgl/annotation/symbol_annotation_impl.hpp b/src/mbgl/annotation/symbol_annotation_impl.hpp
index 5dc882ab93..2e98f2414c 100644
--- a/src/mbgl/annotation/symbol_annotation_impl.hpp
+++ b/src/mbgl/annotation/symbol_annotation_impl.hpp
@@ -37,7 +37,7 @@ class CanonicalTileID;
class SymbolAnnotationImpl {
public:
- SymbolAnnotationImpl(const AnnotationID, const SymbolAnnotation&);
+ SymbolAnnotationImpl(AnnotationID, SymbolAnnotation);
void updateLayer(const CanonicalTileID&, AnnotationTileLayer&) const;
@@ -55,7 +55,7 @@ namespace index {
template <>
struct indexable<std::shared_ptr<const mbgl::SymbolAnnotationImpl>> {
using result_type = mbgl::LatLng;
- inline mbgl::LatLng operator()(const std::shared_ptr<const mbgl::SymbolAnnotationImpl>& v) const {
+ mbgl::LatLng operator()(const std::shared_ptr<const mbgl::SymbolAnnotationImpl>& v) const {
const mbgl::Point<double>& p = v->annotation.geometry;
return mbgl::LatLng(p.y, p.x);
}
diff --git a/src/mbgl/geometry/buffer.hpp b/src/mbgl/geometry/buffer.hpp
index 6465b6f8e3..d372a040bf 100644
--- a/src/mbgl/geometry/buffer.hpp
+++ b/src/mbgl/geometry/buffer.hpp
@@ -27,11 +27,11 @@ public:
// Returns the number of elements in this buffer. This is not the number of
// bytes, but rather the number of coordinates with associated information.
- inline GLsizei index() const {
+ GLsizei index() const {
return static_cast<GLsizei>(pos / itemSize);
}
- inline bool empty() const {
+ bool empty() const {
return pos == 0;
}
@@ -65,7 +65,7 @@ public:
}
// Uploads the buffer to the GPU to be available when we need it.
- inline void upload(gl::ObjectStore& store) {
+ void upload(gl::ObjectStore& store) {
if (!buffer) {
bind(store);
}
@@ -73,7 +73,7 @@ public:
protected:
// increase the buffer size by at least /required/ bytes.
- inline void *addElement() {
+ void *addElement() {
if (buffer) {
throw std::runtime_error("Can't add elements after buffer was bound to GPU");
}
diff --git a/src/mbgl/geometry/circle_buffer.cpp b/src/mbgl/geometry/circle_buffer.cpp
index 74ccfa8267..cc31fb83bf 100644
--- a/src/mbgl/geometry/circle_buffer.cpp
+++ b/src/mbgl/geometry/circle_buffer.cpp
@@ -4,10 +4,12 @@
#include <climits>
-using namespace mbgl;
+namespace mbgl {
void CircleVertexBuffer::add(vertex_type x, vertex_type y, float ex, float ey) {
vertex_type *vertices = static_cast<vertex_type *>(addElement());
vertices[0] = (x * 2) + ((ex + 1) / 2);
vertices[1] = (y * 2) + ((ey + 1) / 2);
}
+
+} // namespace mbgl
diff --git a/src/mbgl/geometry/debug_font_buffer.cpp b/src/mbgl/geometry/debug_font_buffer.cpp
index 5df67accd6..f64ce8816b 100644
--- a/src/mbgl/geometry/debug_font_buffer.cpp
+++ b/src/mbgl/geometry/debug_font_buffer.cpp
@@ -5,7 +5,7 @@
#include <cmath>
#include <cstring>
-using namespace mbgl;
+namespace mbgl {
void DebugFontBuffer::addText(const char *text, double left, double baseline, double scale) {
uint16_t *coords = nullptr;
@@ -40,3 +40,5 @@ void DebugFontBuffer::addText(const char *text, double left, double baseline, do
left += glyph.width * scale;
}
}
+
+} // namespace mbgl
diff --git a/src/mbgl/geometry/elements_buffer.cpp b/src/mbgl/geometry/elements_buffer.cpp
index 3e2e2794dd..b7d8cb2015 100644
--- a/src/mbgl/geometry/elements_buffer.cpp
+++ b/src/mbgl/geometry/elements_buffer.cpp
@@ -1,6 +1,6 @@
#include <mbgl/geometry/elements_buffer.hpp>
-using namespace mbgl;
+namespace mbgl {
void TriangleElementsBuffer::add(element_type a, element_type b, element_type c) {
element_type *elements = static_cast<element_type *>(addElement());
@@ -14,3 +14,5 @@ void LineElementsBuffer::add(element_type a, element_type b) {
elements[0] = a;
elements[1] = b;
}
+
+} // namespace mbgl
diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp
index 8c10344915..6d6521917e 100644
--- a/src/mbgl/geometry/feature_index.cpp
+++ b/src/mbgl/geometry/feature_index.cpp
@@ -57,7 +57,7 @@ void FeatureIndex::query(
const double tileSize,
const double scale,
const optional<std::vector<std::string>>& filterLayerIDs,
- const GeometryTile& geometryTile,
+ const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
const style::Style& style) const {
@@ -75,7 +75,7 @@ void FeatureIndex::query(
if (indexedFeature.sortIndex == previousSortIndex) continue;
previousSortIndex = indexedFeature.sortIndex;
- addFeature(result, indexedFeature, queryGeometry, filterLayerIDs, geometryTile, tileID, style, bearing, pixelsToTileUnits);
+ addFeature(result, indexedFeature, queryGeometry, filterLayerIDs, geometryTileData, tileID, style, bearing, pixelsToTileUnits);
}
// query symbol features
@@ -83,7 +83,7 @@ void FeatureIndex::query(
std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(box, scale);
std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols);
for (const auto& symbolFeature : symbolFeatures) {
- addFeature(result, symbolFeature, queryGeometry, filterLayerIDs, geometryTile, tileID, style, bearing, pixelsToTileUnits);
+ addFeature(result, symbolFeature, queryGeometry, filterLayerIDs, geometryTileData, tileID, style, bearing, pixelsToTileUnits);
}
}
@@ -92,7 +92,7 @@ void FeatureIndex::addFeature(
const IndexedSubfeature& indexedFeature,
const GeometryCollection& queryGeometry,
const optional<std::vector<std::string>>& filterLayerIDs,
- const GeometryTile& geometryTile,
+ const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
const style::Style& style,
const float bearing,
@@ -103,7 +103,7 @@ void FeatureIndex::addFeature(
return;
}
- auto sourceLayer = geometryTile.getLayer(indexedFeature.sourceLayerName);
+ auto sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName);
assert(sourceLayer);
auto geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp
index c944a98130..0fddcb4700 100644
--- a/src/mbgl/geometry/feature_index.hpp
+++ b/src/mbgl/geometry/feature_index.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/style/types.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/util/grid_index.hpp>
#include <mbgl/util/feature.hpp>
@@ -13,7 +13,7 @@ namespace mbgl {
namespace style {
class Style;
-}
+} // namespace style
class CollisionTile;
class CanonicalTileID;
@@ -40,7 +40,7 @@ public:
const double tileSize,
const double scale,
const optional<std::vector<std::string>>& layerIDs,
- const GeometryTile&,
+ const GeometryTileData&,
const CanonicalTileID&,
const style::Style&) const;
@@ -61,7 +61,7 @@ private:
const IndexedSubfeature&,
const GeometryCollection& queryGeometry,
const optional<std::vector<std::string>>& filterLayerIDs,
- const GeometryTile&,
+ const GeometryTileData&,
const CanonicalTileID&,
const style::Style&,
const float bearing,
@@ -73,4 +73,4 @@ private:
std::unordered_map<std::string, std::vector<std::string>> bucketLayerIDs;
};
-}
+} // namespace mbgl
diff --git a/src/mbgl/geometry/fill_buffer.cpp b/src/mbgl/geometry/fill_buffer.cpp
index ee70dfc53b..6cb07ea66a 100644
--- a/src/mbgl/geometry/fill_buffer.cpp
+++ b/src/mbgl/geometry/fill_buffer.cpp
@@ -4,10 +4,12 @@
#include <climits>
-using namespace mbgl;
+namespace mbgl {
void FillVertexBuffer::add(vertex_type x, vertex_type y) {
vertex_type *vertices = static_cast<vertex_type *>(addElement());
vertices[0] = x;
vertices[1] = y;
}
+
+} // namespace mbgl
diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp
index b4687b8e07..cfc5962eb3 100644
--- a/src/mbgl/geometry/glyph_atlas.cpp
+++ b/src/mbgl/geometry/glyph_atlas.cpp
@@ -2,6 +2,7 @@
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/object_store.hpp>
+#include <mbgl/gl/gl_config.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
@@ -9,7 +10,7 @@
#include <algorithm>
-using namespace mbgl;
+namespace mbgl {
GlyphAtlas::GlyphAtlas(uint16_t width_, uint16_t height_)
: width(width_),
@@ -19,8 +20,7 @@ GlyphAtlas::GlyphAtlas(uint16_t width_, uint16_t height_)
dirty(true) {
}
-GlyphAtlas::~GlyphAtlas() {
-}
+GlyphAtlas::~GlyphAtlas() = default;
void GlyphAtlas::addGlyphs(uintptr_t tileUID,
const std::u32string& text,
@@ -53,7 +53,7 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID,
const uint8_t buffer = 3;
std::map<uint32_t, GlyphValue>& face = index[fontStack];
- std::map<uint32_t, GlyphValue>::iterator it = face.find(glyph.id);
+ auto it = face.find(glyph.id);
// The glyph is already in this texture.
if (it != face.end()) {
@@ -142,13 +142,14 @@ void GlyphAtlas::removeGlyphs(uintptr_t tileUID) {
}
}
-void GlyphAtlas::upload(gl::ObjectStore& store) {
+void GlyphAtlas::upload(gl::ObjectStore& store, gl::Config& config, uint32_t unit) {
if (dirty) {
const bool first = !texture;
- bind(store);
+ bind(store, config, unit);
std::lock_guard<std::mutex> lock(mtx);
+ config.activeTexture = unit;
if (first) {
MBGL_CHECK_ERROR(glTexImage2D(
GL_TEXTURE_2D, // GLenum target
@@ -183,10 +184,11 @@ void GlyphAtlas::upload(gl::ObjectStore& store) {
}
}
-void GlyphAtlas::bind(gl::ObjectStore& store) {
+void GlyphAtlas::bind(gl::ObjectStore& store, gl::Config& config, uint32_t unit) {
if (!texture) {
texture = store.createTexture();
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
#ifndef GL_ES_VERSION_2_0
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
#endif
@@ -194,7 +196,10 @@ void GlyphAtlas::bind(gl::ObjectStore& store) {
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- } else {
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
+ } else if (config.texture[unit] != *texture) {
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
}
-};
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/geometry/glyph_atlas.hpp b/src/mbgl/geometry/glyph_atlas.hpp
index d3e2a62199..b9170c1caf 100644
--- a/src/mbgl/geometry/glyph_atlas.hpp
+++ b/src/mbgl/geometry/glyph_atlas.hpp
@@ -2,12 +2,12 @@
#include <mbgl/geometry/binpack.hpp>
#include <mbgl/text/glyph_store.hpp>
-#include <mbgl/util/atomic.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/object_store.hpp>
+#include <atomic>
#include <string>
#include <set>
#include <unordered_map>
@@ -15,6 +15,10 @@
namespace mbgl {
+namespace gl {
+class Config;
+} // namespace gl
+
class GlyphAtlas : public util::noncopyable {
public:
GlyphAtlas(uint16_t width, uint16_t height);
@@ -28,19 +32,19 @@ public:
void removeGlyphs(uintptr_t tileUID);
// Binds the atlas texture to the GPU, and uploads data if it is out of date.
- void bind(gl::ObjectStore&);
+ void bind(gl::ObjectStore&, gl::Config&, uint32_t unit);
// Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
// the texture is only bound when the data is out of date (=dirty).
- void upload(gl::ObjectStore&);
+ void upload(gl::ObjectStore&, gl::Config&, uint32_t unit);
const GLsizei width;
const GLsizei height;
private:
struct GlyphValue {
- GlyphValue(const Rect<uint16_t>& rect_, uintptr_t id)
- : rect(rect_), ids({ id }) {}
+ GlyphValue(Rect<uint16_t> rect_, uintptr_t id)
+ : rect(std::move(rect_)), ids({ id }) {}
Rect<uint16_t> rect;
std::set<uintptr_t> ids;
};
@@ -53,7 +57,7 @@ private:
BinPack<uint16_t> bin;
std::unordered_map<FontStack, std::map<uint32_t, GlyphValue>, FontStackHash> index;
const std::unique_ptr<uint8_t[]> data;
- util::Atomic<bool> dirty;
+ std::atomic<bool> dirty;
mbgl::optional<gl::UniqueTexture> texture;
};
diff --git a/src/mbgl/geometry/icon_buffer.cpp b/src/mbgl/geometry/icon_buffer.cpp
index 101132ddbc..51a4aa69fd 100644
--- a/src/mbgl/geometry/icon_buffer.cpp
+++ b/src/mbgl/geometry/icon_buffer.cpp
@@ -6,7 +6,7 @@
namespace mbgl {
-size_t IconVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float minzoom, float maxzoom, float labelminzoom) {
+size_t IconVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float minzoom, float maxzoom, float labelminzoom, uint8_t labelangle) {
const size_t idx = index();
void *data = addElement();
@@ -21,6 +21,7 @@ size_t IconVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, int16_t t
ubytes[8] /* tex */ = tx / 4;
ubytes[9] /* tex */ = ty / 4;
ubytes[10] /* labelminzoom */ = labelminzoom * 10;
+ ubytes[11] /* labelangle */ = labelangle;
// a_data2
ubytes[12] /* minzoom */ = minzoom * 10; // 1/10 zoom levels: z16 == 160.
diff --git a/src/mbgl/geometry/icon_buffer.hpp b/src/mbgl/geometry/icon_buffer.hpp
index 0116686265..3a1d57ccd7 100644
--- a/src/mbgl/geometry/icon_buffer.hpp
+++ b/src/mbgl/geometry/icon_buffer.hpp
@@ -10,7 +10,7 @@ namespace mbgl {
16
> {
public:
- size_t add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float minzoom, float maxzoom, float labelminzoom);
+ size_t add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float minzoom, float maxzoom, float labelminzoom, uint8_t labelangle);
};
diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp
index 830640f780..f08ea1e5fc 100644
--- a/src/mbgl/geometry/line_atlas.cpp
+++ b/src/mbgl/geometry/line_atlas.cpp
@@ -1,6 +1,7 @@
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/object_store.hpp>
+#include <mbgl/gl/gl_config.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
@@ -9,7 +10,7 @@
#include <sstream>
#include <cmath>
-using namespace mbgl;
+namespace mbgl {
LineAtlas::LineAtlas(GLsizei w, GLsizei h)
: width(w),
@@ -18,10 +19,9 @@ LineAtlas::LineAtlas(GLsizei w, GLsizei h)
dirty(true) {
}
-LineAtlas::~LineAtlas() {
-}
+LineAtlas::~LineAtlas() = default;
-LinePatternPos LineAtlas::getDashPosition(const std::vector<float> &dasharray, bool round, gl::ObjectStore& store) {
+LinePatternPos LineAtlas::getDashPosition(const std::vector<float>& dasharray, bool round) {
size_t key = round ? std::numeric_limits<size_t>::min() : std::numeric_limits<size_t>::max();
for (const float part : dasharray) {
boost::hash_combine<float>(key, part);
@@ -30,7 +30,7 @@ LinePatternPos LineAtlas::getDashPosition(const std::vector<float> &dasharray, b
// Note: We're not handling hash collisions here.
const auto it = positions.find(key);
if (it == positions.end()) {
- auto inserted = positions.emplace(key, addDash(dasharray, round, store));
+ auto inserted = positions.emplace(key, addDash(dasharray, round));
assert(inserted.second);
return inserted.first->second;
} else {
@@ -38,8 +38,7 @@ LinePatternPos LineAtlas::getDashPosition(const std::vector<float> &dasharray, b
}
}
-LinePatternPos LineAtlas::addDash(const std::vector<float> &dasharray, bool round, gl::ObjectStore& store) {
-
+LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, bool round) {
int n = round ? 7 : 0;
int dashheight = 2 * n + 1;
const uint8_t offset = 128;
@@ -116,32 +115,34 @@ LinePatternPos LineAtlas::addDash(const std::vector<float> &dasharray, bool roun
nextRow += dashheight;
dirty = true;
- bind(store);
return position;
-};
+}
-void LineAtlas::upload(gl::ObjectStore& store) {
+void LineAtlas::upload(gl::ObjectStore& store, gl::Config& config, uint32_t unit) {
if (dirty) {
- bind(store);
+ bind(store, config, unit);
}
}
-void LineAtlas::bind(gl::ObjectStore& store) {
+void LineAtlas::bind(gl::ObjectStore& store, gl::Config& config, uint32_t unit) {
bool first = false;
if (!texture) {
texture = store.createTexture();
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
first = true;
- } else {
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
+ } else if (config.texture[unit] != *texture) {
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
}
if (dirty) {
+ config.activeTexture = unit;
if (first) {
MBGL_CHECK_ERROR(glTexImage2D(
GL_TEXTURE_2D, // GLenum target
@@ -171,4 +172,6 @@ void LineAtlas::bind(gl::ObjectStore& store) {
dirty = false;
}
-};
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/geometry/line_atlas.hpp b/src/mbgl/geometry/line_atlas.hpp
index 0ca5e95a23..1e6c0ac84e 100644
--- a/src/mbgl/geometry/line_atlas.hpp
+++ b/src/mbgl/geometry/line_atlas.hpp
@@ -9,6 +9,10 @@
namespace mbgl {
+namespace gl {
+class Config;
+} // namespace gl
+
typedef struct {
float width;
float height;
@@ -21,14 +25,14 @@ public:
~LineAtlas();
// Binds the atlas texture to the GPU, and uploads data if it is out of date.
- void bind(gl::ObjectStore&);
+ void bind(gl::ObjectStore&, gl::Config&, uint32_t unit);
// Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
// the texture is only bound when the data is out of date (=dirty).
- void upload(gl::ObjectStore&);
+ void upload(gl::ObjectStore&, gl::Config&, uint32_t unit);
- LinePatternPos getDashPosition(const std::vector<float>&, bool, gl::ObjectStore&);
- LinePatternPos addDash(const std::vector<float> &dasharray, bool round, gl::ObjectStore&);
+ LinePatternPos getDashPosition(const std::vector<float>&, bool);
+ LinePatternPos addDash(const std::vector<float>& dasharray, bool round);
const GLsizei width;
const GLsizei height;
diff --git a/src/mbgl/geometry/line_buffer.cpp b/src/mbgl/geometry/line_buffer.cpp
index 3d979a2a45..7d2e2eb9a2 100644
--- a/src/mbgl/geometry/line_buffer.cpp
+++ b/src/mbgl/geometry/line_buffer.cpp
@@ -3,7 +3,7 @@
#include <cmath>
-using namespace mbgl;
+namespace mbgl {
GLsizei LineVertexBuffer::add(vertex_type x, vertex_type y, float ex, float ey, bool tx, bool ty, int8_t dir, int32_t linesofar) {
GLsizei idx = index();
@@ -34,3 +34,5 @@ GLsizei LineVertexBuffer::add(vertex_type x, vertex_type y, float ex, float ey,
return idx;
}
+
+} // namespace mbgl
diff --git a/src/mbgl/geometry/static_vertex_buffer.cpp b/src/mbgl/geometry/static_vertex_buffer.cpp
index 254b01b6b6..c66b194748 100644
--- a/src/mbgl/geometry/static_vertex_buffer.cpp
+++ b/src/mbgl/geometry/static_vertex_buffer.cpp
@@ -3,11 +3,21 @@
namespace mbgl {
-StaticVertexBuffer::StaticVertexBuffer(std::initializer_list<std::pair<int16_t, int16_t>> init) {
+StaticVertexBuffer::StaticVertexBuffer(std::initializer_list<std::array<VertexType, 2>> init) {
for (const auto& vertex : init) {
- vertex_type *vertices = static_cast<vertex_type *>(addElement());
- vertices[0] = vertex.first;
- vertices[1] = vertex.second;
+ VertexType* vertices = static_cast<VertexType*>(addElement());
+ vertices[0] = std::get<0>(vertex);
+ vertices[1] = std::get<1>(vertex);
+ }
+}
+
+StaticRasterVertexBuffer::StaticRasterVertexBuffer(std::initializer_list<std::array<VertexType, 4>> init) {
+ for (const auto& vertex : init) {
+ VertexType* vertices = static_cast<VertexType*>(addElement());
+ vertices[0] = std::get<0>(vertex);
+ vertices[1] = std::get<1>(vertex);
+ vertices[2] = std::get<2>(vertex);
+ vertices[3] = std::get<3>(vertex);
}
}
diff --git a/src/mbgl/geometry/static_vertex_buffer.hpp b/src/mbgl/geometry/static_vertex_buffer.hpp
index 79f6117cbc..2e738afc98 100644
--- a/src/mbgl/geometry/static_vertex_buffer.hpp
+++ b/src/mbgl/geometry/static_vertex_buffer.hpp
@@ -2,10 +2,8 @@
#include <mbgl/geometry/buffer.hpp>
-#include <vector>
-#include <cstddef>
+#include <array>
#include <cstdint>
-#include <cmath>
namespace mbgl {
@@ -15,9 +13,18 @@ class StaticVertexBuffer : public Buffer<
32 // default length
> {
public:
- typedef int16_t vertex_type;
+ using VertexType = int16_t;
+ StaticVertexBuffer(std::initializer_list<std::array<VertexType, 2>>);
+};
- StaticVertexBuffer(std::initializer_list<std::pair<int16_t, int16_t>> init);
+class StaticRasterVertexBuffer : public Buffer<
+ 8, // bytes per vertex (4 * signed short == 8 bytes)
+ GL_ARRAY_BUFFER,
+ 32 // default length
+> {
+public:
+ using VertexType = int16_t;
+ StaticRasterVertexBuffer(std::initializer_list<std::array<VertexType, 4>>);
};
} // namespace mbgl
diff --git a/src/mbgl/geometry/text_buffer.cpp b/src/mbgl/geometry/text_buffer.cpp
index 1aa65146a4..382e87324d 100644
--- a/src/mbgl/geometry/text_buffer.cpp
+++ b/src/mbgl/geometry/text_buffer.cpp
@@ -6,7 +6,7 @@
namespace mbgl {
-size_t TextVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, uint16_t tx, uint16_t ty, float minzoom, float maxzoom, float labelminzoom) {
+size_t TextVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, uint16_t tx, uint16_t ty, float minzoom, float maxzoom, float labelminzoom, uint8_t labelangle) {
const size_t idx = index();
void *data = addElement();
@@ -21,6 +21,7 @@ size_t TextVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, uint16_t
ubytes[8] /* tex */ = tx / 4;
ubytes[9] /* tex */ = ty / 4;
ubytes[10] /* labelminzoom */ = labelminzoom * 10;
+ ubytes[11] /* labelangle */ = labelangle;
// a_data2
ubytes[12] /* minzoom */ = minzoom * 10; // 1/10 zoom levels: z16 == 160.
diff --git a/src/mbgl/geometry/text_buffer.hpp b/src/mbgl/geometry/text_buffer.hpp
index 8d7859fde2..c6b632c67e 100644
--- a/src/mbgl/geometry/text_buffer.hpp
+++ b/src/mbgl/geometry/text_buffer.hpp
@@ -13,7 +13,7 @@ class TextVertexBuffer : public Buffer <
public:
typedef int16_t vertex_type;
- size_t add(int16_t x, int16_t y, float ox, float oy, uint16_t tx, uint16_t ty, float minzoom, float maxzoom, float labelminzoom);
+ size_t add(int16_t x, int16_t y, float ox, float oy, uint16_t tx, uint16_t ty, float minzoom, float maxzoom, float labelminzoom, uint8_t labelangle);
};
diff --git a/src/mbgl/geometry/vao.cpp b/src/mbgl/geometry/vao.cpp
index 46f10cb48c..30f5484896 100644
--- a/src/mbgl/geometry/vao.cpp
+++ b/src/mbgl/geometry/vao.cpp
@@ -13,8 +13,7 @@ void VertexArrayObject::Unbind() {
VertexArrayObject::VertexArrayObject() {
}
-VertexArrayObject::~VertexArrayObject() {
-}
+VertexArrayObject::~VertexArrayObject() = default;
void VertexArrayObject::bindVertexArrayObject(gl::ObjectStore& store) {
if (!gl::GenVertexArrays || !gl::BindVertexArray) {
@@ -32,7 +31,7 @@ void VertexArrayObject::bindVertexArrayObject(gl::ObjectStore& store) {
MBGL_CHECK_ERROR(gl::BindVertexArray(*vao));
}
-void VertexArrayObject::verifyBinding(Shader &shader, GLuint vertexBuffer, GLuint elementsBuffer,
+void VertexArrayObject::verifyBinding(Shader& shader, GLuint vertexBuffer, GLuint elementsBuffer,
GLbyte *offset) {
if (bound_shader != shader.getID()) {
throw std::runtime_error(std::string("trying to rebind VAO to another shader from " +
diff --git a/src/mbgl/geometry/vao.hpp b/src/mbgl/geometry/vao.hpp
index 150a6badbd..aa0a72ec59 100644
--- a/src/mbgl/geometry/vao.hpp
+++ b/src/mbgl/geometry/vao.hpp
@@ -10,8 +10,6 @@
namespace mbgl {
-class Shader;
-
class VertexArrayObject : public util::noncopyable {
public:
static void Unbind();
@@ -19,8 +17,8 @@ public:
VertexArrayObject();
~VertexArrayObject();
- template <typename Shader, typename VertexBuffer>
- inline void bind(Shader& shader, VertexBuffer &vertexBuffer, GLbyte *offset, gl::ObjectStore& store) {
+ template <typename VertexBuffer>
+ void bind(Shader& shader, VertexBuffer& vertexBuffer, GLbyte* offset, gl::ObjectStore& store) {
bindVertexArrayObject(store);
if (bound_shader == 0) {
vertexBuffer.bind(store);
@@ -33,8 +31,8 @@ public:
}
}
- template <typename Shader, typename VertexBuffer, typename ElementsBuffer>
- inline void bind(Shader& shader, VertexBuffer &vertexBuffer, ElementsBuffer &elementsBuffer, GLbyte *offset, gl::ObjectStore& store) {
+ template <typename VertexBuffer, typename ElementsBuffer>
+ void bind(Shader& shader, VertexBuffer& vertexBuffer, ElementsBuffer& elementsBuffer, GLbyte* offset, gl::ObjectStore& store) {
bindVertexArrayObject(store);
if (bound_shader == 0) {
vertexBuffer.bind(store);
@@ -62,10 +60,10 @@ private:
// For debug reasons, we're storing the bind information so that we can
// detect errors and report
GLuint bound_shader = 0;
- const char *bound_shader_name = "";
+ const char* bound_shader_name = "";
GLuint bound_vertex_buffer = 0;
GLuint bound_elements_buffer = 0;
- GLbyte *bound_offset = 0;
+ GLbyte *bound_offset = nullptr;
};
} // namespace mbgl
diff --git a/src/mbgl/gl/gl.cpp b/src/mbgl/gl/gl.cpp
index 48f00ec177..7747717c63 100644
--- a/src/mbgl/gl/gl.cpp
+++ b/src/mbgl/gl/gl.cpp
@@ -121,7 +121,7 @@ namespace mbgl {
void mbx_trapExtension(const char *, GLenum, GLuint, GLsizei, const GLchar *) { }
void mbx_trapExtension(const char *, GLDEBUGPROC, const void *) { }
void mbx_trapExtension(const char *, GLuint, GLuint, GLuint, GLuint, GLint, const char *, const void*) { }
-
+
void mbx_trapExtension(const char *name, GLuint array) {
if(strncasecmp(name, "glBindVertexArray", 17) == 0) {
currentVertexArray = array;
@@ -188,7 +188,7 @@ void mbx_glBufferData(GLenum target,
}
std::cout << "GL glBufferData: " << currentBinding << " using " << bufferBindingToSizeMap[currentBinding] << " bytes current total " << currentUsedBufferBytes << " high water mark " << largestAmountUsedSoFar << "\n";
lock.unlock();
-
+
glBufferData(target, size, data, usage);
}
diff --git a/src/mbgl/gl/gl_config.cpp b/src/mbgl/gl/gl_config.cpp
index c3c6601e43..9031c3d34f 100644
--- a/src/mbgl/gl/gl_config.cpp
+++ b/src/mbgl/gl/gl_config.cpp
@@ -13,13 +13,15 @@ const DepthTest::Type DepthTest::Default = GL_FALSE;
const DepthFunc::Type DepthFunc::Default = GL_LEQUAL;
const Blend::Type Blend::Default = GL_TRUE;
const BlendFunc::Type BlendFunc::Default = { GL_ONE, GL_ONE_MINUS_SRC_ALPHA };
+const BlendColor::Type BlendColor::Default = { 0, 0, 0, 0 };
const ColorMask::Type ColorMask::Default = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE };
const ClearDepth::Type ClearDepth::Default = 1;
const ClearColor::Type ClearColor::Default = { 0, 0, 0, 0 };
const ClearStencil::Type ClearStencil::Default = 0;
const Program::Type Program::Default = 0;
const LineWidth::Type LineWidth::Default = 1;
-const ActiveTexture::Type ActiveTexture::Default = GL_TEXTURE0;
+const ActiveTexture::Type ActiveTexture::Default = 0;
+const BindTexture::Type BindTexture::Default = 0;
#ifndef GL_ES_VERSION_2_0
const PixelZoom::Type PixelZoom::Default = { 1, 1 };
diff --git a/src/mbgl/gl/gl_config.hpp b/src/mbgl/gl/gl_config.hpp
index 66f4406530..41bee8fab7 100644
--- a/src/mbgl/gl/gl_config.hpp
+++ b/src/mbgl/gl/gl_config.hpp
@@ -13,28 +13,30 @@ template <typename T>
class Value {
public:
void operator=(const typename T::Type& value) {
- if (dirty || current != value) {
+ if (*this != value) {
dirty = false;
current = value;
T::Set(current);
}
}
+ bool operator!=(const typename T::Type& value) const {
+ return dirty || current != value;
+ }
+
void reset() {
- dirty = true;
- current = T::Default;
- T::Set(current);
+ *this = T::Default;
}
void setDirty() {
dirty = true;
}
- typename T::Type getCurrent() {
+ typename T::Type getCurrent() const {
return current;
}
- bool getDirty() {
+ bool getDirty() const {
return dirty;
}
@@ -56,6 +58,7 @@ public:
depthFunc.reset();
blend.reset();
blendFunc.reset();
+ blendColor.reset();
colorMask.reset();
clearDepth.reset();
clearColor.reset();
@@ -80,6 +83,7 @@ public:
depthFunc.setDirty();
blend.setDirty();
blendFunc.setDirty();
+ blendColor.setDirty();
colorMask.setDirty();
clearDepth.setDirty();
clearColor.setDirty();
@@ -103,6 +107,7 @@ public:
Value<DepthFunc> depthFunc;
Value<Blend> blend;
Value<BlendFunc> blendFunc;
+ Value<BlendColor> blendColor;
Value<ColorMask> colorMask;
Value<ClearDepth> clearDepth;
Value<ClearColor> clearColor;
@@ -114,6 +119,7 @@ public:
Value<PixelZoom> pixelZoom;
Value<RasterPos> rasterPos;
#endif // GL_ES_VERSION_2_0
+ std::array<Value<BindTexture>, 2> texture;
};
} // namespace gl
diff --git a/src/mbgl/gl/object_store.cpp b/src/mbgl/gl/object_store.cpp
index 3072296a1e..4139854f61 100644
--- a/src/mbgl/gl/object_store.cpp
+++ b/src/mbgl/gl/object_store.cpp
@@ -22,7 +22,11 @@ void BufferDeleter::operator()(GLuint id) const {
void TextureDeleter::operator()(GLuint id) const {
assert(store);
- store->abandonedTextures.push_back(id);
+ if (store->pooledTextures.size() >= TextureMax) {
+ store->abandonedTextures.push_back(id);
+ } else {
+ store->pooledTextures.push_back(id);
+ }
}
void VAODeleter::operator()(GLuint id) const {
@@ -30,17 +34,8 @@ void VAODeleter::operator()(GLuint id) const {
store->abandonedVAOs.push_back(id);
}
-void TexturePoolDeleter::operator()(ObjectPool ids) const {
- assert(store);
- for (GLuint& id : ids) {
- if (id) {
- store->abandonedTextures.push_back(id);
- id = 0;
- };
- }
-}
-
ObjectStore::~ObjectStore() {
+ assert(pooledTextures.empty());
assert(abandonedPrograms.empty());
assert(abandonedShaders.empty());
assert(abandonedBuffers.empty());
@@ -48,6 +43,12 @@ ObjectStore::~ObjectStore() {
assert(abandonedVAOs.empty());
}
+void ObjectStore::reset() {
+ std::copy(pooledTextures.begin(), pooledTextures.end(), std::back_inserter(abandonedTextures));
+ pooledTextures.resize(0);
+ performCleanup();
+}
+
void ObjectStore::performCleanup() {
for (GLuint id : abandonedPrograms) {
MBGL_CHECK_ERROR(glDeleteProgram(id));
diff --git a/src/mbgl/gl/object_store.hpp b/src/mbgl/gl/object_store.hpp
index 3ae1b6e2fe..96bb8008f1 100644
--- a/src/mbgl/gl/object_store.hpp
+++ b/src/mbgl/gl/object_store.hpp
@@ -5,9 +5,7 @@
#include <unique_resource.hpp>
-#include <array>
#include <algorithm>
-#include <cassert>
#include <memory>
#include <vector>
@@ -43,19 +41,11 @@ struct VAODeleter {
void operator()(GLuint) const;
};
-using ObjectPool = std::array<GLuint, TextureMax>;
-
-struct TexturePoolDeleter {
- ObjectStore* store;
- void operator()(ObjectPool ids) const;
-};
-
using UniqueProgram = std_experimental::unique_resource<GLuint, ProgramDeleter>;
using UniqueShader = std_experimental::unique_resource<GLuint, ShaderDeleter>;
using UniqueBuffer = std_experimental::unique_resource<GLuint, BufferDeleter>;
using UniqueTexture = std_experimental::unique_resource<GLuint, TextureDeleter>;
using UniqueVAO = std_experimental::unique_resource<GLuint, VAODeleter>;
-using UniqueTexturePool = std_experimental::unique_resource<ObjectPool, TexturePoolDeleter>;
class ObjectStore : private util::noncopyable {
public:
@@ -76,8 +66,13 @@ public:
}
UniqueTexture createTexture() {
- GLuint id = 0;
- MBGL_CHECK_ERROR(glGenTextures(1, &id));
+ if (pooledTextures.empty()) {
+ pooledTextures.resize(TextureMax);
+ MBGL_CHECK_ERROR(glGenTextures(TextureMax, pooledTextures.data()));
+ }
+
+ GLuint id = pooledTextures.back();
+ pooledTextures.pop_back();
return UniqueTexture { std::move(id), { this } };
}
@@ -87,19 +82,17 @@ public:
return UniqueVAO { std::move(id), { this } };
}
- UniqueTexturePool createTexturePool() {
- ObjectPool ids;
- MBGL_CHECK_ERROR(glGenTextures(TextureMax, ids.data()));
- assert(ids.size() == size_t(TextureMax));
- return UniqueTexturePool { std::move(ids), { this } };
- }
-
// Actually remove the objects we marked as abandoned with the above methods.
// Only call this while the OpenGL context is exclusive to this thread.
void performCleanup();
+ // Drain pools and remove abandoned objects, in preparation for destroying the store.
+ // Only call this while the OpenGL context is exclusive to this thread.
+ void reset();
+
bool empty() const {
- return abandonedPrograms.empty()
+ return pooledTextures.empty()
+ && abandonedPrograms.empty()
&& abandonedShaders.empty()
&& abandonedBuffers.empty()
&& abandonedTextures.empty()
@@ -112,7 +105,8 @@ private:
friend BufferDeleter;
friend TextureDeleter;
friend VAODeleter;
- friend TexturePoolDeleter;
+
+ std::vector<GLuint> pooledTextures;
std::vector<GLuint> abandonedPrograms;
std::vector<GLuint> abandonedShaders;
diff --git a/src/mbgl/gl/texture_pool.cpp b/src/mbgl/gl/texture_pool.cpp
deleted file mode 100644
index c4984b0186..0000000000
--- a/src/mbgl/gl/texture_pool.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-#include <mbgl/gl/texture_pool.hpp>
-#include <mbgl/gl/object_store.hpp>
-
-#include <algorithm>
-#include <vector>
-
-namespace mbgl {
-namespace gl {
-
-class TexturePool::Impl : private util::noncopyable {
-public:
- class Pool : private util::noncopyable {
- public:
- Pool(gl::ObjectStore& store) : pool(store.createTexturePool()), availableIDs(gl::TextureMax) {
- std::copy(pool.get().begin(), pool.get().end(), availableIDs.begin());
- }
-
- Pool(Pool&& o) : pool(std::move(o.pool)), availableIDs(std::move(o.availableIDs)) {}
- Pool& operator=(Pool&& o) { pool = std::move(o.pool); availableIDs = std::move(o.availableIDs); return *this; }
-
- gl::UniqueTexturePool pool;
- std::vector<GLuint> availableIDs;
- };
-
- GLuint acquireTexture(gl::ObjectStore& store) {
- auto nextAvailableID = [](auto& pool_) {
- auto it = pool_.availableIDs.begin();
- GLuint id = *it;
- pool_.availableIDs.erase(it);
- return id;
- };
-
- for (auto& pool : pools) {
- if (pool.availableIDs.empty()) continue;
- return nextAvailableID(pool);
- }
-
- // All texture IDs are in use.
- pools.emplace_back(Pool { store });
- return nextAvailableID(pools.back());
- }
-
- void releaseTexture(GLuint id) {
- for (auto it = pools.begin(); it != pools.end(); ++it) {
- if (std::find(it->pool.get().begin(), it->pool.get().end(), id) != it->pool.get().end()) {
- it->availableIDs.push_back(id);
- if (GLsizei(it->availableIDs.size()) == gl::TextureMax) {
- pools.erase(it);
- }
- return;
- }
- }
- }
-
-private:
- std::vector<Pool> pools;
-};
-
-void TextureReleaser::operator()(GLuint id) const {
- assert(pool);
- pool->impl->releaseTexture(id);
-}
-
-TexturePool::TexturePool() : impl(std::make_unique<Impl>()) {
-}
-
-TexturePool::~TexturePool() {
-}
-
-PooledTexture TexturePool::acquireTexture(gl::ObjectStore& store) {
- return PooledTexture { impl->acquireTexture(store) , { this } };
-}
-
-} // namespace gl
-} // namespace mbgl
diff --git a/src/mbgl/gl/texture_pool.hpp b/src/mbgl/gl/texture_pool.hpp
deleted file mode 100644
index 1cdcdf220c..0000000000
--- a/src/mbgl/gl/texture_pool.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/gl/gl.hpp>
-#include <mbgl/gl/object_store.hpp>
-
-#include <unique_resource.hpp>
-
-#include <memory>
-
-namespace mbgl {
-namespace gl {
-
-class TexturePool;
-
-struct TextureReleaser {
- TexturePool* pool;
- void operator()(GLuint) const;
-};
-
-using PooledTexture = std_experimental::unique_resource<GLuint, TextureReleaser>;
-
-class TexturePool : private util::noncopyable {
-public:
- TexturePool();
- ~TexturePool();
-
- PooledTexture acquireTexture(gl::ObjectStore&);
-
-private:
- friend TextureReleaser;
-
- class Impl;
- const std::unique_ptr<Impl> impl;
-};
-
-} // namespace gl
-} // namespace mbgl
diff --git a/src/mbgl/map/camera.cpp b/src/mbgl/map/camera.cpp
deleted file mode 100644
index 4a45e904f8..0000000000
--- a/src/mbgl/map/camera.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include <mbgl/map/camera.hpp>
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 24c39fe243..659832dc77 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -5,6 +5,7 @@
#include <mbgl/map/transform_state.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/style/style.hpp>
+#include <mbgl/style/source.hpp>
#include <mbgl/style/layer.hpp>
#include <mbgl/style/observer.hpp>
#include <mbgl/style/transition_options.hpp>
@@ -15,7 +16,6 @@
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/gl/object_store.hpp>
-#include <mbgl/gl/texture_pool.hpp>
#include <mbgl/util/projection.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/exception.hpp>
@@ -37,7 +37,7 @@ class Map::Impl : public style::Observer {
public:
Impl(View&, FileSource&, MapMode, GLContextMode, ConstrainMode, ViewportMode);
- void onResourceLoaded() override;
+ void onNeedsRepaint() override;
void onResourceError(std::exception_ptr) override;
void update();
@@ -62,7 +62,6 @@ public:
util::AsyncTask asyncUpdate;
std::unique_ptr<AnnotationManager> annotationManager;
- std::unique_ptr<gl::TexturePool> texturePool;
std::unique_ptr<Painter> painter;
std::unique_ptr<Style> style;
@@ -98,8 +97,7 @@ Map::Impl::Impl(View& view_,
contextMode(contextMode_),
pixelRatio(view.getPixelRatio()),
asyncUpdate([this] { update(); }),
- annotationManager(std::make_unique<AnnotationManager>(pixelRatio)),
- texturePool(std::make_unique<gl::TexturePool>()) {
+ annotationManager(std::make_unique<AnnotationManager>(pixelRatio)) {
}
Map::~Map() {
@@ -108,13 +106,11 @@ Map::~Map() {
impl->styleRequest = nullptr;
// Explicit resets currently necessary because these abandon resources that need to be
- // cleaned up by store.performCleanup();
+ // cleaned up by store.reset();
impl->style.reset();
impl->painter.reset();
- impl->texturePool.reset();
impl->annotationManager.reset();
-
- impl->store.performCleanup();
+ impl->store.reset();
impl->view.deactivate();
}
@@ -210,11 +206,15 @@ void Map::Impl::update() {
// - Hint style sources to notify when all its tiles are loaded;
timePoint = Clock::now();
- if (style->loaded && updateFlags & Update::Annotations) {
+ if (style->loaded && updateFlags & Update::AnnotationStyle) {
annotationManager->updateStyle(*style);
updateFlags |= Update::Classes;
}
+ if (updateFlags & Update::AnnotationData) {
+ annotationManager->updateData();
+ }
+
if (updateFlags & Update::Classes) {
style->cascade(timePoint, mode);
}
@@ -229,7 +229,6 @@ void Map::Impl::update() {
transform.getState(),
style->workers,
fileSource,
- *texturePool,
style->shouldReparsePartialTiles,
mode,
*annotationManager,
@@ -337,7 +336,7 @@ void Map::Impl::loadStyleJSON(const std::string& json) {
// force style cascade, causing all pending transitions to complete.
style->cascade(Clock::now(), mode);
- updateFlags |= Update::Classes | Update::RecalculateStyle | Update::Annotations;
+ updateFlags |= Update::Classes | Update::RecalculateStyle | Update::AnnotationStyle;
asyncUpdate.send();
}
@@ -378,7 +377,7 @@ bool Map::isPanning() const {
}
#pragma mark -
-
+
CameraOptions Map::getCameraOptions(optional<EdgeInsets> padding) const {
return impl->transform.getCameraOptions(padding);
}
@@ -691,18 +690,17 @@ double Map::getTopOffsetPixelsForAnnotationIcon(const std::string& name) {
AnnotationID Map::addAnnotation(const Annotation& annotation) {
auto result = impl->annotationManager->addAnnotation(annotation, getMaxZoom());
- update(Update::Annotations);
+ update(Update::AnnotationStyle | Update::AnnotationData);
return result;
}
void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) {
- impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom());
- update(Update::Annotations);
+ update(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom()));
}
void Map::removeAnnotation(AnnotationID annotation) {
impl->annotationManager->removeAnnotation(annotation);
- update(Update::Annotations);
+ update(Update::AnnotationStyle | Update::AnnotationData);
}
AnnotationIDs Map::getPointAnnotationsInBounds(const LatLngBounds& bounds) {
@@ -739,6 +737,22 @@ std::vector<Feature> Map::queryRenderedFeatures(const ScreenBox& box, const opti
#pragma mark - Style API
+style::Source* Map::getSource(const std::string& sourceID) {
+ return impl->style ? impl->style->getSource(sourceID) : nullptr;
+}
+
+void Map::addSource(std::unique_ptr<style::Source> source) {
+ impl->style->addSource(std::move(source));
+}
+
+void Map::removeSource(const std::string& sourceID) {
+ impl->style->removeSource(sourceID);
+}
+
+style::Layer* Map::getLayer(const std::string& layerID) {
+ return impl->style ? impl->style->getLayer(layerID) : nullptr;
+}
+
void Map::addLayer(std::unique_ptr<Layer> layer, const optional<std::string>& before) {
impl->view.activate();
@@ -770,14 +784,14 @@ void Map::cycleDebugOptions() {
#ifndef GL_ES_VERSION_2_0
if (impl->debugOptions & MapDebugOptions::StencilClip)
impl->debugOptions = MapDebugOptions::NoDebug;
- else if (impl->debugOptions & MapDebugOptions::Wireframe)
+ else if (impl->debugOptions & MapDebugOptions::Overdraw)
impl->debugOptions = MapDebugOptions::StencilClip;
#else
- if (impl->debugOptions & MapDebugOptions::Wireframe)
+ if (impl->debugOptions & MapDebugOptions::Overdraw)
impl->debugOptions = MapDebugOptions::NoDebug;
#endif // GL_ES_VERSION_2_0
else if (impl->debugOptions & MapDebugOptions::Collision)
- impl->debugOptions = MapDebugOptions::Collision | MapDebugOptions::Wireframe;
+ impl->debugOptions = MapDebugOptions::Overdraw;
else if (impl->debugOptions & MapDebugOptions::Timestamps)
impl->debugOptions = impl->debugOptions | MapDebugOptions::Collision;
else if (impl->debugOptions & MapDebugOptions::ParseStatus)
@@ -839,7 +853,7 @@ void Map::onLowMemory() {
impl->view.invalidate();
}
-void Map::Impl::onResourceLoaded() {
+void Map::Impl::onNeedsRepaint() {
updateFlags |= Update::Repaint;
asyncUpdate.send();
}
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index bf95dd0714..41fc36ce27 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -14,7 +14,7 @@
#include <cstdio>
#include <cmath>
-using namespace mbgl;
+namespace mbgl {
/** Converts the given angle (in radians) to be numerically close to the anchor angle, allowing it to be interpolated properly without sudden jumps. */
static double _normalizeAngle(double angle, double anchorAngle)
@@ -117,24 +117,24 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
ScreenCoordinate center = getScreenCoordinate(padding);
center.y = state.height - center.y;
-
+
// Constrain camera options.
zoom = util::clamp(zoom, state.getMinZoom(), state.getMaxZoom());
const double scale = state.zoomScale(zoom);
pitch = util::clamp(pitch, 0., util::PITCH_MAX);
-
+
Update update = state.getZoom() == zoom ? Update::Repaint : Update::RecalculateStyle;
-
+
// Minimize rotation by taking the shorter path around the circle.
angle = _normalizeAngle(angle, state.angle);
state.angle = _normalizeAngle(state.angle, angle);
Duration duration = animation.duration ? *animation.duration : Duration::zero();
-
+
const double startWorldSize = state.worldSize();
state.Bc = startWorldSize / util::DEGREES_MAX;
state.Cc = startWorldSize / util::M2PI;
-
+
const double startScale = state.scale;
const double startAngle = state.angle;
const double startPitch = state.pitch;
@@ -147,14 +147,14 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
LatLng frameLatLng = state.unproject(framePoint, startWorldSize);
double frameScale = util::interpolate(startScale, scale, t);
state.setLatLngZoom(frameLatLng, state.scaleZoom(frameScale));
-
+
if (angle != startAngle) {
state.angle = util::wrap(util::interpolate(startAngle, angle, t), -M_PI, M_PI);
}
if (pitch != startPitch) {
state.pitch = util::interpolate(startPitch, pitch, t);
}
-
+
if (padding) {
state.moveLatLng(frameLatLng, center);
}
@@ -163,11 +163,11 @@ void Transform::easeTo(const CameraOptions& camera, const AnimationOptions& anim
}
/** This method implements an “optimal path” animation, as detailed in:
-
+
Van Wijk, Jarke J.; Nuij, Wim A. A. “Smooth and efficient zooming and
panning.” INFOVIS ’03. pp. 15–22.
<https://www.win.tue.nl/~vanwijk/zoompan.pdf#page=5>.
-
+
Where applicable, local variable documentation begins with the associated
variable or function in van Wijk (2003). */
void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &animation) {
@@ -179,7 +179,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
if (!latLng || std::isnan(zoom)) {
return;
}
-
+
// Determine endpoints.
EdgeInsets padding;
if (camera.padding) padding = *camera.padding;
@@ -191,19 +191,19 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
ScreenCoordinate center = getScreenCoordinate(padding);
center.y = state.height - center.y;
-
+
// Constrain camera options.
zoom = util::clamp(zoom, state.getMinZoom(), state.getMaxZoom());
pitch = util::clamp(pitch, 0., util::PITCH_MAX);
-
+
// Minimize rotation by taking the shorter path around the circle.
angle = _normalizeAngle(angle, state.angle);
state.angle = _normalizeAngle(state.angle, angle);
-
+
const double startZoom = state.scaleZoom(state.scale);
const double startAngle = state.angle;
const double startPitch = state.pitch;
-
+
/// w₀: Initial visible span, measured in pixels at the initial scale.
/// Known henceforth as a <i>screenful</i>.
double w0 = padding ? std::max(state.width, state.height)
@@ -215,11 +215,11 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
/// Length of the flight path as projected onto the ground plane, measured
/// in pixels from the world image origin at the initial scale.
double u1 = ::hypot((endPoint - startPoint).x, (endPoint - startPoint).y);
-
+
/** ρ: The relative amount of zooming that takes place along the flight
path. A high value maximizes zooming for an exaggerated animation, while
a low value minimizes zooming for something closer to easeTo().
-
+
1.42 is the average value selected by participants in the user study in
van Wijk (2003). A value of 6<sup>¼</sup> would be equivalent to the
root mean squared average velocity, V<sub>RMS</sub>. A value of 1 would
@@ -235,16 +235,16 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
}
/// ρ²
double rho2 = rho * rho;
-
+
/** rᵢ: Returns the zoom-out factor at one end of the animation.
-
+
@param i 0 for the ascent or 1 for the descent. */
auto r = [=](double i) {
/// bᵢ
double b = (w1 * w1 - w0 * w0 + (i ? -1 : 1) * rho2 * rho2 * u1 * u1) / (2 * (i ? w1 : w0) * rho2 * u1);
return std::log(std::sqrt(b * b + 1) - b);
};
-
+
// When u₀ = u₁, the optimal path doesn’t require both ascent and descent.
bool isClose = std::abs(u1) < 0.000001;
// Perform a more or less instantaneous transition if the path is too short.
@@ -252,12 +252,12 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
easeTo(camera, animation);
return;
}
-
+
/// r₀: Zoom-out factor during ascent.
double r0 = r(0);
/** w(s): Returns the visible span on the ground, measured in pixels with
respect to the initial scale.
-
+
Assumes an angular field of view of 2 arctan ½ ≈ 53°. */
auto w = [=](double s) {
return (isClose ? std::exp((w1 < w0 ? -1 : 1) * rho * s)
@@ -273,7 +273,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
/// S: Total length of the flight path, measured in ρ-screenfuls.
double S = (isClose ? (std::abs(std::log(w1 / w0)) / rho)
: ((r(1) - r0) / rho));
-
+
Duration duration;
if (animation.duration) {
duration = *animation.duration;
@@ -290,36 +290,36 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
jumpTo(camera);
return;
}
-
+
const double startWorldSize = state.worldSize();
state.Bc = startWorldSize / util::DEGREES_MAX;
state.Cc = startWorldSize / util::M2PI;
-
+
state.panning = true;
state.scaling = true;
state.rotating = angle != startAngle;
-
+
startTransition(camera, animation, [=](double k) {
/// s: The distance traveled along the flight path, measured in
/// ρ-screenfuls.
double s = k * S;
double us = u(s);
-
+
// Calculate the current point and zoom level along the flight path.
Point<double> framePoint = util::interpolate(startPoint, endPoint, us);
double frameZoom = startZoom + state.scaleZoom(1 / w(s));
-
+
// Convert to geographic coordinates and set the new viewpoint.
LatLng frameLatLng = state.unproject(framePoint, startWorldSize);
state.setLatLngZoom(frameLatLng, frameZoom);
-
+
if (angle != startAngle) {
state.angle = util::wrap(util::interpolate(startAngle, angle, k), -M_PI, M_PI);
}
if (pitch != startPitch) {
state.pitch = util::interpolate(startPitch, pitch, k);
}
-
+
if (padding) {
state.moveLatLng(frameLatLng, center);
}
@@ -474,7 +474,7 @@ void Transform::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate&
center.x = first.x + std::cos(rotateAngle) * heightOffset;
center.y = first.y + std::sin(rotateAngle) * heightOffset;
}
-
+
CameraOptions camera;
camera.angle = state.angle + util::angle_between(first - center, second - center);
easeTo(camera, duration);
@@ -561,7 +561,7 @@ void Transform::startTransition(const CameraOptions& camera,
if (transitionFinishFn) {
transitionFinishFn();
}
-
+
bool isAnimated = duration != Duration::zero();
if (callback) {
callback(isAnimated ? MapChangeRegionWillChangeAnimated : MapChangeRegionWillChange);
@@ -584,12 +584,12 @@ void Transform::startTransition(const CameraOptions& camera,
if (t >= 1.0) {
result = frame(1.0);
} else {
- util::UnitBezier ease = animation.easing ? *animation.easing : util::UnitBezier(0, 0, 0.25, 1);
+ util::UnitBezier ease = animation.easing ? *animation.easing : util::DEFAULT_TRANSITION_EASE;
result = frame(ease.solve(t, 0.001));
}
-
+
if (anchor) state.moveLatLng(anchorLatLng, *anchor);
-
+
// At t = 1.0, a DidChangeAnimated notification should be sent from finish().
if (t < 1.0) {
if (animation.transitionFrameFn) {
@@ -620,7 +620,7 @@ void Transform::startTransition(const CameraOptions& camera,
callback(isAnimated ? MapChangeRegionDidChangeAnimated : MapChangeRegionDidChange);
}
};
-
+
if (!isAnimated) {
transitionFrameFn(Clock::now());
}
@@ -665,3 +665,5 @@ LatLng Transform::screenCoordinateToLatLng(const ScreenCoordinate& point) const
flippedPoint.y = state.height - flippedPoint.y;
return state.screenCoordinateToLatLng(flippedPoint).wrapped();
}
+
+} // namespace mbgl
diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp
index 904ed11392..abc301b1cb 100644
--- a/src/mbgl/map/transform.hpp
+++ b/src/mbgl/map/transform.hpp
@@ -28,7 +28,7 @@ public:
// Camera
/** Returns the current camera options. */
CameraOptions getCameraOptions(optional<EdgeInsets>) const;
-
+
/** Instantaneously, synchronously applies the given camera options. */
void jumpTo(const CameraOptions&);
/** Asynchronously transitions all specified camera options linearly along
@@ -40,7 +40,7 @@ public:
void flyTo(const CameraOptions&, const AnimationOptions& = {});
// Position
-
+
/** Pans the map by the given amount.
@param offset The distance to pan the map by, measured in pixels from
top to bottom and from left to right. */
@@ -157,7 +157,7 @@ public:
bool isRotating() const { return state.isRotating(); }
bool isScaling() const { return state.isScaling(); }
bool isPanning() const { return state.isPanning(); }
-
+
// Conversion and projection
ScreenCoordinate latLngToScreenCoordinate(const LatLng&) const;
LatLng screenCoordinateToLatLng(const ScreenCoordinate&) const;
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index fab5991de8..eb66513626 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -21,8 +21,8 @@ void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) cons
matrix::identity(matrix);
matrix::translate(matrix, matrix,
- static_cast<int64_t>(tileID.canonical.x + tileID.wrap * tileScale) * s,
- static_cast<int64_t>(tileID.canonical.y) * s, 0);
+ int64_t(tileID.canonical.x + tileID.wrap * tileScale) * s,
+ int64_t(tileID.canonical.y) * s, 0);
matrix::scale(matrix, matrix, s / util::EXTENT, s / util::EXTENT, 1);
}
@@ -336,10 +336,10 @@ void TransformState::setLatLngZoom(const LatLng &latLng, double zoom) {
const double newWorldSize = newScale * util::tileSize;
Bc = newWorldSize / util::DEGREES_MAX;
Cc = newWorldSize / util::M2PI;
-
+
const double m = 1 - 1e-15;
const double f = util::clamp(std::sin(util::DEG2RAD * latLng.latitude), -m, m);
-
+
ScreenCoordinate point = {
-latLng.longitude * Bc,
0.5 * Cc * std::log((1 + f) / (1 - f)),
@@ -351,7 +351,7 @@ void TransformState::setScalePoint(const double newScale, const ScreenCoordinate
double constrainedScale = newScale;
ScreenCoordinate constrainedPoint = point;
constrain(constrainedScale, constrainedPoint.x, constrainedPoint.y);
-
+
scale = constrainedScale;
x = constrainedPoint.x;
y = constrainedPoint.y;
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index 0489b69dd4..1e4b2054f7 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -72,6 +72,9 @@ public:
Point<double> project(const LatLng&) const;
LatLng unproject(const Point<double>&, double worldSize, LatLng::WrapMode = LatLng::Unwrapped) const;
+ double zoomScale(double zoom) const;
+ double scaleZoom(double scale) const;
+
private:
bool rotatedNorth() const;
void constrain(double& scale, double& x, double& y) const;
@@ -85,13 +88,11 @@ private:
// logical dimensions
uint16_t width = 0, height = 0;
- double zoomScale(double zoom) const;
- double scaleZoom(double scale) const;
double worldSize() const;
mat4 coordinatePointMatrix(double z) const;
mat4 getPixelMatrix() const;
-
+
/** Recenter the map so that the given coordinate is located at the given
point on screen. */
void moveLatLng(const LatLng&, const ScreenCoordinate&);
diff --git a/src/mbgl/platform/event.cpp b/src/mbgl/platform/event.cpp
new file mode 100644
index 0000000000..68d75a2941
--- /dev/null
+++ b/src/mbgl/platform/event.cpp
@@ -0,0 +1,34 @@
+#include <mbgl/platform/event.hpp>
+#include <mbgl/util/enum.hpp>
+
+namespace mbgl {
+
+MBGL_DEFINE_ENUM(EventSeverity, {
+ { EventSeverity::Debug, "DEBUG" },
+ { EventSeverity::Info, "INFO" },
+ { EventSeverity::Warning, "WARNING" },
+ { EventSeverity::Error, "ERROR" },
+ { EventSeverity(-1), "UNKNOWN" },
+});
+
+MBGL_DEFINE_ENUM(Event, {
+ { Event::General, "General" },
+ { Event::Setup, "Setup" },
+ { Event::Shader, "Shader" },
+ { Event::ParseStyle, "ParseStyle" },
+ { Event::ParseTile, "ParseTile" },
+ { Event::Render, "Render" },
+ { Event::Style, "Style" },
+ { Event::Database, "Database" },
+ { Event::HttpRequest, "HttpRequest" },
+ { Event::Sprite, "Sprite" },
+ { Event::Image, "Image" },
+ { Event::OpenGL, "OpenGL" },
+ { Event::JNI, "JNI" },
+ { Event::Android, "Android" },
+ { Event::Crash, "Crash" },
+ { Event::Glyph, "Glyph" },
+ { Event(-1), "Unknown" },
+});
+
+} // namespace mbgl
diff --git a/src/mbgl/platform/log.cpp b/src/mbgl/platform/log.cpp
index 2118511592..b140485dd6 100644
--- a/src/mbgl/platform/log.cpp
+++ b/src/mbgl/platform/log.cpp
@@ -1,4 +1,6 @@
#include <mbgl/platform/log.hpp>
+#include <mbgl/util/enum.hpp>
+#include <mbgl/util/thread.hpp>
#include <cstdio>
#include <cstdarg>
@@ -48,13 +50,8 @@ void Log::record(EventSeverity severity, Event event, int64_t code, const std::s
std::stringstream logStream;
- #if !defined(__ANDROID__) && (defined( __APPLE__) || defined(__linux__))
- char name[32];
- pthread_getname_np(pthread_self(), name, sizeof(name));
- logStream << "{" << name << "}";
- #endif
-
- logStream << "[" << event << "]";
+ logStream << "{" << platform::getCurrentThreadName() << "}";
+ logStream << "[" << Enum<Event>::toString(event) << "]";
if (code >= 0) {
logStream << "(" << code << ")";
diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp
index f12ed1fb96..cf461e4389 100644
--- a/src/mbgl/renderer/bucket.hpp
+++ b/src/mbgl/renderer/bucket.hpp
@@ -2,9 +2,9 @@
#include <mbgl/gl/gl.hpp>
#include <mbgl/renderer/render_pass.hpp>
-#include <mbgl/util/atomic.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/mat4.hpp>
+
+#include <atomic>
#define BUFFER_OFFSET_0 ((GLbyte*)nullptr)
#define BUFFER_OFFSET(i) ((BUFFER_OFFSET_0) + (i))
@@ -12,28 +12,30 @@
namespace mbgl {
class Painter;
-class UnwrappedTileID;
+class PaintParameters;
class CollisionTile;
+class RenderTile;
namespace gl {
class ObjectStore;
-}
+class Config;
+} // namespace gl
namespace style {
class Layer;
-}
+} // namespace style
class Bucket : private util::noncopyable {
public:
- Bucket() : uploaded(false) {}
+ Bucket() = default;
// As long as this bucket has a Prepare render pass, this function is getting called. Typically,
// this only happens once when the bucket is being rendered for the first time.
- virtual void upload(gl::ObjectStore&) = 0;
+ virtual void upload(gl::ObjectStore&, gl::Config&) = 0;
// Every time this bucket is getting rendered, this function is called. This happens either
// once or twice (for Opaque and Transparent render passes).
- virtual void render(Painter&, const style::Layer&, const UnwrappedTileID&, const mat4&) = 0;
+ virtual void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) = 0;
virtual ~Bucket() = default;
@@ -41,7 +43,7 @@ public:
virtual bool needsClipping() const = 0;
- inline bool needsUpload() const {
+ bool needsUpload() const {
return !uploaded;
}
@@ -49,8 +51,7 @@ public:
virtual void swapRenderData() {}
protected:
- util::Atomic<bool> uploaded;
-
+ std::atomic<bool> uploaded { false };
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/circle_bucket.cpp b/src/mbgl/renderer/circle_bucket.cpp
index 4ae63fed46..0b5c45ccc2 100644
--- a/src/mbgl/renderer/circle_bucket.cpp
+++ b/src/mbgl/renderer/circle_bucket.cpp
@@ -16,17 +16,17 @@ CircleBucket::~CircleBucket() {
// Do not remove. header file only contains forward definitions to unique pointers.
}
-void CircleBucket::upload(gl::ObjectStore& store) {
+void CircleBucket::upload(gl::ObjectStore& store, gl::Config&) {
vertexBuffer_.upload(store);
elementsBuffer_.upload(store);
uploaded = true;
}
void CircleBucket::render(Painter& painter,
+ PaintParameters& parameters,
const Layer& layer,
- const UnwrappedTileID& tileID,
- const mat4& matrix) {
- painter.renderCircle(*this, *layer.as<CircleLayer>(), tileID, matrix);
+ const RenderTile& tile) {
+ painter.renderCircle(parameters, *this, *layer.as<CircleLayer>(), tile);
}
bool CircleBucket::hasData() const {
@@ -100,4 +100,4 @@ void CircleBucket::drawCircles(CircleShader& shader, gl::ObjectStore& store) {
}
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/circle_bucket.hpp b/src/mbgl/renderer/circle_bucket.hpp
index fa34aa088a..617a9e5b24 100644
--- a/src/mbgl/renderer/circle_bucket.hpp
+++ b/src/mbgl/renderer/circle_bucket.hpp
@@ -2,7 +2,7 @@
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/map/mode.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/circle_buffer.hpp>
@@ -18,8 +18,8 @@ public:
CircleBucket(const MapMode);
~CircleBucket() override;
- void upload(gl::ObjectStore&) override;
- void render(Painter&, const style::Layer&, const UnwrappedTileID&, const mat4&) override;
+ void upload(gl::ObjectStore&, gl::Config&) override;
+ void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override;
bool hasData() const override;
bool needsClipping() const override;
diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/debug_bucket.cpp
index 8c7a25c675..b4218485b2 100644
--- a/src/mbgl/renderer/debug_bucket.cpp
+++ b/src/mbgl/renderer/debug_bucket.cpp
@@ -8,7 +8,7 @@
#include <cassert>
#include <string>
-using namespace mbgl;
+namespace mbgl {
DebugBucket::DebugBucket(const OverscaledTileID& id,
const bool renderable_,
@@ -51,3 +51,5 @@ void DebugBucket::drawPoints(PlainShader& shader, gl::ObjectStore& store) {
MBGL_CHECK_ERROR(glDrawArrays(GL_POINTS, 0, (GLsizei)(fontBuffer.index())));
}
}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/debug_bucket.hpp
index d69014bb8b..7097ae0735 100644
--- a/src/mbgl/renderer/debug_bucket.hpp
+++ b/src/mbgl/renderer/debug_bucket.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include <mbgl/tile/tile_data.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/geometry/debug_font_buffer.hpp>
#include <mbgl/geometry/vao.hpp>
@@ -8,11 +7,12 @@
namespace mbgl {
+class OverscaledTileID;
class PlainShader;
namespace gl {
class ObjectStore;
-}
+} // namespace gl
class DebugBucket : private util::noncopyable {
public:
diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp
index 02f346decb..4622c2097e 100644
--- a/src/mbgl/renderer/fill_bucket.cpp
+++ b/src/mbgl/renderer/fill_bucket.cpp
@@ -15,14 +15,14 @@
namespace mapbox {
namespace util {
template <> struct nth<0, mbgl::GeometryCoordinate> {
- inline static int64_t get(const mbgl::GeometryCoordinate& t) { return t.x; };
+ static int64_t get(const mbgl::GeometryCoordinate& t) { return t.x; };
};
template <> struct nth<1, mbgl::GeometryCoordinate> {
- inline static int64_t get(const mbgl::GeometryCoordinate& t) { return t.y; };
+ static int64_t get(const mbgl::GeometryCoordinate& t) { return t.y; };
};
-}
-}
+} // namespace util
+} // namespace mapbox
namespace mbgl {
@@ -33,8 +33,7 @@ struct GeometryTooLongException : std::exception {};
FillBucket::FillBucket() {
}
-FillBucket::~FillBucket() {
-}
+FillBucket::~FillBucket() = default;
void FillBucket::addGeometry(const GeometryCollection& geometry) {
for (auto& polygon : classifyRings(geometry)) {
@@ -96,7 +95,7 @@ void FillBucket::addGeometry(const GeometryCollection& geometry) {
}
}
-void FillBucket::upload(gl::ObjectStore& store) {
+void FillBucket::upload(gl::ObjectStore& store, gl::Config&) {
vertexBuffer.upload(store);
triangleElementsBuffer.upload(store);
lineElementsBuffer.upload(store);
@@ -106,10 +105,10 @@ void FillBucket::upload(gl::ObjectStore& store) {
}
void FillBucket::render(Painter& painter,
+ PaintParameters& parameters,
const Layer& layer,
- const UnwrappedTileID& tileID,
- const mat4& matrix) {
- painter.renderFill(*this, *layer.as<FillLayer>(), tileID, matrix);
+ const RenderTile& tile) {
+ painter.renderFill(parameters, *this, *layer.as<FillLayer>(), tile);
}
bool FillBucket::hasData() const {
@@ -120,52 +119,52 @@ bool FillBucket::needsClipping() const {
return true;
}
-void FillBucket::drawElements(PlainShader& shader, gl::ObjectStore& store) {
+void FillBucket::drawElements(PlainShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte* vertex_index = BUFFER_OFFSET(0);
GLbyte* elements_index = BUFFER_OFFSET(0);
for (auto& group : triangleGroups) {
assert(group);
- group->array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
+ group->array[overdraw ? 1 : 0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
}
}
-void FillBucket::drawElements(PatternShader& shader, gl::ObjectStore& store) {
+void FillBucket::drawElements(PatternShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte* vertex_index = BUFFER_OFFSET(0);
GLbyte* elements_index = BUFFER_OFFSET(0);
for (auto& group : triangleGroups) {
assert(group);
- group->array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
+ group->array[overdraw ? 3 : 2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * triangleElementsBuffer.itemSize;
}
}
-void FillBucket::drawVertices(OutlineShader& shader, gl::ObjectStore& store) {
+void FillBucket::drawVertices(OutlineShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte* vertex_index = BUFFER_OFFSET(0);
GLbyte* elements_index = BUFFER_OFFSET(0);
for (auto& group : lineGroups) {
assert(group);
- group->array[0].bind(shader, vertexBuffer, lineElementsBuffer, vertex_index, store);
+ group->array[overdraw ? 1 : 0].bind(shader, vertexBuffer, lineElementsBuffer, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_LINES, group->elements_length * 2, GL_UNSIGNED_SHORT, elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * lineElementsBuffer.itemSize;
}
}
-void FillBucket::drawVertices(OutlinePatternShader& shader, gl::ObjectStore& store) {
+void FillBucket::drawVertices(OutlinePatternShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte* vertex_index = BUFFER_OFFSET(0);
GLbyte* elements_index = BUFFER_OFFSET(0);
for (auto& group : lineGroups) {
assert(group);
- group->array[1].bind(shader, vertexBuffer, lineElementsBuffer, vertex_index, store);
+ group->array[overdraw? 3 : 2].bind(shader, vertexBuffer, lineElementsBuffer, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_LINES, group->elements_length * 2, GL_UNSIGNED_SHORT, elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
elements_index += group->elements_length * lineElementsBuffer.itemSize;
}
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/fill_bucket.hpp
index 35d70d169c..cacda975f0 100644
--- a/src/mbgl/renderer/fill_bucket.hpp
+++ b/src/mbgl/renderer/fill_bucket.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/fill_buffer.hpp>
@@ -20,25 +20,25 @@ public:
FillBucket();
~FillBucket() override;
- void upload(gl::ObjectStore&) override;
- void render(Painter&, const style::Layer&, const UnwrappedTileID&, const mat4&) override;
+ void upload(gl::ObjectStore&, gl::Config&) override;
+ void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override;
bool hasData() const override;
bool needsClipping() const override;
void addGeometry(const GeometryCollection&);
- void drawElements(PlainShader&, gl::ObjectStore&);
- void drawElements(PatternShader&, gl::ObjectStore&);
- void drawVertices(OutlineShader&, gl::ObjectStore&);
- void drawVertices(OutlinePatternShader&, gl::ObjectStore&);
+ void drawElements(PlainShader&, gl::ObjectStore&, bool overdraw);
+ void drawElements(PatternShader&, gl::ObjectStore&, bool overdraw);
+ void drawVertices(OutlineShader&, gl::ObjectStore&, bool overdraw);
+ void drawVertices(OutlinePatternShader&, gl::ObjectStore&, bool overdraw);
private:
FillVertexBuffer vertexBuffer;
TriangleElementsBuffer triangleElementsBuffer;
LineElementsBuffer lineElementsBuffer;
- typedef ElementGroup<2> TriangleGroup;
- typedef ElementGroup<2> LineGroup;
+ typedef ElementGroup<4> TriangleGroup;
+ typedef ElementGroup<4> LineGroup;
std::vector<std::unique_ptr<TriangleGroup>> triangleGroups;
std::vector<std::unique_ptr<LineGroup>> lineGroups;
diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp
index dbaf8fbf7a..fc9d9b6616 100644
--- a/src/mbgl/renderer/frame_history.cpp
+++ b/src/mbgl/renderer/frame_history.cpp
@@ -1,12 +1,13 @@
#include <mbgl/renderer/frame_history.hpp>
#include <mbgl/math/minmax.hpp>
+#include <mbgl/gl/gl_config.hpp>
-using namespace mbgl;
+namespace mbgl {
FrameHistory::FrameHistory() {
changeOpacities.fill(0);
opacities.fill(0);
-};
+}
void FrameHistory::record(const TimePoint& now, float zoom, const Duration& duration) {
@@ -57,11 +58,11 @@ bool FrameHistory::needsAnimation(const Duration& duration) const {
return (time - previousTime) < duration;
}
-void FrameHistory::upload(gl::ObjectStore& store) {
+void FrameHistory::upload(gl::ObjectStore& store, gl::Config& config, uint32_t unit) {
if (changed) {
const bool first = !texture;
- bind(store);
+ bind(store, config, unit);
if (first) {
MBGL_CHECK_ERROR(glTexImage2D(
@@ -94,10 +95,11 @@ void FrameHistory::upload(gl::ObjectStore& store) {
}
}
-void FrameHistory::bind(gl::ObjectStore& store) {
+void FrameHistory::bind(gl::ObjectStore& store, gl::Config& config, uint32_t unit) {
if (!texture) {
texture = store.createTexture();
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
#ifndef GL_ES_VERSION_2_0
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
#endif
@@ -105,8 +107,10 @@ void FrameHistory::bind(gl::ObjectStore& store) {
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
- } else {
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
+ } else if (config.texture[unit] != *texture) {
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
}
-
}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp
index 04ebe23276..ec43e2beb5 100644
--- a/src/mbgl/renderer/frame_history.hpp
+++ b/src/mbgl/renderer/frame_history.hpp
@@ -9,14 +9,18 @@
namespace mbgl {
+namespace gl {
+class Config;
+} // namespace gl
+
class FrameHistory {
public:
FrameHistory();
void record(const TimePoint&, float zoom, const Duration&);
bool needsAnimation(const Duration&) const;
- void bind(gl::ObjectStore&);
- void upload(gl::ObjectStore&);
+ void bind(gl::ObjectStore&, gl::Config&, uint32_t);
+ void upload(gl::ObjectStore&, gl::Config&, uint32_t);
private:
const int width = 256;
diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp
index d207cdb8c5..e9f920efa6 100644
--- a/src/mbgl/renderer/line_bucket.cpp
+++ b/src/mbgl/renderer/line_bucket.cpp
@@ -437,7 +437,7 @@ void LineBucket::addPieSliceVertex(const GeometryCoordinate& currentVertex,
}
}
-void LineBucket::upload(gl::ObjectStore& store) {
+void LineBucket::upload(gl::ObjectStore& store, gl::Config&) {
vertexBuffer.upload(store);
triangleElementsBuffer.upload(store);
@@ -446,10 +446,10 @@ void LineBucket::upload(gl::ObjectStore& store) {
}
void LineBucket::render(Painter& painter,
+ PaintParameters& parameters,
const Layer& layer,
- const UnwrappedTileID& tileID,
- const mat4& matrix) {
- painter.renderLine(*this, *layer.as<LineLayer>(), tileID, matrix);
+ const RenderTile& tile) {
+ painter.renderLine(parameters, *this, *layer.as<LineLayer>(), tile);
}
bool LineBucket::hasData() const {
@@ -460,7 +460,7 @@ bool LineBucket::needsClipping() const {
return true;
}
-void LineBucket::drawLines(LineShader& shader, gl::ObjectStore& store) {
+void LineBucket::drawLines(LineShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte* vertex_index = BUFFER_OFFSET(0);
GLbyte* elements_index = BUFFER_OFFSET(0);
for (auto& group : triangleGroups) {
@@ -468,7 +468,7 @@ void LineBucket::drawLines(LineShader& shader, gl::ObjectStore& store) {
if (!group->elements_length) {
continue;
}
- group->array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
+ group->array[overdraw ? 1 : 0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
@@ -476,7 +476,7 @@ void LineBucket::drawLines(LineShader& shader, gl::ObjectStore& store) {
}
}
-void LineBucket::drawLineSDF(LineSDFShader& shader, gl::ObjectStore& store) {
+void LineBucket::drawLineSDF(LineSDFShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte* vertex_index = BUFFER_OFFSET(0);
GLbyte* elements_index = BUFFER_OFFSET(0);
for (auto& group : triangleGroups) {
@@ -484,7 +484,7 @@ void LineBucket::drawLineSDF(LineSDFShader& shader, gl::ObjectStore& store) {
if (!group->elements_length) {
continue;
}
- group->array[2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
+ group->array[overdraw ? 3 : 2].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
@@ -492,7 +492,7 @@ void LineBucket::drawLineSDF(LineSDFShader& shader, gl::ObjectStore& store) {
}
}
-void LineBucket::drawLinePatterns(LinepatternShader& shader, gl::ObjectStore& store) {
+void LineBucket::drawLinePatterns(LinepatternShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte* vertex_index = BUFFER_OFFSET(0);
GLbyte* elements_index = BUFFER_OFFSET(0);
for (auto& group : triangleGroups) {
@@ -500,7 +500,7 @@ void LineBucket::drawLinePatterns(LinepatternShader& shader, gl::ObjectStore& st
if (!group->elements_length) {
continue;
}
- group->array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
+ group->array[overdraw ? 5 : 4].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT,
elements_index));
vertex_index += group->vertex_length * vertexBuffer.itemSize;
@@ -508,4 +508,4 @@ void LineBucket::drawLinePatterns(LinepatternShader& shader, gl::ObjectStore& st
}
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp
index d746f29c7e..6687faab4c 100644
--- a/src/mbgl/renderer/line_bucket.hpp
+++ b/src/mbgl/renderer/line_bucket.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/line_buffer.hpp>
@@ -18,23 +18,23 @@ class LineSDFShader;
class LinepatternShader;
class LineBucket : public Bucket {
- using TriangleGroup = ElementGroup<3>;
+ using TriangleGroup = ElementGroup<6>;
public:
LineBucket(uint32_t overscaling);
~LineBucket() override;
- void upload(gl::ObjectStore&) override;
- void render(Painter&, const style::Layer&, const UnwrappedTileID&, const mat4&) override;
+ void upload(gl::ObjectStore&, gl::Config&) override;
+ void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override;
bool hasData() const override;
bool needsClipping() const override;
void addGeometry(const GeometryCollection&);
void addGeometry(const GeometryCoordinates& line);
- void drawLines(LineShader&, gl::ObjectStore&);
- void drawLineSDF(LineSDFShader&, gl::ObjectStore&);
- void drawLinePatterns(LinepatternShader&, gl::ObjectStore&);
+ void drawLines(LineShader&, gl::ObjectStore&, bool overdraw);
+ void drawLineSDF(LineSDFShader&, gl::ObjectStore&, bool overdraw);
+ void drawLinePatterns(LinepatternShader&, gl::ObjectStore&, bool overdraw);
private:
struct TriangleElement {
diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp
new file mode 100644
index 0000000000..13bf21080d
--- /dev/null
+++ b/src/mbgl/renderer/paint_parameters.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+namespace mbgl {
+
+class Shaders;
+
+class PaintParameters {
+public:
+ Shaders& shaders;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
index 1864bf7ef1..b196c71b96 100644
--- a/src/mbgl/renderer/painter.cpp
+++ b/src/mbgl/renderer/painter.cpp
@@ -1,7 +1,9 @@
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/style/source.hpp>
-#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/source_impl.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/gl/debugging.hpp>
@@ -17,18 +19,7 @@
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
-#include <mbgl/shader/pattern_shader.hpp>
-#include <mbgl/shader/plain_shader.hpp>
-#include <mbgl/shader/outline_shader.hpp>
-#include <mbgl/shader/outlinepattern_shader.hpp>
-#include <mbgl/shader/line_shader.hpp>
-#include <mbgl/shader/linesdf_shader.hpp>
-#include <mbgl/shader/linepattern_shader.hpp>
-#include <mbgl/shader/icon_shader.hpp>
-#include <mbgl/shader/raster_shader.hpp>
-#include <mbgl/shader/sdf_shader.hpp>
-#include <mbgl/shader/collision_box_shader.hpp>
-#include <mbgl/shader/circle_shader.hpp>
+#include <mbgl/shader/shaders.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
@@ -49,26 +40,18 @@ namespace mbgl {
using namespace style;
-Painter::Painter(const TransformState& state_, gl::ObjectStore& store_)
- : state(state_),
- store(store_) {
+Painter::Painter(const TransformState& state_,
+ gl::ObjectStore& store_)
+ : state(state_), store(store_) {
gl::debugging::enable();
- plainShader = std::make_unique<PlainShader>(store);
- outlineShader = std::make_unique<OutlineShader>(store);
- outlinePatternShader = std::make_unique<OutlinePatternShader>(store);
- lineShader = std::make_unique<LineShader>(store);
- linesdfShader = std::make_unique<LineSDFShader>(store);
- linepatternShader = std::make_unique<LinepatternShader>(store);
- patternShader = std::make_unique<PatternShader>(store);
- iconShader = std::make_unique<IconShader>(store);
- rasterShader = std::make_unique<RasterShader>(store);
- sdfGlyphShader = std::make_unique<SDFGlyphShader>(store);
- sdfIconShader = std::make_unique<SDFIconShader>(store);
- collisionBoxShader = std::make_unique<CollisionBoxShader>(store);
- circleShader = std::make_unique<CircleShader>(store);
+ shaders = std::make_unique<Shaders>(store);
+#if defined(DEBUG)
+ overdrawShaders = std::make_unique<Shaders>(store, Shader::Overdraw);
+#endif
// Reset GL values
+ config.setDirty();
config.reset();
}
@@ -87,11 +70,19 @@ void Painter::setClipping(const ClipID& clip) {
void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& annotationSpriteAtlas) {
frame = frame_;
+ PaintParameters parameters {
+#if defined(DEBUG)
+ isOverdraw() ? *overdrawShaders : *shaders
+#else
+ *shaders
+#endif
+ };
+
glyphAtlas = style.glyphAtlas.get();
spriteAtlas = style.spriteAtlas.get();
lineAtlas = style.lineAtlas.get();
- RenderData renderData = style.getRenderData();
+ RenderData renderData = style.getRenderData(frame.debugOptions);
const std::vector<RenderItem>& order = renderData.order;
const std::set<Source*>& sources = renderData.sources;
const Color& background = renderData.backgroundColor;
@@ -99,15 +90,10 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
// Update the default matrices to the current viewport dimensions.
state.getProjMatrix(projMatrix);
- // The extrusion scale.
- const float flippedY = state.getViewportMode() == ViewportMode::FlippedY;
- extrudeScale = {{ 2.0f / state.getWidth() * state.getAltitude(),
- (flippedY ? 2.0f : -2.0f) / state.getHeight() * state.getAltitude() }};
-
- // The native matrix is a 1:1 matrix that paints the coordinates at the
- // same screen position as the vertex specifies.
- matrix::identity(nativeMatrix);
- matrix::multiply(nativeMatrix, projMatrix, nativeMatrix);
+ pixelsToGLUnits = {{ 2.0f / state.getWidth(), -2.0f / state.getHeight() }};
+ if (state.getViewportMode() == ViewportMode::FlippedY) {
+ pixelsToGLUnits[1] *= -1;
+ }
frameHistory.record(frame.timePoint, state.getZoom(),
frame.mapMode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Milliseconds(0));
@@ -118,16 +104,17 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
MBGL_DEBUG_GROUP("upload");
tileStencilBuffer.upload(store);
+ rasterBoundsBuffer.upload(store);
tileBorderBuffer.upload(store);
- spriteAtlas->upload(store);
- lineAtlas->upload(store);
- glyphAtlas->upload(store);
- frameHistory.upload(store);
- annotationSpriteAtlas.upload(store);
+ spriteAtlas->upload(store, config, 0);
+ lineAtlas->upload(store, config, 0);
+ glyphAtlas->upload(store, config, 0);
+ frameHistory.upload(store, config, 0);
+ annotationSpriteAtlas.upload(store, config, 0);
for (const auto& item : order) {
if (item.bucket && item.bucket->needsUpload()) {
- item.bucket->upload(store);
+ item.bucket->upload(store, config);
}
}
}
@@ -143,10 +130,15 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
config.depthTest = GL_FALSE;
config.depthMask = GL_TRUE;
config.colorMask = { GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE };
- if (frame.debugOptions & MapDebugOptions::Wireframe) {
- config.clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
+
+ if (isOverdraw()) {
+ config.blend = GL_TRUE;
+ config.blendFunc = { GL_CONSTANT_COLOR, GL_ONE };
+ const float overdraw = 1.0f / 8.0f;
+ config.blendColor = { overdraw, overdraw, overdraw, 0.0f };
+ config.clearColor = Color::black();
} else {
- config.clearColor = { background[0], background[1], background[2], background[3] };
+ config.clearColor = background;
}
config.clearStencil = 0;
config.clearDepth = 1;
@@ -161,20 +153,18 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
// Update all clipping IDs.
algorithm::ClipIDGenerator generator;
for (const auto& source : sources) {
- if (source->type == SourceType::Vector || source->type == SourceType::GeoJSON ||
- source->type == SourceType::Annotations) {
- source->updateClipIDs(generator);
- }
- source->updateMatrices(projMatrix, state);
+ source->baseImpl->startRender(generator, projMatrix, state);
}
- drawClippingMasks(generator.getStencils());
+ drawClippingMasks(parameters, generator.getStencils());
}
+#if defined(DEBUG)
if (frame.debugOptions & MapDebugOptions::StencilClip) {
renderClipMasks();
return;
}
+#endif
// Actually render the layers
if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
@@ -184,13 +174,15 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
// - OPAQUE PASS -------------------------------------------------------------------------------
// Render everything top-to-bottom by using reverse iterators. Render opaque objects first.
- renderPass(RenderPass::Opaque,
+ renderPass(parameters,
+ RenderPass::Opaque,
order.rbegin(), order.rend(),
0, 1);
// - TRANSLUCENT PASS --------------------------------------------------------------------------
// Make a second pass, rendering translucent objects. This time, we render bottom-to-top.
- renderPass(RenderPass::Translucent,
+ renderPass(parameters,
+ RenderPass::Translucent,
order.begin(), order.end(),
static_cast<GLsizei>(order.size()) - 1, -1);
@@ -206,7 +198,7 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
// When only rendering layers via the stylesheet, it's possible that we don't
// ever visit a tile during rendering.
for (const auto& source : sources) {
- source->finishRender(*this);
+ source->baseImpl->finishRender(*this);
}
}
@@ -215,7 +207,11 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
{
MBGL_DEBUG_GROUP("cleanup");
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, 0));
+ config.activeTexture = 1;
+ config.texture[1] = 0;
+ config.activeTexture = 0;
+ config.texture[0] = 0;
+
MBGL_CHECK_ERROR(VertexArrayObject::Unbind());
}
@@ -225,7 +221,8 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
}
template <class Iterator>
-void Painter::renderPass(RenderPass pass_,
+void Painter::renderPass(PaintParameters& parameters,
+ RenderPass pass_,
Iterator it, Iterator end,
GLsizei i, int8_t increment) {
pass = pass_;
@@ -246,7 +243,9 @@ void Painter::renderPass(RenderPass pass_,
if (!layer.baseImpl->hasRenderPass(pass))
continue;
- if (pass == RenderPass::Translucent) {
+ if (isOverdraw()) {
+ config.blend = GL_TRUE;
+ } else if (pass == RenderPass::Translucent) {
config.blendFunc.reset();
config.blend = GL_TRUE;
} else {
@@ -258,7 +257,7 @@ void Painter::renderPass(RenderPass pass_,
if (layer.is<BackgroundLayer>()) {
MBGL_DEBUG_GROUP("background");
- renderBackground(*layer.as<BackgroundLayer>());
+ renderBackground(parameters, *layer.as<BackgroundLayer>());
} else if (layer.is<CustomLayer>()) {
MBGL_DEBUG_GROUP(layer.baseImpl->id + " - custom");
VertexArrayObject::Unbind();
@@ -269,7 +268,7 @@ void Painter::renderPass(RenderPass pass_,
if (item.bucket->needsClipping()) {
setClipping(item.tile->clip);
}
- item.bucket->render(*this, layer, item.tile->id, item.tile->matrix);
+ item.bucket->render(*this, parameters, layer, *item.tile);
}
}
@@ -278,36 +277,10 @@ void Painter::renderPass(RenderPass pass_,
}
}
-mat4 Painter::translatedMatrix(const mat4& matrix,
- const std::array<float, 2>& translation,
- const UnwrappedTileID& id,
- TranslateAnchorType anchor) {
- if (translation[0] == 0 && translation[1] == 0) {
- return matrix;
- } else {
- mat4 vtxMatrix;
- if (anchor == TranslateAnchorType::Viewport) {
- const double sin_a = std::sin(-state.getAngle());
- const double cos_a = std::cos(-state.getAngle());
- matrix::translate(vtxMatrix, matrix,
- id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()),
- id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()),
- 0);
- } else {
- matrix::translate(vtxMatrix, matrix,
- id.pixelsToTileUnits(translation[0], state.getZoom()),
- id.pixelsToTileUnits(translation[1], state.getZoom()),
- 0);
- }
-
- return vtxMatrix;
- }
-}
-
void Painter::setDepthSublayer(int n) {
float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon;
float farDepth = nearDepth + depthRangeSize;
config.depthRange = { nearDepth, farDepth };
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
index 25627a8d7a..b291c14b14 100644
--- a/src/mbgl/renderer/painter.hpp
+++ b/src/mbgl/renderer/painter.hpp
@@ -27,12 +27,12 @@
namespace mbgl {
-class Tile;
+class RenderTile;
class SpriteAtlas;
class GlyphAtlas;
class LineAtlas;
struct FrameData;
-class TileData;
+class Tile;
class DebugBucket;
class FillBucket;
@@ -41,27 +41,15 @@ class CircleBucket;
class SymbolBucket;
class RasterBucket;
+class Shaders;
class SDFShader;
-class PlainShader;
-class OutlineShader;
-class OutlinePatternShader;
-class LineShader;
-class LinejoinShader;
-class LineSDFShader;
-class LinepatternShader;
-class CircleShader;
-class PatternShader;
-class IconShader;
-class RasterShader;
-class SDFGlyphShader;
-class SDFIconShader;
-class CollisionBoxShader;
+class PaintParameters;
struct ClipID;
namespace util {
class ObjectStore;
-}
+} // namespace util
namespace style {
class Style;
@@ -72,7 +60,7 @@ class CircleLayer;
class SymbolLayer;
class RasterLayer;
class BackgroundLayer;
-}
+} // namespace style
struct FrameData {
std::array<uint16_t, 2> framebufferSize;
@@ -93,54 +81,53 @@ public:
SpriteAtlas& annotationSpriteAtlas);
// Renders debug information for a tile.
- void renderTileDebug(const Tile& tile);
+ void renderTileDebug(const RenderTile&);
// Renders the red debug frame around a tile, visualizing its perimeter.
void renderDebugFrame(const mat4 &matrix);
+#if defined(DEBUG)
+ // Renders tile clip boundaries, using stencil buffer to calculate fill color.
void renderClipMasks();
+#endif
- void renderDebugText(TileData&, const mat4&);
- void renderFill(FillBucket&, const style::FillLayer&, const UnwrappedTileID&, const mat4&);
- void renderLine(LineBucket&, const style::LineLayer&, const UnwrappedTileID&, const mat4&);
- void renderCircle(CircleBucket&, const style::CircleLayer&, const UnwrappedTileID&, const mat4&);
- void renderSymbol(SymbolBucket&, const style::SymbolLayer&, const UnwrappedTileID&, const mat4&);
- void renderRaster(RasterBucket&, const style::RasterLayer&, const UnwrappedTileID&, const mat4&);
- void renderBackground(const style::BackgroundLayer&);
+ void renderDebugText(Tile&, const mat4&);
+ void renderFill(PaintParameters&, FillBucket&, const style::FillLayer&, const RenderTile&);
+ void renderLine(PaintParameters&, LineBucket&, const style::LineLayer&, const RenderTile&);
+ void renderCircle(PaintParameters&, CircleBucket&, const style::CircleLayer&, const RenderTile&);
+ void renderSymbol(PaintParameters&, SymbolBucket&, const style::SymbolLayer&, const RenderTile&);
+ void renderRaster(PaintParameters&, RasterBucket&, const style::RasterLayer&, const RenderTile&);
+ void renderBackground(PaintParameters&, const style::BackgroundLayer&);
float saturationFactor(float saturation);
float contrastFactor(float contrast);
std::array<float, 3> spinWeights(float spin_value);
- void drawClippingMasks(const std::map<UnwrappedTileID, ClipID>&);
+ void drawClippingMasks(PaintParameters&, const std::map<UnwrappedTileID, ClipID>&);
bool needsAnimation() const;
private:
- mat4 translatedMatrix(const mat4& matrix,
- const std::array<float, 2>& translation,
- const UnwrappedTileID& id,
- style::TranslateAnchorType anchor);
-
std::vector<RenderItem> determineRenderOrder(const style::Style&);
template <class Iterator>
- void renderPass(RenderPass,
+ void renderPass(PaintParameters&,
+ RenderPass,
Iterator it, Iterator end,
GLsizei i, int8_t increment);
void setClipping(const ClipID&);
- void renderSDF(SymbolBucket &bucket,
- const UnwrappedTileID &tileID,
- const mat4 &matrixSymbol,
+ void renderSDF(SymbolBucket&,
+ const RenderTile&,
float scaleDivisor,
std::array<float, 2> texsize,
SDFShader& sdfShader,
- void (SymbolBucket::*drawSDF)(SDFShader&, gl::ObjectStore&),
+ void (SymbolBucket::*drawSDF)(SDFShader&, gl::ObjectStore&, bool),
// Layout
- style::RotationAlignmentType rotationAlignment,
+ style::AlignmentType rotationAlignment,
+ style::AlignmentType pitchAlignment,
float layoutSize,
// Paint
@@ -155,18 +142,15 @@ private:
void setDepthSublayer(int n);
- mat4 projMatrix;
- mat4 nativeMatrix;
+#if defined(DEBUG)
+ bool isOverdraw() const { return frame.debugOptions & MapDebugOptions::Overdraw; }
+#else
+ bool isOverdraw() const { return false; }
+#endif
- std::array<float, 2> extrudeScale;
+ mat4 projMatrix;
- // used to composite images and flips the geometry upside down
- const mat4 flipMatrix = []{
- mat4 flip;
- matrix::ortho(flip, 0, util::EXTENT, -util::EXTENT, 0, 0, 1);
- matrix::translate(flip, flip, 0, -util::EXTENT, 0);
- return flip;
- }();
+ std::array<float, 2> pixelsToGLUnits;
const mat4 identityMatrix = []{
mat4 identity;
@@ -196,45 +180,38 @@ private:
FrameHistory frameHistory;
- std::unique_ptr<PlainShader> plainShader;
- std::unique_ptr<OutlineShader> outlineShader;
- std::unique_ptr<OutlinePatternShader> outlinePatternShader;
- std::unique_ptr<LineShader> lineShader;
- std::unique_ptr<LineSDFShader> linesdfShader;
- std::unique_ptr<LinepatternShader> linepatternShader;
- std::unique_ptr<PatternShader> patternShader;
- std::unique_ptr<IconShader> iconShader;
- std::unique_ptr<RasterShader> rasterShader;
- std::unique_ptr<SDFGlyphShader> sdfGlyphShader;
- std::unique_ptr<SDFIconShader> sdfIconShader;
- std::unique_ptr<CollisionBoxShader> collisionBoxShader;
- std::unique_ptr<CircleShader> circleShader;
+ std::unique_ptr<Shaders> shaders;
+#if defined(DEBUG)
+ std::unique_ptr<Shaders> overdrawShaders;
+#endif
// Set up the stencil quad we're using to generate the stencil mask.
- StaticVertexBuffer tileStencilBuffer = {
+ StaticVertexBuffer tileStencilBuffer {
// top left triangle
- { 0, 0 },
- { util::EXTENT, 0 },
- { 0, util::EXTENT },
+ {{ 0, 0 }},
+ {{ util::EXTENT, 0 }},
+ {{ 0, util::EXTENT }},
// bottom right triangle
- { util::EXTENT, 0 },
- { 0, util::EXTENT },
- { util::EXTENT, util::EXTENT },
+ {{ util::EXTENT, 0 }},
+ {{ 0, util::EXTENT }},
+ {{ util::EXTENT, util::EXTENT }},
};
- VertexArrayObject coveringPlainArray;
- VertexArrayObject coveringRasterArray;
- VertexArrayObject backgroundPatternArray;
- VertexArrayObject backgroundArray;
+ StaticRasterVertexBuffer rasterBoundsBuffer {
+ {{ 0, 0, 0, 0 }},
+ {{ util::EXTENT, 0, 32767, 0 }},
+ {{ 0, util::EXTENT, 0, 32767 }},
+ {{ util::EXTENT, util::EXTENT, 32767, 32767 }},
+ };
// Set up the tile boundary lines we're using to draw the tile outlines.
- StaticVertexBuffer tileBorderBuffer = {
- { 0, 0 },
- { util::EXTENT, 0 },
- { util::EXTENT, util::EXTENT },
- { 0, util::EXTENT },
- { 0, 0 },
+ StaticVertexBuffer tileBorderBuffer {
+ {{ 0, 0 }},
+ {{ util::EXTENT, 0 }},
+ {{ util::EXTENT, util::EXTENT }},
+ {{ 0, util::EXTENT }},
+ {{ 0, 0 }},
};
VertexArrayObject tileBorderArray;
diff --git a/src/mbgl/renderer/painter_background.cpp b/src/mbgl/renderer/painter_background.cpp
index 07e5821ce8..51a892575b 100644
--- a/src/mbgl/renderer/painter_background.cpp
+++ b/src/mbgl/renderer/painter_background.cpp
@@ -1,9 +1,9 @@
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/background_layer_impl.hpp>
-#include <mbgl/shader/pattern_shader.hpp>
-#include <mbgl/shader/plain_shader.hpp>
+#include <mbgl/shader/shaders.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/tile_cover.hpp>
@@ -12,7 +12,7 @@ namespace mbgl {
using namespace style;
-void Painter::renderBackground(const BackgroundLayer& layer) {
+void Painter::renderBackground(PaintParameters& parameters, const BackgroundLayer& layer) {
// Note that for bottommost layers without a pattern, the background color is drawn with
// glClear rather than this method.
const BackgroundPaintProperties& properties = layer.impl->paint;
@@ -21,8 +21,10 @@ void Painter::renderBackground(const BackgroundLayer& layer) {
optional<SpriteAtlasPosition> imagePosA;
optional<SpriteAtlasPosition> imagePosB;
- bool wireframe = frame.debugOptions & MapDebugOptions::Wireframe;
- isPatterned &= !wireframe;
+ auto& patternShader = parameters.shaders.pattern;
+ auto& plainShader = parameters.shaders.plain;
+ auto& arrayBackgroundPattern = parameters.shaders.backgroundPatternArray;
+ auto& arrayBackground = parameters.shaders.backgroundArray;
if (isPatterned) {
imagePosA = spriteAtlas->getPosition(properties.backgroundPattern.value.from, true);
@@ -31,29 +33,24 @@ void Painter::renderBackground(const BackgroundLayer& layer) {
if (!imagePosA || !imagePosB)
return;
- config.program = patternShader->getID();
- patternShader->u_matrix = identityMatrix;
- patternShader->u_pattern_tl_a = (*imagePosA).tl;
- patternShader->u_pattern_br_a = (*imagePosA).br;
- patternShader->u_pattern_tl_b = (*imagePosB).tl;
- patternShader->u_pattern_br_b = (*imagePosB).br;
- patternShader->u_mix = properties.backgroundPattern.value.t;
- patternShader->u_opacity = properties.backgroundOpacity;
+ config.program = patternShader.getID();
+ patternShader.u_matrix = identityMatrix;
+ patternShader.u_pattern_tl_a = imagePosA->tl;
+ patternShader.u_pattern_br_a = imagePosA->br;
+ patternShader.u_pattern_tl_b = imagePosB->tl;
+ patternShader.u_pattern_br_b = imagePosB->br;
+ patternShader.u_mix = properties.backgroundPattern.value.t;
+ patternShader.u_opacity = properties.backgroundOpacity;
- spriteAtlas->bind(true, store);
- backgroundPatternArray.bind(*patternShader, tileStencilBuffer, BUFFER_OFFSET(0), store);
+ spriteAtlas->bind(true, store, config, 0);
+ arrayBackgroundPattern.bind(patternShader, tileStencilBuffer, BUFFER_OFFSET(0), store);
} else {
- if (wireframe) {
- plainShader->u_color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
- plainShader->u_opacity = 1.0f;
- } else {
- plainShader->u_color = properties.backgroundColor;
- plainShader->u_opacity = properties.backgroundOpacity;
- }
+ config.program = plainShader.getID();
+ plainShader.u_color = properties.backgroundColor;
+ plainShader.u_opacity = properties.backgroundOpacity;
- config.program = plainShader->getID();
- backgroundArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0), store);
+ arrayBackground.bind(plainShader, tileStencilBuffer, BUFFER_OFFSET(0), store);
}
config.stencilTest = GL_FALSE;
@@ -62,52 +59,30 @@ void Painter::renderBackground(const BackgroundLayer& layer) {
config.depthMask = GL_FALSE;
setDepthSublayer(0);
- auto tileIDs = util::tileCover(state, state.getIntegerZoom());
-
- for (auto& id : tileIDs) {
- mat4 vtxMatrix;
- state.matrixFor(vtxMatrix, id);
- matrix::multiply(vtxMatrix, projMatrix, vtxMatrix);
+ for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) {
+ mat4 vertexMatrix;
+ state.matrixFor(vertexMatrix, tileID);
+ matrix::multiply(vertexMatrix, projMatrix, vertexMatrix);
if (isPatterned) {
- patternShader->u_matrix = vtxMatrix;
-
- std::array<int, 2> imageSizeScaledA = {{
- (int)((*imagePosA).size[0] * properties.backgroundPattern.value.fromScale),
- (int)((*imagePosA).size[1] * properties.backgroundPattern.value.fromScale)
- }};
- std::array<int, 2> imageSizeScaledB = {{
- (int)((*imagePosB).size[0] * properties.backgroundPattern.value.toScale),
- (int)((*imagePosB).size[1] * properties.backgroundPattern.value.toScale)
- }};
-
- patternShader->u_patternscale_a = {{
- 1.0f / id.pixelsToTileUnits(imageSizeScaledA[0], state.getIntegerZoom()),
- 1.0f / id.pixelsToTileUnits(imageSizeScaledA[1], state.getIntegerZoom())
- }};
- patternShader->u_patternscale_b = {{
- 1.0f / id.pixelsToTileUnits(imageSizeScaledB[0], state.getIntegerZoom()),
- 1.0f / id.pixelsToTileUnits(imageSizeScaledB[1], state.getIntegerZoom())
- }};
-
- float offsetAx = (std::fmod(util::tileSize, imageSizeScaledA[0]) * id.canonical.x) /
- (float)imageSizeScaledA[0];
- float offsetAy = (std::fmod(util::tileSize, imageSizeScaledA[1]) * id.canonical.y) /
- (float)imageSizeScaledA[1];
-
- float offsetBx = (std::fmod(util::tileSize, imageSizeScaledB[0]) * id.canonical.x) /
- (float)imageSizeScaledB[0];
- float offsetBy = (std::fmod(util::tileSize, imageSizeScaledB[1]) * id.canonical.y) /
- (float)imageSizeScaledB[1];
-
- patternShader->u_offset_a = std::array<float, 2>{{offsetAx, offsetAy}};
- patternShader->u_offset_b = std::array<float, 2>{{offsetBx, offsetBy}};
+ patternShader.u_matrix = vertexMatrix;
+ patternShader.u_pattern_size_a = imagePosA->size;
+ patternShader.u_pattern_size_b = imagePosB->size;
+ patternShader.u_scale_a = properties.backgroundPattern.value.fromScale;
+ patternShader.u_scale_b = properties.backgroundPattern.value.toScale;
+ patternShader.u_tile_units_to_pixels = 1.0f / tileID.pixelsToTileUnits(1.0f, state.getIntegerZoom());
+
+ GLint tileSizeAtNearestZoom = util::tileSize * state.zoomScale(state.getIntegerZoom() - tileID.canonical.z);
+ GLint pixelX = tileSizeAtNearestZoom * (tileID.canonical.x + tileID.wrap * state.zoomScale(tileID.canonical.z));
+ GLint pixelY = tileSizeAtNearestZoom * tileID.canonical.y;
+ patternShader.u_pixel_coord_upper = {{ float(pixelX >> 16), float(pixelY >> 16) }};
+ patternShader.u_pixel_coord_lower = {{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }};
} else {
- plainShader->u_matrix = vtxMatrix;
+ plainShader.u_matrix = vertexMatrix;
}
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)tileStencilBuffer.index()));
}
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_circle.cpp b/src/mbgl/renderer/painter_circle.cpp
index 9f2cd17f7f..f91370ff22 100644
--- a/src/mbgl/renderer/painter_circle.cpp
+++ b/src/mbgl/renderer/painter_circle.cpp
@@ -1,19 +1,21 @@
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/circle_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/circle_layer_impl.hpp>
-#include <mbgl/shader/circle_shader.hpp>
+#include <mbgl/shader/shaders.hpp>
namespace mbgl {
using namespace style;
-void Painter::renderCircle(CircleBucket& bucket,
+void Painter::renderCircle(PaintParameters& parameters,
+ CircleBucket& bucket,
const CircleLayer& layer,
- const UnwrappedTileID& tileID,
- const mat4& matrix) {
+ const RenderTile& tile) {
// Abort early.
if (pass == RenderPass::Opaque) return;
@@ -24,26 +26,32 @@ void Painter::renderCircle(CircleBucket& bucket,
setDepthSublayer(0);
const CirclePaintProperties& properties = layer.impl->paint;
- mat4 vtxMatrix = translatedMatrix(matrix, properties.circleTranslate, tileID,
- properties.circleTranslateAnchor);
-
- // Antialiasing factor: this is a minimum blur distance that serves as
- // a faux-antialiasing for the circle. since blur is a ratio of the circle's
- // size and the intent is to keep the blur at roughly 1px, the two
- // are inversely related.
- float antialiasing = 1 / frame.pixelRatio / properties.circleRadius;
-
- config.program = circleShader->getID();
-
- circleShader->u_matrix = vtxMatrix;
- circleShader->u_extrude_scale = extrudeScale;
- circleShader->u_devicepixelratio = frame.pixelRatio;
- circleShader->u_color = properties.circleColor;
- circleShader->u_radius = properties.circleRadius;
- circleShader->u_blur = std::max<float>(properties.circleBlur, antialiasing);
- circleShader->u_opacity = properties.circleOpacity;
-
- bucket.drawCircles(*circleShader, store);
+ auto& circleShader = parameters.shaders.circle;
+
+ config.program = circleShader.getID();
+
+ circleShader.u_matrix = tile.translatedMatrix(properties.circleTranslate,
+ properties.circleTranslateAnchor,
+ state);
+
+ if (properties.circlePitchScale == CirclePitchScaleType::Map) {
+ circleShader.u_extrude_scale = {{
+ pixelsToGLUnits[0] * state.getAltitude(),
+ pixelsToGLUnits[1] * state.getAltitude()
+ }};
+ circleShader.u_scale_with_map = true;
+ } else {
+ circleShader.u_extrude_scale = pixelsToGLUnits;
+ circleShader.u_scale_with_map = false;
+ }
+
+ circleShader.u_devicepixelratio = frame.pixelRatio;
+ circleShader.u_color = properties.circleColor;
+ circleShader.u_radius = properties.circleRadius;
+ circleShader.u_blur = properties.circleBlur;
+ circleShader.u_opacity = properties.circleOpacity;
+
+ bucket.drawCircles(circleShader, store);
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp
index e6ce1a040e..7cb8e01c57 100644
--- a/src/mbgl/renderer/painter_clipping.cpp
+++ b/src/mbgl/renderer/painter_clipping.cpp
@@ -1,20 +1,24 @@
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/style/source.hpp>
-#include <mbgl/shader/plain_shader.hpp>
+#include <mbgl/shader/shaders.hpp>
#include <mbgl/util/clip_id.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/gl/debugging.hpp>
-using namespace mbgl;
+namespace mbgl {
-void Painter::drawClippingMasks(const std::map<UnwrappedTileID, ClipID>& stencils) {
+void Painter::drawClippingMasks(PaintParameters& parameters, const std::map<UnwrappedTileID, ClipID>& stencils) {
MBGL_DEBUG_GROUP("clipping masks");
+ auto& plainShader = parameters.shaders.plain;
+ auto& arrayCoveringPlain = parameters.shaders.coveringPlainArray;
+
mat4 matrix;
const GLuint mask = 0b11111111;
- config.program = plainShader->getID();
+ config.program = plainShader.getID();
config.stencilOp.reset();
config.stencilTest = GL_TRUE;
config.depthTest = GL_FALSE;
@@ -22,7 +26,7 @@ void Painter::drawClippingMasks(const std::map<UnwrappedTileID, ClipID>& stencil
config.colorMask = { GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE };
config.stencilMask = mask;
- coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET_0, store);
+ arrayCoveringPlain.bind(plainShader, tileStencilBuffer, BUFFER_OFFSET_0, store);
for (const auto& stencil : stencils) {
const auto& id = stencil.first;
@@ -31,10 +35,12 @@ void Painter::drawClippingMasks(const std::map<UnwrappedTileID, ClipID>& stencil
MBGL_DEBUG_GROUP(std::string{ "mask: " } + util::toString(id));
state.matrixFor(matrix, id);
matrix::multiply(matrix, projMatrix, matrix);
- plainShader->u_matrix = matrix;
+ plainShader.u_matrix = matrix;
const GLint ref = (GLint)(clip.reference.to_ulong());
config.stencilFunc = { GL_ALWAYS, ref, mask };
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index()));
}
}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp
index b55a3c1628..8bb8e9d646 100644
--- a/src/mbgl/renderer/painter_debug.cpp
+++ b/src/mbgl/renderer/painter_debug.cpp
@@ -1,22 +1,23 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/debug_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/tile/tile.hpp>
-#include <mbgl/tile/tile_data.hpp>
-#include <mbgl/shader/plain_shader.hpp>
+#include <mbgl/shader/shaders.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/gl/debugging.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/gl_values.hpp>
#include <mbgl/gl/gl_helper.hpp>
+#include <mbgl/util/color.hpp>
-using namespace mbgl;
+namespace mbgl {
-void Painter::renderTileDebug(const Tile& tile) {
+void Painter::renderTileDebug(const RenderTile& tile) {
MBGL_DEBUG_GROUP(std::string { "debug " } + util::toString(tile.id));
if (frame.debugOptions != MapDebugOptions::NoDebug) {
setClipping(tile.clip);
if (frame.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
- renderDebugText(tile.data, tile.matrix);
+ renderDebugText(tile.tile, tile.matrix);
}
if (frame.debugOptions & MapDebugOptions::TileBorders) {
renderDebugFrame(tile.matrix);
@@ -24,39 +25,41 @@ void Painter::renderTileDebug(const Tile& tile) {
}
}
-void Painter::renderDebugText(TileData& tileData, const mat4 &matrix) {
+void Painter::renderDebugText(Tile& tile, const mat4 &matrix) {
MBGL_DEBUG_GROUP("debug text");
config.depthTest = GL_FALSE;
- if (!tileData.debugBucket || tileData.debugBucket->renderable != tileData.isRenderable() ||
- tileData.debugBucket->complete != tileData.isComplete() ||
- !(tileData.debugBucket->modified == tileData.modified) ||
- !(tileData.debugBucket->expires == tileData.expires) ||
- tileData.debugBucket->debugMode != frame.debugOptions) {
- tileData.debugBucket = std::make_unique<DebugBucket>(
- tileData.id, tileData.isRenderable(), tileData.isComplete(), tileData.modified,
- tileData.expires, frame.debugOptions);
+ if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() ||
+ tile.debugBucket->complete != tile.isComplete() ||
+ !(tile.debugBucket->modified == tile.modified) ||
+ !(tile.debugBucket->expires == tile.expires) ||
+ tile.debugBucket->debugMode != frame.debugOptions) {
+ tile.debugBucket = std::make_unique<DebugBucket>(
+ tile.id, tile.isRenderable(), tile.isComplete(), tile.modified,
+ tile.expires, frame.debugOptions);
}
- config.program = plainShader->getID();
- plainShader->u_matrix = matrix;
+ auto& plainShader = shaders->plain;
+ config.program = plainShader.getID();
+ plainShader.u_matrix = matrix;
+ plainShader.u_opacity = 1.0f;
// Draw white outline
- plainShader->u_color = {{ 1.0f, 1.0f, 1.0f, 1.0f }};
+ plainShader.u_color = Color::white();
config.lineWidth = 4.0f * frame.pixelRatio;
- tileData.debugBucket->drawLines(*plainShader, store);
+ tile.debugBucket->drawLines(plainShader, store);
#ifndef GL_ES_VERSION_2_0
// Draw line "end caps"
MBGL_CHECK_ERROR(glPointSize(2));
- tileData.debugBucket->drawPoints(*plainShader, store);
+ tile.debugBucket->drawPoints(plainShader, store);
#endif
// Draw black text.
- plainShader->u_color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
+ plainShader.u_color = Color::black();
config.lineWidth = 2.0f * frame.pixelRatio;
- tileData.debugBucket->drawLines(*plainShader, store);
+ tile.debugBucket->drawLines(plainShader, store);
config.depthFunc.reset();
config.depthTest = GL_TRUE;
@@ -72,16 +75,19 @@ void Painter::renderDebugFrame(const mat4 &matrix) {
config.stencilOp.reset();
config.stencilTest = GL_TRUE;
- config.program = plainShader->getID();
- plainShader->u_matrix = matrix;
+ auto& plainShader = shaders->plain;
+ config.program = plainShader.getID();
+ plainShader.u_matrix = matrix;
+ plainShader.u_opacity = 1.0f;
// draw tile outline
- tileBorderArray.bind(*plainShader, tileBorderBuffer, BUFFER_OFFSET_0, store);
- plainShader->u_color = {{ 1.0f, 0.0f, 0.0f, 1.0f }};
+ tileBorderArray.bind(plainShader, tileBorderBuffer, BUFFER_OFFSET_0, store);
+ plainShader.u_color = { 1.0f, 0.0f, 0.0f, 1.0f };
config.lineWidth = 4.0f * frame.pixelRatio;
MBGL_CHECK_ERROR(glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)tileBorderBuffer.index()));
}
+#if defined(DEBUG)
void Painter::renderClipMasks() {
config.stencilTest = GL_FALSE;
config.depthTest = GL_FALSE;
@@ -117,3 +123,6 @@ void Painter::renderClipMasks() {
MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get()));
#endif // GL_ES_VERSION_2_0
}
+#endif // defined(DEBUG)
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp
index 4c6ad1ba0f..9af2af9d10 100644
--- a/src/mbgl/renderer/painter_fill.cpp
+++ b/src/mbgl/renderer/painter_fill.cpp
@@ -1,201 +1,171 @@
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/shader/outline_shader.hpp>
-#include <mbgl/shader/outlinepattern_shader.hpp>
-#include <mbgl/shader/pattern_shader.hpp>
-#include <mbgl/shader/plain_shader.hpp>
+#include <mbgl/shader/shaders.hpp>
+#include <mbgl/util/convert.hpp>
namespace mbgl {
using namespace style;
-void Painter::renderFill(FillBucket& bucket,
+void Painter::renderFill(PaintParameters& parameters,
+ FillBucket& bucket,
const FillLayer& layer,
- const UnwrappedTileID& tileID,
- const mat4& matrix) {
+ const RenderTile& tile) {
const FillPaintProperties& properties = layer.impl->paint;
- mat4 vtxMatrix =
- translatedMatrix(matrix, properties.fillTranslate, tileID, properties.fillTranslateAnchor);
+ mat4 vertexMatrix = tile.translatedMatrix(properties.fillTranslate,
+ properties.fillTranslateAnchor,
+ state);
- Color fill_color = properties.fillColor;
+ Color fillColor = properties.fillColor;
float opacity = properties.fillOpacity;
- Color stroke_color = properties.fillOutlineColor;
- if (stroke_color[3] < 0) {
- stroke_color = fill_color;
- }
+ const bool isOutlineColorDefined = !properties.fillOutlineColor.isUndefined();
+ Color strokeColor = isOutlineColorDefined? properties.fillOutlineColor : fillColor;
+
+ auto worldSize = util::convert<GLfloat>(frame.framebufferSize);
bool pattern = !properties.fillPattern.value.from.empty();
- bool outline = properties.fillAntialias && !pattern && stroke_color != fill_color;
- bool fringeline = properties.fillAntialias && !pattern && stroke_color == fill_color;
-
- bool wireframe = frame.debugOptions & MapDebugOptions::Wireframe;
- if (wireframe) {
- fill_color = {{ 1.0f, 1.0f, 1.0f, 1.0f }};
- stroke_color = {{ 1.0f, 1.0f, 1.0f, 1.0f }};
- opacity = 1.0f;
- pattern = false;
- outline = true;
- fringeline = true;
- }
+ bool outline = properties.fillAntialias && !pattern && isOutlineColorDefined;
+ bool fringeline = properties.fillAntialias && !pattern && !isOutlineColorDefined;
config.stencilOp.reset();
config.stencilTest = GL_TRUE;
config.depthFunc.reset();
config.depthTest = GL_TRUE;
config.depthMask = GL_TRUE;
+ config.lineWidth = 2.0f; // This is always fixed and does not depend on the pixelRatio!
+
+ auto& outlineShader = parameters.shaders.outline;
+ auto& patternShader = parameters.shaders.pattern;
+ auto& outlinePatternShader = parameters.shaders.outlinePattern;
+ auto& plainShader = parameters.shaders.plain;
// Because we're drawing top-to-bottom, and we update the stencil mask
// befrom, we have to draw the outline first (!)
if (outline && pass == RenderPass::Translucent) {
- config.program = outlineShader->getID();
- outlineShader->u_matrix = vtxMatrix;
- config.lineWidth = 2.0f; // This is always fixed and does not depend on the pixelRatio!
+ config.program = outlineShader.getID();
+ outlineShader.u_matrix = vertexMatrix;
- outlineShader->u_outline_color = stroke_color;
- outlineShader->u_opacity = opacity;
+ outlineShader.u_outline_color = strokeColor;
+ outlineShader.u_opacity = opacity;
// Draw the entire line
- outlineShader->u_world = {{
- static_cast<float>(frame.framebufferSize[0]),
- static_cast<float>(frame.framebufferSize[1])
- }};
- setDepthSublayer(0);
- bucket.drawVertices(*outlineShader, store);
+ outlineShader.u_world = worldSize;
+ if (isOutlineColorDefined) {
+ // If we defined a different color for the fill outline, we are
+ // going to ignore the bits in 0x07 and just care about the global
+ // clipping mask.
+ setDepthSublayer(2); // OK
+ } else {
+ // Otherwise, we only want to drawFill the antialiased parts that are
+ // *outside* the current shape. This is important in case the fill
+ // or stroke color is translucent. If we wouldn't clip to outside
+ // the current shape, some pixels from the outline stroke overlapped
+ // the (non-antialiased) fill.
+ setDepthSublayer(0); // OK
+ }
+ bucket.drawVertices(outlineShader, store, isOverdraw());
}
if (pattern) {
- optional<SpriteAtlasPosition> posA = spriteAtlas->getPosition(properties.fillPattern.value.from, true);
- optional<SpriteAtlasPosition> posB = spriteAtlas->getPosition(properties.fillPattern.value.to, true);
+ optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition(properties.fillPattern.value.from, true);
+ optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition(properties.fillPattern.value.to, true);
// Image fill.
- if (pass == RenderPass::Translucent && posA && posB) {
- config.program = patternShader->getID();
- patternShader->u_matrix = vtxMatrix;
- patternShader->u_pattern_tl_a = (*posA).tl;
- patternShader->u_pattern_br_a = (*posA).br;
- patternShader->u_pattern_tl_b = (*posB).tl;
- patternShader->u_pattern_br_b = (*posB).br;
- patternShader->u_opacity = properties.fillOpacity;
- patternShader->u_image = 0;
- patternShader->u_mix = properties.fillPattern.value.t;
-
- std::array<int, 2> imageSizeScaledA = {{
- (int)((*posA).size[0] * properties.fillPattern.value.fromScale),
- (int)((*posA).size[1] * properties.fillPattern.value.fromScale)
- }};
- std::array<int, 2> imageSizeScaledB = {{
- (int)((*posB).size[0] * properties.fillPattern.value.toScale),
- (int)((*posB).size[1] * properties.fillPattern.value.toScale)
- }};
-
- patternShader->u_patternscale_a = {
- { 1.0f / tileID.pixelsToTileUnits(imageSizeScaledA[0], state.getIntegerZoom()),
- 1.0f / tileID.pixelsToTileUnits(imageSizeScaledB[1], state.getIntegerZoom()) }
- };
- patternShader->u_patternscale_b = {
- { 1.0f / tileID.pixelsToTileUnits(imageSizeScaledB[0], state.getIntegerZoom()),
- 1.0f / tileID.pixelsToTileUnits(imageSizeScaledB[1], state.getIntegerZoom()) }
- };
-
- float offsetAx = (std::fmod(util::tileSize, imageSizeScaledA[0]) * tileID.canonical.x) /
- (float)imageSizeScaledA[0];
- float offsetAy = (std::fmod(util::tileSize, imageSizeScaledA[1]) * tileID.canonical.y) /
- (float)imageSizeScaledA[1];
-
- float offsetBx = (std::fmod(util::tileSize, imageSizeScaledB[0]) * tileID.canonical.x) /
- (float)imageSizeScaledB[0];
- float offsetBy = (std::fmod(util::tileSize, imageSizeScaledB[1]) * tileID.canonical.y) /
- (float)imageSizeScaledB[1];
-
- patternShader->u_offset_a = std::array<float, 2>{{offsetAx, offsetAy}};
- patternShader->u_offset_b = std::array<float, 2>{{offsetBx, offsetBy}};
-
- config.activeTexture = GL_TEXTURE0;
- spriteAtlas->bind(true, store);
+ if (pass == RenderPass::Translucent && imagePosA && imagePosB) {
+ config.program = patternShader.getID();
+ patternShader.u_matrix = vertexMatrix;
+ patternShader.u_pattern_tl_a = imagePosA->tl;
+ patternShader.u_pattern_br_a = imagePosA->br;
+ patternShader.u_pattern_tl_b = imagePosB->tl;
+ patternShader.u_pattern_br_b = imagePosB->br;
+ patternShader.u_opacity = properties.fillOpacity;
+ patternShader.u_image = 0;
+ patternShader.u_mix = properties.fillPattern.value.t;
+ patternShader.u_pattern_size_a = imagePosA->size;
+ patternShader.u_pattern_size_b = imagePosB->size;
+ patternShader.u_scale_a = properties.fillPattern.value.fromScale;
+ patternShader.u_scale_b = properties.fillPattern.value.toScale;
+ patternShader.u_tile_units_to_pixels = 1.0f / tile.id.pixelsToTileUnits(1.0f, state.getIntegerZoom());
+
+ GLint tileSizeAtNearestZoom = util::tileSize * state.zoomScale(state.getIntegerZoom() - tile.id.canonical.z);
+ GLint pixelX = tileSizeAtNearestZoom * (tile.id.canonical.x + tile.id.wrap * state.zoomScale(tile.id.canonical.z));
+ GLint pixelY = tileSizeAtNearestZoom * tile.id.canonical.y;
+ patternShader.u_pixel_coord_upper = {{ float(pixelX >> 16), float(pixelY >> 16) }};
+ patternShader.u_pixel_coord_lower = {{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }};
+
+ spriteAtlas->bind(true, store, config, 0);
// Draw the actual triangles into the color & stencil buffer.
setDepthSublayer(0);
- bucket.drawElements(*patternShader, store);
-
- if (properties.fillAntialias && stroke_color == fill_color) {
- config.program = outlinePatternShader->getID();
- outlinePatternShader->u_matrix = vtxMatrix;
- config.lineWidth = 2.0f;
+ bucket.drawElements(patternShader, store, isOverdraw());
+
+ if (properties.fillAntialias && !isOutlineColorDefined) {
+ config.program = outlinePatternShader.getID();
+ outlinePatternShader.u_matrix = vertexMatrix;
+
+ outlinePatternShader.u_pattern_tl_a = imagePosA->tl;
+ outlinePatternShader.u_pattern_br_a = imagePosA->br;
+ outlinePatternShader.u_pattern_tl_b = imagePosB->tl;
+ outlinePatternShader.u_pattern_br_b = imagePosB->br;
+ outlinePatternShader.u_opacity = properties.fillOpacity;
+ outlinePatternShader.u_image = 0;
+ outlinePatternShader.u_mix = properties.fillPattern.value.t;
+ outlinePatternShader.u_pattern_size_a = imagePosA->size;
+ outlinePatternShader.u_pattern_size_b = imagePosB->size;
+ outlinePatternShader.u_scale_a = properties.fillPattern.value.fromScale;
+ outlinePatternShader.u_scale_b = properties.fillPattern.value.toScale;
+ outlinePatternShader.u_tile_units_to_pixels = 1.0f / tile.id.pixelsToTileUnits(1.0f, state.getIntegerZoom());
+ outlinePatternShader.u_pixel_coord_upper = {{ float(pixelX >> 16), float(pixelY >> 16) }};
+ outlinePatternShader.u_pixel_coord_lower = {{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }};
// Draw the entire line
- outlinePatternShader->u_world = {{
- static_cast<float>(frame.framebufferSize[0]),
- static_cast<float>(frame.framebufferSize[1])
- }};
-
- outlinePatternShader->u_pattern_tl_a = (*posA).tl;
- outlinePatternShader->u_pattern_br_a = (*posA).br;
- outlinePatternShader->u_pattern_tl_b = (*posB).tl;
- outlinePatternShader->u_pattern_br_b = (*posB).br;
- outlinePatternShader->u_opacity = properties.fillOpacity;
- outlinePatternShader->u_image = 0;
- outlinePatternShader->u_mix = properties.fillPattern.value.t;
-
- outlinePatternShader->u_patternscale_a = {{
- 1.0f / tileID.pixelsToTileUnits(imageSizeScaledA[0], state.getIntegerZoom()),
- 1.0f / tileID.pixelsToTileUnits(imageSizeScaledB[1], state.getIntegerZoom())
- }};
- outlinePatternShader->u_patternscale_b = {{
- 1.0f / tileID.pixelsToTileUnits(imageSizeScaledB[0], state.getIntegerZoom()),
- 1.0f / tileID.pixelsToTileUnits(imageSizeScaledB[1], state.getIntegerZoom())
- }};
-
- outlinePatternShader->u_offset_a = std::array<float, 2>{{offsetAx, offsetAy}};
- outlinePatternShader->u_offset_b = std::array<float, 2>{{offsetBx, offsetBy}};
-
- config.activeTexture = GL_TEXTURE0;
- spriteAtlas->bind(true, store);
+ outlinePatternShader.u_world = worldSize;
+
+ spriteAtlas->bind(true, store, config, 0);
setDepthSublayer(2);
- bucket.drawVertices(*outlinePatternShader, store);
+ bucket.drawVertices(outlinePatternShader, store, isOverdraw());
}
}
- } else if (!wireframe) {
+ } else {
// No image fill.
- if ((fill_color[3] >= 1.0f && opacity >= 1.0f) == (pass == RenderPass::Opaque)) {
+ if ((fillColor.a >= 1.0f && opacity >= 1.0f) == (pass == RenderPass::Opaque)) {
// Only draw the fill when it's either opaque and we're drawing opaque
// fragments or when it's translucent and we're drawing translucent
// fragments
// Draw filling rectangle.
- config.program = plainShader->getID();
- plainShader->u_matrix = vtxMatrix;
- plainShader->u_color = fill_color;
- plainShader->u_opacity = opacity;
+ config.program = plainShader.getID();
+ plainShader.u_matrix = vertexMatrix;
+ plainShader.u_color = fillColor;
+ plainShader.u_opacity = opacity;
// Draw the actual triangles into the color & stencil buffer.
setDepthSublayer(1);
- bucket.drawElements(*plainShader, store);
+ bucket.drawElements(plainShader, store, isOverdraw());
}
}
// Because we're drawing top-to-bottom, and we update the stencil mask
// below, we have to draw the outline first (!)
if (fringeline && pass == RenderPass::Translucent) {
- config.program = outlineShader->getID();
- outlineShader->u_matrix = vtxMatrix;
- config.lineWidth = 2.0f; // This is always fixed and does not depend on the pixelRatio!
+ config.program = outlineShader.getID();
+ outlineShader.u_matrix = vertexMatrix;
- outlineShader->u_outline_color = fill_color;
- outlineShader->u_opacity = opacity;
+ outlineShader.u_outline_color = fillColor;
+ outlineShader.u_opacity = opacity;
// Draw the entire line
- outlineShader->u_world = {{
- static_cast<float>(frame.framebufferSize[0]),
- static_cast<float>(frame.framebufferSize[1])
- }};
+ outlineShader.u_world = worldSize;
setDepthSublayer(2);
- bucket.drawVertices(*outlineShader, store);
+ bucket.drawVertices(outlineShader, store, isOverdraw());
}
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp
index 26041a8165..ed1975f955 100644
--- a/src/mbgl/renderer/painter_line.cpp
+++ b/src/mbgl/renderer/painter_line.cpp
@@ -1,10 +1,10 @@
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/line_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/line_layer_impl.hpp>
-#include <mbgl/shader/line_shader.hpp>
-#include <mbgl/shader/linesdf_shader.hpp>
-#include <mbgl/shader/linepattern_shader.hpp>
+#include <mbgl/shader/shaders.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/util/mat2.hpp>
@@ -13,10 +13,10 @@ namespace mbgl {
using namespace style;
-void Painter::renderLine(LineBucket& bucket,
+void Painter::renderLine(PaintParameters& parameters,
+ LineBucket& bucket,
const LineLayer& layer,
- const UnwrappedTileID& tileID,
- const mat4& matrix) {
+ const RenderTile& tile) {
// Abort early.
if (pass == RenderPass::Opaque) return;
@@ -33,18 +33,11 @@ void Painter::renderLine(LineBucket& bucket,
// Retina devices need a smaller distance to avoid aliasing.
float antialiasing = 1.0 / frame.pixelRatio;
- bool wireframe = frame.debugOptions & MapDebugOptions::Wireframe;
-
float blur = properties.lineBlur + antialiasing;
- Color color = {{ 1.0f, 1.0f, 1.0f, 1.0f }};
- float opacity = 1.0f;
- if (!wireframe) {
- color = properties.lineColor;
- opacity = properties.lineOpacity;
- }
-
- const float ratio = 1.0 / tileID.pixelsToTileUnits(1.0, state.getZoom());
+ const Color color = properties.lineColor;
+ const float opacity = properties.lineOpacity;
+ const float ratio = 1.0 / tile.id.pixelsToTileUnits(1.0, state.getZoom());
mat2 antialiasingMatrix;
matrix::identity(antialiasingMatrix);
@@ -53,115 +46,117 @@ void Painter::renderLine(LineBucket& bucket,
// calculate how much longer the real world distance is at the top of the screen
// than at the middle of the screen.
- float topedgelength = std::sqrt(std::pow(state.getHeight(), 2) / 4 * (1 + std::pow(state.getAltitude(), 2)));
+ float topedgelength = std::sqrt(std::pow(state.getHeight(), 2.0f) / 4.0f * (1.0f + std::pow(state.getAltitude(), 2.0f)));
float x = state.getHeight() / 2.0f * std::tan(state.getPitch());
- float extra = (topedgelength + x) / topedgelength - 1;
+ float extra = (topedgelength + x) / topedgelength - 1.0f;
- mat4 vtxMatrix =
- translatedMatrix(matrix, properties.lineTranslate, tileID, properties.lineTranslateAnchor);
+ mat4 vtxMatrix = tile.translatedMatrix(properties.lineTranslate,
+ properties.lineTranslateAnchor,
+ state);
setDepthSublayer(0);
- if (!properties.lineDasharray.value.from.empty()) {
+ auto& linesdfShader = parameters.shaders.linesdf;
+ auto& linepatternShader = parameters.shaders.linepattern;
+ auto& lineShader = parameters.shaders.line;
- config.program = linesdfShader->getID();
+ if (!properties.lineDasharray.value.from.empty()) {
+ config.program = linesdfShader.getID();
- linesdfShader->u_matrix = vtxMatrix;
- linesdfShader->u_linewidth = properties.lineWidth / 2;
- linesdfShader->u_gapwidth = properties.lineGapWidth / 2;
- linesdfShader->u_antialiasing = antialiasing / 2;
- linesdfShader->u_ratio = ratio;
- linesdfShader->u_blur = blur;
- linesdfShader->u_color = color;
- linesdfShader->u_opacity = opacity;
+ linesdfShader.u_matrix = vtxMatrix;
+ linesdfShader.u_linewidth = properties.lineWidth / 2;
+ linesdfShader.u_gapwidth = properties.lineGapWidth / 2;
+ linesdfShader.u_antialiasing = antialiasing / 2;
+ linesdfShader.u_ratio = ratio;
+ linesdfShader.u_blur = blur;
+ linesdfShader.u_color = color;
+ linesdfShader.u_opacity = opacity;
- LinePatternPos posA = lineAtlas->getDashPosition(properties.lineDasharray.value.from, layout.lineCap == LineCapType::Round, store);
- LinePatternPos posB = lineAtlas->getDashPosition(properties.lineDasharray.value.to, layout.lineCap == LineCapType::Round, store);
+ LinePatternPos posA = lineAtlas->getDashPosition(properties.lineDasharray.value.from, layout.lineCap == LineCapType::Round);
+ LinePatternPos posB = lineAtlas->getDashPosition(properties.lineDasharray.value.to, layout.lineCap == LineCapType::Round);
const float widthA = posA.width * properties.lineDasharray.value.fromScale * layer.impl->dashLineWidth;
const float widthB = posB.width * properties.lineDasharray.value.toScale * layer.impl->dashLineWidth;
- float scaleXA = 1.0 / tileID.pixelsToTileUnits(widthA, state.getIntegerZoom());
+ float scaleXA = 1.0 / tile.id.pixelsToTileUnits(widthA, state.getIntegerZoom());
float scaleYA = -posA.height / 2.0;
- float scaleXB = 1.0 / tileID.pixelsToTileUnits(widthB, state.getIntegerZoom());
+ float scaleXB = 1.0 / tile.id.pixelsToTileUnits(widthB, state.getIntegerZoom());
float scaleYB = -posB.height / 2.0;
- linesdfShader->u_patternscale_a = {{ scaleXA, scaleYA }};
- linesdfShader->u_tex_y_a = posA.y;
- linesdfShader->u_patternscale_b = {{ scaleXB, scaleYB }};
- linesdfShader->u_tex_y_b = posB.y;
- linesdfShader->u_sdfgamma = lineAtlas->width / (std::min(widthA, widthB) * 256.0 * frame.pixelRatio) / 2;
- linesdfShader->u_mix = properties.lineDasharray.value.t;
- linesdfShader->u_extra = extra;
- linesdfShader->u_offset = -properties.lineOffset;
- linesdfShader->u_antialiasingmatrix = antialiasingMatrix;
+ linesdfShader.u_patternscale_a = {{ scaleXA, scaleYA }};
+ linesdfShader.u_tex_y_a = posA.y;
+ linesdfShader.u_patternscale_b = {{ scaleXB, scaleYB }};
+ linesdfShader.u_tex_y_b = posB.y;
+ linesdfShader.u_sdfgamma = lineAtlas->width / (std::min(widthA, widthB) * 256.0 * frame.pixelRatio) / 2;
+ linesdfShader.u_mix = properties.lineDasharray.value.t;
+ linesdfShader.u_extra = extra;
+ linesdfShader.u_offset = -properties.lineOffset;
+ linesdfShader.u_antialiasingmatrix = antialiasingMatrix;
- linesdfShader->u_image = 0;
- config.activeTexture = GL_TEXTURE0;
- lineAtlas->bind(store);
+ linesdfShader.u_image = 0;
+ lineAtlas->bind(store, config, 0);
- bucket.drawLineSDF(*linesdfShader, store);
+ bucket.drawLineSDF(linesdfShader, store, isOverdraw());
} else if (!properties.linePattern.value.from.empty()) {
optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition(properties.linePattern.value.from, true);
optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition(properties.linePattern.value.to, true);
-
+
if (!imagePosA || !imagePosB)
return;
- config.program = linepatternShader->getID();
+ config.program = linepatternShader.getID();
- linepatternShader->u_matrix = vtxMatrix;
- linepatternShader->u_linewidth = properties.lineWidth / 2;
- linepatternShader->u_gapwidth = properties.lineGapWidth / 2;
- linepatternShader->u_antialiasing = antialiasing / 2;
- linepatternShader->u_ratio = ratio;
- linepatternShader->u_blur = blur;
+ linepatternShader.u_matrix = vtxMatrix;
+ linepatternShader.u_linewidth = properties.lineWidth / 2;
+ linepatternShader.u_gapwidth = properties.lineGapWidth / 2;
+ linepatternShader.u_antialiasing = antialiasing / 2;
+ linepatternShader.u_ratio = ratio;
+ linepatternShader.u_blur = blur;
- linepatternShader->u_pattern_size_a = {{
- tileID.pixelsToTileUnits((*imagePosA).size[0] * properties.linePattern.value.fromScale, state.getIntegerZoom()),
+ linepatternShader.u_pattern_size_a = {{
+ tile.id.pixelsToTileUnits((*imagePosA).size[0] * properties.linePattern.value.fromScale, state.getIntegerZoom()),
(*imagePosA).size[1]
}};
- linepatternShader->u_pattern_tl_a = (*imagePosA).tl;
- linepatternShader->u_pattern_br_a = (*imagePosA).br;
+ linepatternShader.u_pattern_tl_a = (*imagePosA).tl;
+ linepatternShader.u_pattern_br_a = (*imagePosA).br;
- linepatternShader->u_pattern_size_b = {{
- tileID.pixelsToTileUnits((*imagePosB).size[0] * properties.linePattern.value.toScale, state.getIntegerZoom()),
+ linepatternShader.u_pattern_size_b = {{
+ tile.id.pixelsToTileUnits((*imagePosB).size[0] * properties.linePattern.value.toScale, state.getIntegerZoom()),
(*imagePosB).size[1]
}};
- linepatternShader->u_pattern_tl_b = (*imagePosB).tl;
- linepatternShader->u_pattern_br_b = (*imagePosB).br;
+ linepatternShader.u_pattern_tl_b = (*imagePosB).tl;
+ linepatternShader.u_pattern_br_b = (*imagePosB).br;
- linepatternShader->u_fade = properties.linePattern.value.t;
- linepatternShader->u_opacity = properties.lineOpacity;
- linepatternShader->u_extra = extra;
- linepatternShader->u_offset = -properties.lineOffset;
- linepatternShader->u_antialiasingmatrix = antialiasingMatrix;
+ linepatternShader.u_fade = properties.linePattern.value.t;
+ linepatternShader.u_opacity = properties.lineOpacity;
+ linepatternShader.u_extra = extra;
+ linepatternShader.u_offset = -properties.lineOffset;
+ linepatternShader.u_antialiasingmatrix = antialiasingMatrix;
- linepatternShader->u_image = 0;
- config.activeTexture = GL_TEXTURE0;
- spriteAtlas->bind(true, store);
+ linepatternShader.u_image = 0;
+ spriteAtlas->bind(true, store, config, 0);
- bucket.drawLinePatterns(*linepatternShader, store);
+ bucket.drawLinePatterns(linepatternShader, store, isOverdraw());
} else {
- config.program = lineShader->getID();
-
- lineShader->u_matrix = vtxMatrix;
- lineShader->u_linewidth = properties.lineWidth / 2;
- lineShader->u_gapwidth = properties.lineGapWidth / 2;
- lineShader->u_antialiasing = antialiasing / 2;
- lineShader->u_ratio = ratio;
- lineShader->u_blur = blur;
- lineShader->u_extra = extra;
- lineShader->u_offset = -properties.lineOffset;
- lineShader->u_antialiasingmatrix = antialiasingMatrix;
-
- lineShader->u_color = color;
- lineShader->u_opacity = opacity;
-
- bucket.drawLines(*lineShader, store);
+ config.program = lineShader.getID();
+
+ lineShader.u_matrix = vtxMatrix;
+ lineShader.u_linewidth = properties.lineWidth / 2;
+ lineShader.u_gapwidth = properties.lineGapWidth / 2;
+ lineShader.u_antialiasing = antialiasing / 2;
+ lineShader.u_ratio = ratio;
+ lineShader.u_blur = blur;
+ lineShader.u_extra = extra;
+ lineShader.u_offset = -properties.lineOffset;
+ lineShader.u_antialiasingmatrix = antialiasingMatrix;
+
+ lineShader.u_color = color;
+ lineShader.u_opacity = opacity;
+
+ bucket.drawLines(lineShader, store, isOverdraw());
}
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp
index cce71e8ce2..8f6a1ac27b 100644
--- a/src/mbgl/renderer/painter_raster.cpp
+++ b/src/mbgl/renderer/painter_raster.cpp
@@ -1,43 +1,53 @@
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/renderer/raster_bucket.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
#include <mbgl/style/layers/raster_layer_impl.hpp>
-#include <mbgl/shader/raster_shader.hpp>
+#include <mbgl/shader/shaders.hpp>
namespace mbgl {
using namespace style;
-void Painter::renderRaster(RasterBucket& bucket,
+void Painter::renderRaster(PaintParameters& parameters,
+ RasterBucket& bucket,
const RasterLayer& layer,
- const UnwrappedTileID&,
- const mat4& matrix) {
+ const RenderTile& tile) {
if (pass != RenderPass::Translucent) return;
const RasterPaintProperties& properties = layer.impl->paint;
if (bucket.hasData()) {
- config.program = rasterShader->getID();
- rasterShader->u_matrix = matrix;
- rasterShader->u_buffer = 0;
- rasterShader->u_opacity = properties.rasterOpacity;
- rasterShader->u_brightness_low = properties.rasterBrightnessMin;
- rasterShader->u_brightness_high = properties.rasterBrightnessMax;
- rasterShader->u_saturation_factor = saturationFactor(properties.rasterSaturation);
- rasterShader->u_contrast_factor = contrastFactor(properties.rasterContrast);
- rasterShader->u_spin_weights = spinWeights(properties.rasterHueRotate);
+ auto& rasterShader = parameters.shaders.raster;
+ auto& rasterVAO = parameters.shaders.coveringRasterArray;
+
+ config.program = rasterShader.getID();
+ rasterShader.u_matrix = tile.matrix;
+ rasterShader.u_buffer_scale = 1.0f;
+ rasterShader.u_opacity0 = properties.rasterOpacity;
+ rasterShader.u_opacity1 = 0;
+
+ rasterShader.u_brightness_low = properties.rasterBrightnessMin;
+ rasterShader.u_brightness_high = properties.rasterBrightnessMax;
+ rasterShader.u_saturation_factor = saturationFactor(properties.rasterSaturation);
+ rasterShader.u_contrast_factor = contrastFactor(properties.rasterContrast);
+ rasterShader.u_spin_weights = spinWeights(properties.rasterHueRotate);
config.stencilTest = GL_FALSE;
- rasterShader->u_image = 0;
- config.activeTexture = GL_TEXTURE0;
+ rasterShader.u_image0 = 0; // GL_TEXTURE0
+ rasterShader.u_image1 = 1; // GL_TEXTURE1
+ rasterShader.u_tl_parent = {{ 0.0f, 0.0f }};
+ rasterShader.u_scale_parent = 1.0f;
config.depthFunc.reset();
config.depthTest = GL_TRUE;
config.depthMask = GL_FALSE;
setDepthSublayer(0);
- bucket.drawRaster(*rasterShader, tileStencilBuffer, coveringRasterArray, store);
+
+ bucket.drawRaster(rasterShader, rasterBoundsBuffer, rasterVAO, config, store);
}
}
@@ -69,4 +79,4 @@ std::array<float, 3> Painter::spinWeights(float spin) {
return spin_weights;
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp
index 3c453242ae..8599329842 100644
--- a/src/mbgl/renderer/painter_symbol.cpp
+++ b/src/mbgl/renderer/painter_symbol.cpp
@@ -1,12 +1,12 @@
#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
-#include <mbgl/shader/sdf_shader.hpp>
-#include <mbgl/shader/icon_shader.hpp>
-#include <mbgl/shader/collision_box_shader.hpp>
+#include <mbgl/shader/shaders.hpp>
#include <mbgl/util/math.hpp>
#include <cmath>
@@ -15,16 +15,16 @@ namespace mbgl {
using namespace style;
-void Painter::renderSDF(SymbolBucket &bucket,
- const UnwrappedTileID &tileID,
- const mat4 &matrix,
+void Painter::renderSDF(SymbolBucket& bucket,
+ const RenderTile& tile,
float sdfFontSize,
std::array<float, 2> texsize,
SDFShader& sdfShader,
- void (SymbolBucket::*drawSDF)(SDFShader&, gl::ObjectStore&),
+ void (SymbolBucket::*drawSDF)(SDFShader&, gl::ObjectStore&, bool),
// Layout
- RotationAlignmentType rotationAlignment,
+ AlignmentType rotationAlignment,
+ AlignmentType pitchAlignment,
float layoutSize,
// Paint
@@ -37,39 +37,46 @@ void Painter::renderSDF(SymbolBucket &bucket,
TranslateAnchorType translateAnchor,
float paintSize)
{
- mat4 vtxMatrix = translatedMatrix(matrix, translate, tileID, translateAnchor);
+ mat4 vtxMatrix = tile.translatedMatrix(translate, translateAnchor, state);
// If layerStyle.size > bucket.info.fontSize then labels may collide
float fontSize = paintSize;
float fontScale = fontSize / sdfFontSize;
- float scale = fontScale;
- std::array<float, 2> exScale = extrudeScale;
- bool alignedWithMap = rotationAlignment == RotationAlignmentType::Map;
- float gammaScale = 1.0f;
+ bool rotateWithMap = rotationAlignment == AlignmentType::Map;
+ bool pitchWithMap = pitchAlignment == AlignmentType::Map;
- if (alignedWithMap) {
- scale *= tileID.pixelsToTileUnits(1, state.getZoom());
- exScale.fill(scale);
- gammaScale /= std::cos(state.getPitch());
+ std::array<float, 2> extrudeScale;
+ float gammaScale;
+
+ if (pitchWithMap) {
+ gammaScale = 1.0 / std::cos(state.getPitch());
+ extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom()) * fontScale);
} else {
- exScale = {{ exScale[0] * scale, exScale[1] * scale }};
+ gammaScale = 1.0;
+ extrudeScale = {{
+ pixelsToGLUnits[0] * fontScale * state.getAltitude(),
+ pixelsToGLUnits[1] * fontScale * state.getAltitude()
+ }};
}
config.program = sdfShader.getID();
sdfShader.u_matrix = vtxMatrix;
- sdfShader.u_extrude_scale = exScale;
+ sdfShader.u_extrude_scale = extrudeScale;
sdfShader.u_texsize = texsize;
- sdfShader.u_skewed = alignedWithMap;
+ sdfShader.u_rotate_with_map = rotateWithMap;
+ sdfShader.u_pitch_with_map = pitchWithMap;
sdfShader.u_texture = 0;
+ sdfShader.u_pitch = state.getPitch() * util::DEG2RAD;
+ sdfShader.u_bearing = -1.0f * state.getAngle();
+ sdfShader.u_aspect_ratio = (state.getWidth() * 1.0f) / (state.getHeight() * 1.0f);
// adjust min/max zooms for variable font sies
float zoomAdjust = std::log(fontSize / layoutSize) / std::log(2);
sdfShader.u_zoom = (state.getZoom() - zoomAdjust) * 10; // current zoom level
- config.activeTexture = GL_TEXTURE1;
- frameHistory.bind(store);
+ frameHistory.bind(store, config, 1);
sdfShader.u_fadetexture = 1;
// The default gamma value has to be adjust for the current pixelratio so that we're not
@@ -82,32 +89,32 @@ void Painter::renderSDF(SymbolBucket &bucket,
// We're drawing in the translucent pass which is bottom-to-top, so we need
// to draw the halo first.
- if (haloColor[3] > 0.0f && haloWidth > 0.0f) {
+ if (haloColor.a > 0.0f && haloWidth > 0.0f) {
sdfShader.u_gamma = (haloBlur * blurOffset / fontScale / sdfPx + gamma) * gammaScale;
sdfShader.u_color = haloColor;
sdfShader.u_opacity = opacity;
sdfShader.u_buffer = (haloOffset - haloWidth / fontScale) / sdfPx;
setDepthSublayer(0);
- (bucket.*drawSDF)(sdfShader, store);
+ (bucket.*drawSDF)(sdfShader, store, isOverdraw());
}
// Then, we draw the text/icon over the halo
- if (color[3] > 0.0f) {
+ if (color.a > 0.0f) {
sdfShader.u_gamma = gamma * gammaScale;
sdfShader.u_color = color;
sdfShader.u_opacity = opacity;
sdfShader.u_buffer = (256.0f - 64.0f) / 256.0f;
setDepthSublayer(1);
- (bucket.*drawSDF)(sdfShader, store);
+ (bucket.*drawSDF)(sdfShader, store, isOverdraw());
}
}
-void Painter::renderSymbol(SymbolBucket& bucket,
+void Painter::renderSymbol(PaintParameters& parameters,
+ SymbolBucket& bucket,
const SymbolLayer& layer,
- const UnwrappedTileID& tileID,
- const mat4& matrix) {
+ const RenderTile& tile) {
// Abort early.
if (pass == RenderPass::Opaque) {
return;
@@ -135,7 +142,7 @@ void Painter::renderSymbol(SymbolBucket& bucket,
}
if (bucket.hasIconData()) {
- if (layout.iconRotationAlignment == RotationAlignmentType::Map) {
+ if (layout.iconRotationAlignment == AlignmentType::Map) {
config.depthFunc.reset();
config.depthTest = GL_TRUE;
} else {
@@ -145,7 +152,7 @@ void Painter::renderSymbol(SymbolBucket& bucket,
bool sdf = bucket.sdfIcons;
const float angleOffset =
- layout.iconRotationAlignment == RotationAlignmentType::Map
+ layout.iconRotationAlignment == AlignmentType::Map
? state.getAngle()
: 0;
@@ -154,19 +161,20 @@ void Painter::renderSymbol(SymbolBucket& bucket,
SpriteAtlas* activeSpriteAtlas = layer.impl->spriteAtlas;
const bool iconScaled = fontScale != 1 || frame.pixelRatio != activeSpriteAtlas->getPixelRatio() || bucket.iconsNeedLinear;
- const bool iconTransformed = layout.iconRotationAlignment == RotationAlignmentType::Map || angleOffset != 0 || state.getPitch() != 0;
- config.activeTexture = GL_TEXTURE0;
- activeSpriteAtlas->bind(sdf || state.isChanging() || iconScaled || iconTransformed, store);
+ const bool iconTransformed = layout.iconRotationAlignment == AlignmentType::Map || angleOffset != 0 || state.getPitch() != 0;
+ activeSpriteAtlas->bind(sdf || state.isChanging() || iconScaled || iconTransformed, store, config, 0);
if (sdf) {
renderSDF(bucket,
- tileID,
- matrix,
+ tile,
1.0f,
{{ float(activeSpriteAtlas->getWidth()) / 4.0f, float(activeSpriteAtlas->getHeight()) / 4.0f }},
- *sdfIconShader,
+ parameters.shaders.sdfIcon,
&SymbolBucket::drawIcons,
layout.iconRotationAlignment,
+ // icon-pitch-alignment is not yet implemented
+ // and we simply inherit the rotation alignment
+ layout.iconRotationAlignment,
layout.iconSize,
paint.iconOpacity,
paint.iconColor,
@@ -177,59 +185,62 @@ void Painter::renderSymbol(SymbolBucket& bucket,
paint.iconTranslateAnchor,
layer.impl->iconSize);
} else {
- mat4 vtxMatrix =
- translatedMatrix(matrix, paint.iconTranslate, tileID, paint.iconTranslateAnchor);
+ mat4 vtxMatrix = tile.translatedMatrix(paint.iconTranslate,
+ paint.iconTranslateAnchor,
+ state);
+
+ std::array<float, 2> extrudeScale;
- float scale = fontScale;
- std::array<float, 2> exScale = extrudeScale;
- const bool alignedWithMap = layout.iconRotationAlignment == RotationAlignmentType::Map;
+ const bool alignedWithMap = layout.iconRotationAlignment == AlignmentType::Map;
if (alignedWithMap) {
- scale *= tileID.pixelsToTileUnits(1, state.getZoom());
- exScale.fill(scale);
+ extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom()) * fontScale);
} else {
- exScale = {{ exScale[0] * scale, exScale[1] * scale }};
+ extrudeScale = {{
+ pixelsToGLUnits[0] * fontScale * state.getAltitude(),
+ pixelsToGLUnits[1] * fontScale * state.getAltitude()
+ }};
}
- config.program = iconShader->getID();
- iconShader->u_matrix = vtxMatrix;
- iconShader->u_extrude_scale = exScale;
- iconShader->u_texsize = {{ float(activeSpriteAtlas->getWidth()) / 4.0f, float(activeSpriteAtlas->getHeight()) / 4.0f }};
- iconShader->u_skewed = alignedWithMap;
- iconShader->u_texture = 0;
+ auto& iconShader = parameters.shaders.icon;
+
+ config.program = iconShader.getID();
+ iconShader.u_matrix = vtxMatrix;
+ iconShader.u_extrude_scale = extrudeScale;
+ iconShader.u_texsize = {{ float(activeSpriteAtlas->getWidth()) / 4.0f, float(activeSpriteAtlas->getHeight()) / 4.0f }};
+ iconShader.u_rotate_with_map = alignedWithMap;
+ iconShader.u_texture = 0;
// adjust min/max zooms for variable font sies
float zoomAdjust = std::log(fontSize / layout.iconSize) / std::log(2);
- iconShader->u_zoom = (state.getZoom() - zoomAdjust) * 10; // current zoom level
- iconShader->u_opacity = paint.iconOpacity;
+ iconShader.u_zoom = (state.getZoom() - zoomAdjust) * 10; // current zoom level
+ iconShader.u_opacity = paint.iconOpacity;
- config.activeTexture = GL_TEXTURE1;
- frameHistory.bind(store);
- iconShader->u_fadetexture = 1;
+ frameHistory.bind(store, config, 1);
+ iconShader.u_fadetexture = 1;
setDepthSublayer(0);
- bucket.drawIcons(*iconShader, store);
+ bucket.drawIcons(iconShader, store, isOverdraw());
}
}
if (bucket.hasTextData()) {
- if (layout.textRotationAlignment == RotationAlignmentType::Map) {
+ if (layout.textRotationAlignment == AlignmentType::Map) {
config.depthFunc.reset();
config.depthTest = GL_TRUE;
} else {
config.depthTest = GL_FALSE;
}
- config.activeTexture = GL_TEXTURE0;
- glyphAtlas->bind(store);
+ glyphAtlas->bind(store, config, 0);
renderSDF(bucket,
- tileID,
- matrix,
+ tile,
24.0f,
{{ float(glyphAtlas->width) / 4, float(glyphAtlas->height) / 4 }},
- *sdfGlyphShader,
+ parameters.shaders.sdfGlyph,
&SymbolBucket::drawGlyphs,
layout.textRotationAlignment,
+ layout.textPitchAlignment,
layout.textSize,
paint.textOpacity,
paint.textColor,
@@ -245,20 +256,19 @@ void Painter::renderSymbol(SymbolBucket& bucket,
config.stencilOp.reset();
config.stencilTest = GL_TRUE;
- config.program = collisionBoxShader->getID();
- collisionBoxShader->u_matrix = matrix;
+ auto& collisionBoxShader = shaders->collisionBox;
+ config.program = collisionBoxShader.getID();
+ collisionBoxShader.u_matrix = tile.matrix;
// TODO: This was the overscaled z instead of the canonical z.
- collisionBoxShader->u_scale = std::pow(2, state.getZoom() - tileID.canonical.z);
- collisionBoxShader->u_zoom = state.getZoom() * 10;
- collisionBoxShader->u_maxzoom = (tileID.canonical.z + 1) * 10;
+ collisionBoxShader.u_scale = std::pow(2, state.getZoom() - tile.id.canonical.z);
+ collisionBoxShader.u_zoom = state.getZoom() * 10;
+ collisionBoxShader.u_maxzoom = (tile.id.canonical.z + 1) * 10;
config.lineWidth = 1.0f;
setDepthSublayer(0);
- bucket.drawCollisionBoxes(*collisionBoxShader, store);
+ bucket.drawCollisionBoxes(collisionBoxShader, store);
}
-
- config.activeTexture = GL_TEXTURE0;
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp
index 39f8dacd92..58647a330d 100644
--- a/src/mbgl/renderer/raster_bucket.cpp
+++ b/src/mbgl/renderer/raster_bucket.cpp
@@ -7,32 +7,33 @@ namespace mbgl {
using namespace style;
-RasterBucket::RasterBucket(gl::TexturePool& texturePool)
-: raster(texturePool) {
-}
-
-void RasterBucket::upload(gl::ObjectStore& store) {
+void RasterBucket::upload(gl::ObjectStore& store, gl::Config& config) {
if (hasData()) {
- raster.upload(store);
+ raster.upload(store, config, 0);
uploaded = true;
}
}
void RasterBucket::render(Painter& painter,
+ PaintParameters& parameters,
const Layer& layer,
- const UnwrappedTileID& tileID,
- const mat4& matrix) {
- painter.renderRaster(*this, *layer.as<RasterLayer>(), tileID, matrix);
+ const RenderTile& tile) {
+ painter.renderRaster(parameters, *this, *layer.as<RasterLayer>(), tile);
}
void RasterBucket::setImage(PremultipliedImage image) {
raster.load(std::move(image));
}
-void RasterBucket::drawRaster(RasterShader& shader, StaticVertexBuffer &vertices, VertexArrayObject &array, gl::ObjectStore& store) {
- raster.bind(true, store);
+void RasterBucket::drawRaster(RasterShader& shader,
+ StaticRasterVertexBuffer& vertices,
+ VertexArrayObject& array,
+ gl::Config& config,
+ gl::ObjectStore& store) {
+ raster.bind(store, config, 0, Raster::Scaling::Linear);
+ raster.bind(store, config, 1, Raster::Scaling::Linear);
array.bind(shader, vertices, BUFFER_OFFSET_0, store);
- MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices.index()));
+ MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)vertices.index()));
}
bool RasterBucket::hasData() const {
@@ -43,4 +44,4 @@ bool RasterBucket::needsClipping() const {
return false;
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/renderer/raster_bucket.hpp b/src/mbgl/renderer/raster_bucket.hpp
index 93331755fb..e212b9ecea 100644
--- a/src/mbgl/renderer/raster_bucket.hpp
+++ b/src/mbgl/renderer/raster_bucket.hpp
@@ -2,25 +2,24 @@
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/util/raster.hpp>
+#include <mbgl/gl/gl_config.hpp>
namespace mbgl {
class RasterShader;
-class StaticVertexBuffer;
+class StaticRasterVertexBuffer;
class VertexArrayObject;
class RasterBucket : public Bucket {
public:
- RasterBucket(gl::TexturePool&);
-
- void upload(gl::ObjectStore&) override;
- void render(Painter&, const style::Layer&, const UnwrappedTileID&, const mat4&) override;
+ void upload(gl::ObjectStore&, gl::Config&) override;
+ void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override;
bool hasData() const override;
bool needsClipping() const override;
void setImage(PremultipliedImage);
- void drawRaster(RasterShader&, StaticVertexBuffer&, VertexArrayObject&, gl::ObjectStore&);
+ void drawRaster(RasterShader&, StaticRasterVertexBuffer&, VertexArrayObject&, gl::Config&, gl::ObjectStore&);
Raster raster;
};
diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp
index 6c97b66bd1..82ea897047 100644
--- a/src/mbgl/renderer/render_item.hpp
+++ b/src/mbgl/renderer/render_item.hpp
@@ -7,30 +7,30 @@
namespace mbgl {
-class Tile;
+class RenderTile;
class Bucket;
namespace style {
class Layer;
class Source;
-}
+} // namespace style
class RenderItem {
public:
RenderItem(const style::Layer& layer_,
- const Tile* tile_ = nullptr,
+ const RenderTile* tile_ = nullptr,
Bucket* bucket_ = nullptr)
: tile(tile_), bucket(bucket_), layer(layer_) {
}
- const Tile* const tile;
+ const RenderTile* const tile;
Bucket* const bucket;
const style::Layer& layer;
};
class RenderData {
public:
- Color backgroundColor = {{ 0, 0, 0, 0 }};
+ Color backgroundColor;
std::set<style::Source*> sources;
std::vector<RenderItem> order;
};
diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp
index e31383b7cf..93edac2039 100644
--- a/src/mbgl/renderer/render_pass.hpp
+++ b/src/mbgl/renderer/render_pass.hpp
@@ -12,16 +12,16 @@ enum class RenderPass : uint8_t {
Translucent = 1 << 1,
};
-constexpr inline RenderPass operator|(RenderPass a, RenderPass b) {
- return static_cast<RenderPass>(mbgl::underlying_type(a) | mbgl::underlying_type(b));
+constexpr RenderPass operator|(RenderPass a, RenderPass b) {
+ return RenderPass(mbgl::underlying_type(a) | mbgl::underlying_type(b));
}
-inline RenderPass operator|=(RenderPass& a, RenderPass b) {
+constexpr RenderPass& operator|=(RenderPass& a, RenderPass b) {
return (a = a | b);
}
-constexpr inline RenderPass operator&(RenderPass a, RenderPass b) {
- return static_cast<RenderPass>(mbgl::underlying_type(a) & mbgl::underlying_type(b));
+constexpr RenderPass operator&(RenderPass a, RenderPass b) {
+ return RenderPass(mbgl::underlying_type(a) & mbgl::underlying_type(b));
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp
new file mode 100644
index 0000000000..5c7c491be0
--- /dev/null
+++ b/src/mbgl/renderer/render_tile.cpp
@@ -0,0 +1,34 @@
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation,
+ TranslateAnchorType anchor,
+ const TransformState& state) const {
+ if (translation[0] == 0 && translation[1] == 0) {
+ return matrix;
+ }
+
+ mat4 vtxMatrix;
+
+ if (anchor == TranslateAnchorType::Viewport) {
+ const double sin_a = std::sin(-state.getAngle());
+ const double cos_a = std::cos(-state.getAngle());
+ matrix::translate(vtxMatrix, matrix,
+ id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()),
+ id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()),
+ 0);
+ } else {
+ matrix::translate(vtxMatrix, matrix,
+ id.pixelsToTileUnits(translation[0], state.getZoom()),
+ id.pixelsToTileUnits(translation[1], state.getZoom()),
+ 0);
+ }
+
+ return vtxMatrix;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp
new file mode 100644
index 0000000000..82b60a66c7
--- /dev/null
+++ b/src/mbgl/renderer/render_tile.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/clip_id.hpp>
+#include <mbgl/style/types.hpp>
+
+#include <array>
+
+namespace mbgl {
+
+class Tile;
+class TransformState;
+
+class RenderTile {
+public:
+ RenderTile(UnwrappedTileID id_, Tile& tile_) : id(std::move(id_)), tile(tile_) {}
+ RenderTile(const RenderTile&) = delete;
+ RenderTile(RenderTile&&) = default;
+ RenderTile& operator=(const RenderTile&) = delete;
+ RenderTile& operator=(RenderTile&&) = default;
+
+ const UnwrappedTileID id;
+ Tile& tile;
+ ClipID clip;
+ mat4 matrix;
+
+ mat4 translatedMatrix(const std::array<float, 2>& translate,
+ style::TranslateAnchorType anchor,
+ const TransformState&) const;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp
index f4e55432c1..008372b2bf 100644
--- a/src/mbgl/renderer/symbol_bucket.cpp
+++ b/src/mbgl/renderer/symbol_bucket.cpp
@@ -1,7 +1,7 @@
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/style/filter_evaluator.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
@@ -51,29 +51,29 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line,
// Create the quad used for rendering the icon.
iconQuads(addToBuffers && shapedIcon ?
- getIconQuads(anchor, shapedIcon, line, layout, iconAlongLine) :
+ getIconQuads(anchor, shapedIcon, line, layout, iconAlongLine, shapedText) :
SymbolQuads()),
// Create the collision features that will be used to check whether this symbol instance can be placed
textCollisionFeature(line, anchor, shapedText, textBoxScale, textPadding, textAlongLine, indexedFeature),
iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconAlongLine, indexedFeature)
- {};
+ {}
-SymbolBucket::SymbolBucket(uint32_t overscaling_, float zoom_, const MapMode mode_, const std::string& bucketName_, const std::string& sourceLayerName_)
+SymbolBucket::SymbolBucket(uint32_t overscaling_, float zoom_, const MapMode mode_, std::string bucketName_, std::string sourceLayerName_)
: overscaling(overscaling_),
zoom(zoom_),
tileSize(util::tileSize * overscaling_),
tilePixelRatio(float(util::EXTENT) / tileSize),
mode(mode_),
- bucketName(bucketName_),
- sourceLayerName(sourceLayerName_) {}
+ bucketName(std::move(bucketName_)),
+ sourceLayerName(std::move(sourceLayerName_)) {}
SymbolBucket::~SymbolBucket() {
// Do not remove. header file only contains forward definitions to unique pointers.
}
-void SymbolBucket::upload(gl::ObjectStore& store) {
+void SymbolBucket::upload(gl::ObjectStore& store, gl::Config&) {
if (hasTextData()) {
renderData->text.vertices.upload(store);
renderData->text.triangles.upload(store);
@@ -87,10 +87,10 @@ void SymbolBucket::upload(gl::ObjectStore& store) {
}
void SymbolBucket::render(Painter& painter,
+ PaintParameters& parameters,
const Layer& layer,
- const UnwrappedTileID& tileID,
- const mat4& matrix) {
- painter.renderSymbol(*this, *layer.as<SymbolLayer>(), tileID, matrix);
+ const RenderTile& tile) {
+ painter.renderSymbol(parameters, *this, *layer.as<SymbolLayer>(), tile);
}
bool SymbolBucket::hasData() const { return hasTextData() || hasIconData() || !symbolInstances.empty(); }
@@ -119,9 +119,7 @@ void SymbolBucket::parseFeatures(const GeometryTileLayer& layer, const Filter& f
const GLsizei featureCount = static_cast<GLsizei>(layer.featureCount());
for (GLsizei i = 0; i < featureCount; i++) {
auto feature = layer.getFeature(i);
-
- FilterEvaluator evaluator(*feature);
- if (!Filter::visit(filter, evaluator))
+ if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
continue;
SymbolFeature ft;
@@ -284,6 +282,8 @@ void SymbolBucket::addFeatures(uintptr_t tileUID,
}
if ((*image).relativePixelRatio != 1.0f) {
iconsNeedLinear = true;
+ } else if (layout.iconRotate != 0) {
+ iconsNeedLinear = true;
}
}
}
@@ -314,10 +314,10 @@ void SymbolBucket::addFeature(const GeometryCollection &lines,
const float iconPadding = layout.iconPadding * tilePixelRatio;
const float textMaxAngle = layout.textMaxAngle * util::DEG2RAD;
const bool textAlongLine =
- layout.textRotationAlignment == RotationAlignmentType::Map &&
+ layout.textRotationAlignment == AlignmentType::Map &&
layout.symbolPlacement == SymbolPlacementType::Line;
const bool iconAlongLine =
- layout.iconRotationAlignment == RotationAlignmentType::Map &&
+ layout.iconRotationAlignment == AlignmentType::Map &&
layout.symbolPlacement == SymbolPlacementType::Line;
const bool mayOverlap = layout.textAllowOverlap || layout.iconAllowOverlap ||
layout.textIgnorePlacement || layout.iconIgnorePlacement;
@@ -370,7 +370,7 @@ void SymbolBucket::addFeature(const GeometryCollection &lines,
}
}
}
-
+
bool SymbolBucket::anchorIsTooClose(const std::u32string &text, const float repeatDistance, Anchor &anchor) {
if (compareText.find(text) == compareText.end()) {
compareText.emplace(text, Anchors());
@@ -394,10 +394,10 @@ void SymbolBucket::placeFeatures(CollisionTile& collisionTile) {
// create the bufers used for rendering.
const bool textAlongLine =
- layout.textRotationAlignment == RotationAlignmentType::Map &&
+ layout.textRotationAlignment == AlignmentType::Map &&
layout.symbolPlacement == SymbolPlacementType::Line;
const bool iconAlongLine =
- layout.iconRotationAlignment == RotationAlignmentType::Map &&
+ layout.iconRotationAlignment == AlignmentType::Map &&
layout.symbolPlacement == SymbolPlacementType::Line;
const bool mayOverlap = layout.textAllowOverlap || layout.iconAllowOverlap ||
@@ -495,7 +495,7 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float
const auto &anchorPoint = symbol.anchorPoint;
// drop upside down versions of glyphs
- const float a = std::fmod(symbol.angle + placementAngle + M_PI, M_PI * 2);
+ const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2);
if (keepUpright && alongLine && (a <= M_PI / 2 || a > M_PI * 3 / 2)) continue;
@@ -521,15 +521,18 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float
auto &triangleGroup = *buffer.groups.back();
GLsizei triangleIndex = triangleGroup.vertex_length;
+ // Encode angle of glyph
+ uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256);
+
// coordinates (2 triangles)
buffer.vertices.add(anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom,
- maxZoom, placementZoom);
+ maxZoom, placementZoom, glyphAngle);
buffer.vertices.add(anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y,
- minZoom, maxZoom, placementZoom);
+ minZoom, maxZoom, placementZoom, glyphAngle);
buffer.vertices.add(anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h,
- minZoom, maxZoom, placementZoom);
+ minZoom, maxZoom, placementZoom, glyphAngle);
buffer.vertices.add(anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h,
- minZoom, maxZoom, placementZoom);
+ minZoom, maxZoom, placementZoom, glyphAngle);
// add the two triangles, referencing the four coordinates we just inserted.
buffer.triangles.add(triangleIndex + 0, triangleIndex + 1, triangleIndex + 2);
@@ -598,39 +601,39 @@ void SymbolBucket::swapRenderData() {
}
}
-void SymbolBucket::drawGlyphs(SDFShader& shader, gl::ObjectStore& store) {
+void SymbolBucket::drawGlyphs(SDFShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte *vertex_index = BUFFER_OFFSET_0;
GLbyte *elements_index = BUFFER_OFFSET_0;
auto& text = renderData->text;
for (auto &group : text.groups) {
assert(group);
- group->array[0].bind(shader, text.vertices, text.triangles, vertex_index, store);
+ group->array[overdraw ? 1 : 0].bind(shader, text.vertices, text.triangles, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
vertex_index += group->vertex_length * text.vertices.itemSize;
elements_index += group->elements_length * text.triangles.itemSize;
}
}
-void SymbolBucket::drawIcons(SDFShader& shader, gl::ObjectStore& store) {
+void SymbolBucket::drawIcons(SDFShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte *vertex_index = BUFFER_OFFSET_0;
GLbyte *elements_index = BUFFER_OFFSET_0;
auto& icon = renderData->icon;
for (auto &group : icon.groups) {
assert(group);
- group->array[0].bind(shader, icon.vertices, icon.triangles, vertex_index, store);
+ group->array[overdraw ? 1 : 0].bind(shader, icon.vertices, icon.triangles, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
vertex_index += group->vertex_length * icon.vertices.itemSize;
elements_index += group->elements_length * icon.triangles.itemSize;
}
}
-void SymbolBucket::drawIcons(IconShader& shader, gl::ObjectStore& store) {
+void SymbolBucket::drawIcons(IconShader& shader, gl::ObjectStore& store, bool overdraw) {
GLbyte *vertex_index = BUFFER_OFFSET_0;
GLbyte *elements_index = BUFFER_OFFSET_0;
auto& icon = renderData->icon;
for (auto &group : icon.groups) {
assert(group);
- group->array[1].bind(shader, icon.vertices, icon.triangles, vertex_index, store);
+ group->array[overdraw ? 3 : 2].bind(shader, icon.vertices, icon.triangles, vertex_index, store);
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elements_index));
vertex_index += group->vertex_length * icon.vertices.itemSize;
elements_index += group->elements_length * icon.triangles.itemSize;
diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp
index 314d44bdee..79c32e9d88 100644
--- a/src/mbgl/renderer/symbol_bucket.hpp
+++ b/src/mbgl/renderer/symbol_bucket.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
@@ -61,16 +61,16 @@ class SymbolInstance {
};
class SymbolBucket : public Bucket {
- typedef ElementGroup<1> TextElementGroup;
- typedef ElementGroup<2> IconElementGroup;
+ typedef ElementGroup<2> TextElementGroup;
+ typedef ElementGroup<4> IconElementGroup;
typedef ElementGroup<1> CollisionBoxElementGroup;
public:
- SymbolBucket(uint32_t overscaling, float zoom, const MapMode, const std::string& bucketName_, const std::string& sourceLayerName_);
+ SymbolBucket(uint32_t overscaling, float zoom, const MapMode, std::string bucketName_, std::string sourceLayerName_);
~SymbolBucket() override;
- void upload(gl::ObjectStore&) override;
- void render(Painter&, const style::Layer&, const UnwrappedTileID&, const mat4&) override;
+ void upload(gl::ObjectStore&, gl::Config&) override;
+ void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override;
bool hasData() const override;
bool hasTextData() const;
bool hasIconData() const;
@@ -82,9 +82,9 @@ public:
GlyphAtlas&,
GlyphStore&);
- void drawGlyphs(SDFShader&, gl::ObjectStore&);
- void drawIcons(SDFShader&, gl::ObjectStore&);
- void drawIcons(IconShader&, gl::ObjectStore&);
+ void drawGlyphs(SDFShader&, gl::ObjectStore&, bool overdraw);
+ void drawIcons(SDFShader&, gl::ObjectStore&, bool overdraw);
+ void drawIcons(IconShader&, gl::ObjectStore&, bool overdraw);
void drawCollisionBoxes(CollisionBoxShader&, gl::ObjectStore&);
void parseFeatures(const GeometryTileLayer&, const style::Filter&);
@@ -98,7 +98,7 @@ private:
const size_t index);
bool anchorIsTooClose(const std::u32string &text, const float repeatDistance, Anchor &anchor);
std::map<std::u32string, std::vector<Anchor>> compareText;
-
+
void addToDebugBuffers(CollisionTile &collisionTile);
void swapRenderData() override;
diff --git a/src/mbgl/shader/circle_shader.cpp b/src/mbgl/shader/circle_shader.cpp
index 2865769ee2..b19dbeba68 100644
--- a/src/mbgl/shader/circle_shader.cpp
+++ b/src/mbgl/shader/circle_shader.cpp
@@ -3,15 +3,18 @@
#include <mbgl/shader/circle.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-CircleShader::CircleShader(gl::ObjectStore& store)
- : Shader("circle", shaders::circle::vertex, shaders::circle::fragment, store) {
+CircleShader::CircleShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::circle::name,
+ shaders::circle::vertex,
+ shaders::circle::fragment,
+ store, defines) {
}
void CircleShader::bind(GLbyte* offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 4, offset));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/circle_shader.hpp b/src/mbgl/shader/circle_shader.hpp
index cd22d4c9b1..c454fc1337 100644
--- a/src/mbgl/shader/circle_shader.hpp
+++ b/src/mbgl/shader/circle_shader.hpp
@@ -2,22 +2,24 @@
#include <mbgl/shader/shader.hpp>
#include <mbgl/shader/uniform.hpp>
+#include <mbgl/util/color.hpp>
namespace mbgl {
class CircleShader : public Shader {
public:
- CircleShader(gl::ObjectStore&);
+ CircleShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
UniformMatrix<4> u_matrix = {"u_matrix", *this};
Uniform<std::array<GLfloat, 2>> u_extrude_scale = {"u_extrude_scale", *this};
Uniform<GLfloat> u_devicepixelratio = {"u_devicepixelratio", *this};
- Uniform<std::array<GLfloat, 4>> u_color = {"u_color", *this};
+ Uniform<Color> u_color = {"u_color", *this};
Uniform<GLfloat> u_radius = {"u_radius", *this};
Uniform<GLfloat> u_blur = {"u_blur", *this};
Uniform<GLfloat> u_opacity = {"u_opacity", *this};
+ Uniform<GLint> u_scale_with_map = {"u_scale_with_map", *this};
};
} // namespace mbgl
diff --git a/src/mbgl/shader/collision_box_shader.cpp b/src/mbgl/shader/collision_box_shader.cpp
index cf4ff81e96..bfee89fac2 100644
--- a/src/mbgl/shader/collision_box_shader.cpp
+++ b/src/mbgl/shader/collision_box_shader.cpp
@@ -3,14 +3,13 @@
#include <mbgl/shader/collisionbox.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
-
-using namespace mbgl;
+namespace mbgl {
CollisionBoxShader::CollisionBoxShader(gl::ObjectStore& store)
- : Shader("collisionbox", shaders::collisionbox::vertex, shaders::collisionbox::fragment, store)
- , a_extrude(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_extrude")))
- , a_data(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_data"))) {
+ : Shader(shaders::collisionbox::name,
+ shaders::collisionbox::vertex,
+ shaders::collisionbox::fragment,
+ store) {
}
void CollisionBoxShader::bind(GLbyte *offset) {
@@ -24,5 +23,6 @@ void CollisionBoxShader::bind(GLbyte *offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_data));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_data, 2, GL_UNSIGNED_BYTE, false, stride, offset + 8));
-
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/collision_box_shader.hpp b/src/mbgl/shader/collision_box_shader.hpp
index a9b38a9849..bbd2611ce8 100644
--- a/src/mbgl/shader/collision_box_shader.hpp
+++ b/src/mbgl/shader/collision_box_shader.hpp
@@ -16,10 +16,6 @@ public:
Uniform<GLfloat> u_scale = {"u_scale", *this};
Uniform<GLfloat> u_zoom = {"u_zoom", *this};
Uniform<GLfloat> u_maxzoom = {"u_maxzoom", *this};
-
-protected:
- GLint a_extrude = -1;
- GLint a_data = -1;
};
} // namespace mbgl
diff --git a/src/mbgl/shader/icon_shader.cpp b/src/mbgl/shader/icon_shader.cpp
index 7fb0335dc8..d91fbe2ec0 100644
--- a/src/mbgl/shader/icon_shader.cpp
+++ b/src/mbgl/shader/icon_shader.cpp
@@ -3,15 +3,13 @@
#include <mbgl/shader/icon.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-IconShader::IconShader(gl::ObjectStore& store)
- : Shader("icon", shaders::icon::vertex, shaders::icon::fragment, store)
- , a_offset(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_offset")))
- , a_data1(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_data1")))
- , a_data2(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_data2"))) {
+IconShader::IconShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::icon::name,
+ shaders::icon::vertex,
+ shaders::icon::fragment,
+ store, defines) {
}
void IconShader::bind(GLbyte* offset) {
@@ -29,3 +27,5 @@ void IconShader::bind(GLbyte* offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_data2));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_data2, 4, GL_UNSIGNED_BYTE, false, stride, offset + 12));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/icon_shader.hpp b/src/mbgl/shader/icon_shader.hpp
index b8cfcabb10..86c7b8ece0 100644
--- a/src/mbgl/shader/icon_shader.hpp
+++ b/src/mbgl/shader/icon_shader.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class IconShader : public Shader {
public:
- IconShader(gl::ObjectStore&);
+ IconShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
@@ -16,14 +16,9 @@ public:
Uniform<GLfloat> u_zoom = {"u_zoom", *this};
Uniform<GLfloat> u_opacity = {"u_opacity", *this};
Uniform<std::array<GLfloat, 2>> u_texsize = {"u_texsize", *this};
- Uniform<GLint> u_skewed = {"u_skewed", *this};
+ Uniform<GLint> u_rotate_with_map = {"u_rotate_with_map", *this};
Uniform<GLint> u_texture = {"u_texture", *this};
Uniform<GLint> u_fadetexture = {"u_fadetexture", *this};
-
-protected:
- GLint a_offset = -1;
- GLint a_data1 = -1;
- GLint a_data2 = -1;
};
} // namespace mbgl
diff --git a/src/mbgl/shader/line_shader.cpp b/src/mbgl/shader/line_shader.cpp
index f21384082f..8c3ad32d73 100644
--- a/src/mbgl/shader/line_shader.cpp
+++ b/src/mbgl/shader/line_shader.cpp
@@ -3,13 +3,13 @@
#include <mbgl/shader/line.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-LineShader::LineShader(gl::ObjectStore& store)
- : Shader("line", shaders::line::vertex, shaders::line::fragment, store)
- , a_data(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_data"))) {
+LineShader::LineShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::line::name,
+ shaders::line::vertex,
+ shaders::line::fragment,
+ store, defines) {
}
void LineShader::bind(GLbyte* offset) {
@@ -19,3 +19,5 @@ void LineShader::bind(GLbyte* offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_data));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_data, 4, GL_UNSIGNED_BYTE, false, 8, offset + 4));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/line_shader.hpp b/src/mbgl/shader/line_shader.hpp
index e0d14094a6..a2cda56314 100644
--- a/src/mbgl/shader/line_shader.hpp
+++ b/src/mbgl/shader/line_shader.hpp
@@ -2,17 +2,18 @@
#include <mbgl/shader/shader.hpp>
#include <mbgl/shader/uniform.hpp>
+#include <mbgl/util/color.hpp>
namespace mbgl {
class LineShader : public Shader {
public:
- LineShader(gl::ObjectStore&);
+ LineShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<std::array<GLfloat, 4>> u_color = {"u_color", *this};
+ Uniform<Color> u_color = {"u_color", *this};
Uniform<GLfloat> u_opacity = {"u_opacity", *this};
Uniform<GLfloat> u_ratio = {"u_ratio", *this};
Uniform<GLfloat> u_linewidth = {"u_linewidth", *this};
@@ -22,9 +23,6 @@ public:
Uniform<GLfloat> u_extra = {"u_extra", *this};
Uniform<GLfloat> u_offset = {"u_offset", *this};
UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this};
-
-private:
- GLint a_data = -1;
};
diff --git a/src/mbgl/shader/linepattern_shader.cpp b/src/mbgl/shader/linepattern_shader.cpp
index a9923233af..48375dec24 100644
--- a/src/mbgl/shader/linepattern_shader.cpp
+++ b/src/mbgl/shader/linepattern_shader.cpp
@@ -3,13 +3,13 @@
#include <mbgl/shader/linepattern.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-LinepatternShader::LinepatternShader(gl::ObjectStore& store)
- : Shader("linepattern", shaders::linepattern::vertex, shaders::linepattern::fragment, store)
- , a_data(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_data"))) {
+LinepatternShader::LinepatternShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::linepattern::name,
+ shaders::linepattern::vertex,
+ shaders::linepattern::fragment,
+ store, defines) {
}
void LinepatternShader::bind(GLbyte* offset) {
@@ -19,3 +19,5 @@ void LinepatternShader::bind(GLbyte* offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_data));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_data, 4, GL_UNSIGNED_BYTE, false, 8, offset + 4));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/linepattern_shader.hpp b/src/mbgl/shader/linepattern_shader.hpp
index c34c1a5292..6c44716d10 100644
--- a/src/mbgl/shader/linepattern_shader.hpp
+++ b/src/mbgl/shader/linepattern_shader.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class LinepatternShader : public Shader {
public:
- LinepatternShader(gl::ObjectStore&);
+ LinepatternShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
@@ -30,8 +30,6 @@ public:
Uniform<GLfloat> u_offset = {"u_offset", *this};
Uniform<GLint> u_image = {"u_image", *this};
UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this};
-
-private:
- GLint a_data = -1;
};
+
} // namespace mbgl
diff --git a/src/mbgl/shader/linesdf_shader.cpp b/src/mbgl/shader/linesdf_shader.cpp
index b316d2562d..8b2e983e0d 100644
--- a/src/mbgl/shader/linesdf_shader.cpp
+++ b/src/mbgl/shader/linesdf_shader.cpp
@@ -3,13 +3,13 @@
#include <mbgl/shader/linesdfpattern.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-LineSDFShader::LineSDFShader(gl::ObjectStore& store)
- : Shader("linesdfpattern", shaders::linesdfpattern::vertex, shaders::linesdfpattern::fragment, store)
- , a_data(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_data"))) {
+LineSDFShader::LineSDFShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::linesdfpattern::name,
+ shaders::linesdfpattern::vertex,
+ shaders::linesdfpattern::fragment,
+ store, defines) {
}
void LineSDFShader::bind(GLbyte* offset) {
@@ -19,3 +19,5 @@ void LineSDFShader::bind(GLbyte* offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_data));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_data, 4, GL_UNSIGNED_BYTE, false, 8, offset + 4));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/linesdf_shader.hpp b/src/mbgl/shader/linesdf_shader.hpp
index 8791f243c0..ce1fe4d17c 100644
--- a/src/mbgl/shader/linesdf_shader.hpp
+++ b/src/mbgl/shader/linesdf_shader.hpp
@@ -2,17 +2,18 @@
#include <mbgl/shader/shader.hpp>
#include <mbgl/shader/uniform.hpp>
+#include <mbgl/util/color.hpp>
namespace mbgl {
class LineSDFShader : public Shader {
public:
- LineSDFShader(gl::ObjectStore&);
+ LineSDFShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<std::array<GLfloat, 4>> u_color = {"u_color", *this};
+ Uniform<Color> u_color = {"u_color", *this};
Uniform<GLfloat> u_opacity = {"u_opacity", *this};
Uniform<GLfloat> u_linewidth = {"u_linewidth", *this};
Uniform<GLfloat> u_gapwidth = {"u_gapwidth", *this};
@@ -29,9 +30,6 @@ public:
Uniform<GLfloat> u_extra = {"u_extra", *this};
Uniform<GLfloat> u_offset = {"u_offset", *this};
UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this};
-
-private:
- GLint a_data = -1;
};
diff --git a/src/mbgl/shader/outline_shader.cpp b/src/mbgl/shader/outline_shader.cpp
index 58a647f375..58f73d9f44 100644
--- a/src/mbgl/shader/outline_shader.cpp
+++ b/src/mbgl/shader/outline_shader.cpp
@@ -3,15 +3,18 @@
#include <mbgl/shader/outline.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-OutlineShader::OutlineShader(gl::ObjectStore& store)
- : Shader("outline", shaders::outline::vertex, shaders::outline::fragment, store) {
+OutlineShader::OutlineShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::outline::name,
+ shaders::outline::vertex,
+ shaders::outline::fragment,
+ store, defines) {
}
void OutlineShader::bind(GLbyte* offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/outline_shader.hpp b/src/mbgl/shader/outline_shader.hpp
index 25c873b4fe..5840ad2238 100644
--- a/src/mbgl/shader/outline_shader.hpp
+++ b/src/mbgl/shader/outline_shader.hpp
@@ -2,17 +2,18 @@
#include <mbgl/shader/shader.hpp>
#include <mbgl/shader/uniform.hpp>
+#include <mbgl/util/color.hpp>
namespace mbgl {
class OutlineShader : public Shader {
public:
- OutlineShader(gl::ObjectStore&);
+ OutlineShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<std::array<GLfloat, 4>> u_outline_color = {"u_outline_color", *this};
+ Uniform<Color> u_outline_color = {"u_outline_color", *this};
Uniform<GLfloat> u_opacity = {"u_opacity", *this};
Uniform<std::array<GLfloat, 2>> u_world = {"u_world", *this};
};
diff --git a/src/mbgl/shader/outlinepattern_shader.cpp b/src/mbgl/shader/outlinepattern_shader.cpp
index 5ffa458a4f..2373a75370 100644
--- a/src/mbgl/shader/outlinepattern_shader.cpp
+++ b/src/mbgl/shader/outlinepattern_shader.cpp
@@ -3,19 +3,18 @@
#include <mbgl/shader/outlinepattern.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-OutlinePatternShader::OutlinePatternShader(gl::ObjectStore& store)
- : Shader(
- "outlinepattern",
- shaders::outlinepattern::vertex, shaders::outlinepattern::fragment,
- store
- ) {
+OutlinePatternShader::OutlinePatternShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::outlinepattern::name,
+ shaders::outlinepattern::vertex,
+ shaders::outlinepattern::fragment,
+ store, defines) {
}
void OutlinePatternShader::bind(GLbyte *offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/outlinepattern_shader.hpp b/src/mbgl/shader/outlinepattern_shader.hpp
index e124923b6f..6869763412 100644
--- a/src/mbgl/shader/outlinepattern_shader.hpp
+++ b/src/mbgl/shader/outlinepattern_shader.hpp
@@ -7,23 +7,26 @@ namespace mbgl {
class OutlinePatternShader : public Shader {
public:
- OutlinePatternShader(gl::ObjectStore&);
+ OutlinePatternShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
- UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<std::array<GLfloat, 2>> u_pattern_tl_a = {"u_pattern_tl_a", *this};
- Uniform<std::array<GLfloat, 2>> u_pattern_br_a = {"u_pattern_br_a", *this};
- Uniform<std::array<GLfloat, 2>> u_pattern_tl_b = {"u_pattern_tl_b", *this};
- Uniform<std::array<GLfloat, 2>> u_pattern_br_b = {"u_pattern_br_b", *this};
- Uniform<GLfloat> u_opacity = {"u_opacity", *this};
- Uniform<GLfloat> u_mix = {"u_mix", *this};
- Uniform<GLint> u_image = {"u_image", *this};
- Uniform<std::array<GLfloat, 2>> u_patternscale_a = {"u_patternscale_a", *this};
- Uniform<std::array<GLfloat, 2>> u_patternscale_b = {"u_patternscale_b", *this};
- Uniform<std::array<GLfloat, 2>> u_offset_a = {"u_offset_a", *this};
- Uniform<std::array<GLfloat, 2>> u_offset_b = {"u_offset_b", *this};
- Uniform<std::array<GLfloat, 2>> u_world = {"u_world", *this};
+ UniformMatrix<4> u_matrix = {"u_matrix", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_tl_a = {"u_pattern_tl_a", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_br_a = {"u_pattern_br_a", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_tl_b = {"u_pattern_tl_b", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_br_b = {"u_pattern_br_b", *this};
+ Uniform<GLfloat> u_opacity = {"u_opacity", *this};
+ Uniform<GLfloat> u_mix = {"u_mix", *this};
+ Uniform<GLfloat> u_scale_a = {"u_scale_a", *this};
+ Uniform<GLfloat> u_scale_b = {"u_scale_b", *this};
+ Uniform<GLfloat> u_tile_units_to_pixels = {"u_tile_units_to_pixels", *this};
+ Uniform<GLint> u_image = {"u_image", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_size_a = {"u_pattern_size_a", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_size_b = {"u_pattern_size_b", *this};
+ Uniform<std::array<GLfloat, 2>> u_pixel_coord_upper = {"u_pixel_coord_upper", *this};
+ Uniform<std::array<GLfloat, 2>> u_pixel_coord_lower = {"u_pixel_coord_lower", *this};
+ Uniform<std::array<GLfloat, 2>> u_world = {"u_world", *this};
};
} // namespace mbgl
diff --git a/src/mbgl/shader/pattern_shader.cpp b/src/mbgl/shader/pattern_shader.cpp
index 64a72d1451..5caa892feb 100644
--- a/src/mbgl/shader/pattern_shader.cpp
+++ b/src/mbgl/shader/pattern_shader.cpp
@@ -3,19 +3,18 @@
#include <mbgl/shader/pattern.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-PatternShader::PatternShader(gl::ObjectStore& store)
- : Shader(
- "pattern",
- shaders::pattern::vertex, shaders::pattern::fragment,
- store
- ) {
+PatternShader::PatternShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::pattern::name,
+ shaders::pattern::vertex,
+ shaders::pattern::fragment,
+ store, defines) {
}
void PatternShader::bind(GLbyte *offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/pattern_shader.hpp b/src/mbgl/shader/pattern_shader.hpp
index ae515c1948..ae356fdfb7 100644
--- a/src/mbgl/shader/pattern_shader.hpp
+++ b/src/mbgl/shader/pattern_shader.hpp
@@ -7,22 +7,25 @@ namespace mbgl {
class PatternShader : public Shader {
public:
- PatternShader(gl::ObjectStore&);
+ PatternShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
- UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<std::array<GLfloat, 2>> u_pattern_tl_a = {"u_pattern_tl_a", *this};
- Uniform<std::array<GLfloat, 2>> u_pattern_br_a = {"u_pattern_br_a", *this};
- Uniform<std::array<GLfloat, 2>> u_pattern_tl_b = {"u_pattern_tl_b", *this};
- Uniform<std::array<GLfloat, 2>> u_pattern_br_b = {"u_pattern_br_b", *this};
- Uniform<GLfloat> u_opacity = {"u_opacity", *this};
- Uniform<GLfloat> u_mix = {"u_mix", *this};
- Uniform<GLint> u_image = {"u_image", *this};
- Uniform<std::array<GLfloat, 2>> u_patternscale_a = {"u_patternscale_a", *this};
- Uniform<std::array<GLfloat, 2>> u_patternscale_b = {"u_patternscale_b", *this};
- Uniform<std::array<GLfloat, 2>> u_offset_a = {"u_offset_a", *this};
- Uniform<std::array<GLfloat, 2>> u_offset_b = {"u_offset_b", *this};
+ UniformMatrix<4> u_matrix = {"u_matrix", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_tl_a = {"u_pattern_tl_a", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_br_a = {"u_pattern_br_a", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_tl_b = {"u_pattern_tl_b", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_br_b = {"u_pattern_br_b", *this};
+ Uniform<GLfloat> u_opacity = {"u_opacity", *this};
+ Uniform<GLfloat> u_mix = {"u_mix", *this};
+ Uniform<GLfloat> u_scale_a = {"u_scale_a", *this};
+ Uniform<GLfloat> u_scale_b = {"u_scale_b", *this};
+ Uniform<GLfloat> u_tile_units_to_pixels = {"u_tile_units_to_pixels", *this};
+ Uniform<GLint> u_image = {"u_image", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_size_a = {"u_pattern_size_a", *this};
+ Uniform<std::array<GLfloat, 2>> u_pattern_size_b = {"u_pattern_size_b", *this};
+ Uniform<std::array<GLfloat, 2>> u_pixel_coord_upper = {"u_pixel_coord_upper", *this};
+ Uniform<std::array<GLfloat, 2>> u_pixel_coord_lower = {"u_pixel_coord_lower", *this};
};
} // namespace mbgl
diff --git a/src/mbgl/shader/plain_shader.cpp b/src/mbgl/shader/plain_shader.cpp
index c030a2d7b3..1309c4f14a 100644
--- a/src/mbgl/shader/plain_shader.cpp
+++ b/src/mbgl/shader/plain_shader.cpp
@@ -3,15 +3,18 @@
#include <mbgl/shader/fill.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-PlainShader::PlainShader(gl::ObjectStore& store)
- : Shader("fill", shaders::fill::vertex, shaders::fill::fragment, store) {
+PlainShader::PlainShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::fill::name,
+ shaders::fill::vertex,
+ shaders::fill::fragment,
+ store, defines) {
}
void PlainShader::bind(GLbyte* offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/plain_shader.hpp b/src/mbgl/shader/plain_shader.hpp
index 7edf4b4b19..a126fa30a9 100644
--- a/src/mbgl/shader/plain_shader.hpp
+++ b/src/mbgl/shader/plain_shader.hpp
@@ -2,17 +2,18 @@
#include <mbgl/shader/shader.hpp>
#include <mbgl/shader/uniform.hpp>
+#include <mbgl/util/color.hpp>
namespace mbgl {
class PlainShader : public Shader {
public:
- PlainShader(gl::ObjectStore&);
+ PlainShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<std::array<GLfloat, 4>> u_color = {"u_color", *this};
+ Uniform<Color> u_color = {"u_color", *this};
Uniform<GLfloat> u_opacity = {"u_opacity", *this};
};
diff --git a/src/mbgl/shader/raster_shader.cpp b/src/mbgl/shader/raster_shader.cpp
index e5e290bedd..76723b06e9 100644
--- a/src/mbgl/shader/raster_shader.cpp
+++ b/src/mbgl/shader/raster_shader.cpp
@@ -3,15 +3,23 @@
#include <mbgl/shader/raster.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-RasterShader::RasterShader(gl::ObjectStore& store)
- : Shader("raster", shaders::raster::vertex, shaders::raster::fragment, store) {
+RasterShader::RasterShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::raster::name,
+ shaders::raster::vertex,
+ shaders::raster::fragment,
+ store, defines) {
}
void RasterShader::bind(GLbyte* offset) {
+ const GLint stride = 8;
+
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
- MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset));
+ MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, stride, offset));
+
+ MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_texture_pos));
+ MBGL_CHECK_ERROR(glVertexAttribPointer(a_texture_pos, 2, GL_SHORT, false, stride, offset + 4));
}
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/raster_shader.hpp b/src/mbgl/shader/raster_shader.hpp
index ee53385a0b..4b3ba9168b 100644
--- a/src/mbgl/shader/raster_shader.hpp
+++ b/src/mbgl/shader/raster_shader.hpp
@@ -7,19 +7,23 @@ namespace mbgl {
class RasterShader : public Shader {
public:
- RasterShader(gl::ObjectStore&);
+ RasterShader(gl::ObjectStore&, Defines defines = None);
void bind(GLbyte *offset) final;
UniformMatrix<4> u_matrix = {"u_matrix", *this};
- Uniform<GLint> u_image = {"u_image", *this};
- Uniform<GLfloat> u_opacity = {"u_opacity", *this};
- Uniform<GLfloat> u_buffer = {"u_buffer", *this};
+ Uniform<GLint> u_image0 = {"u_image0", *this};
+ Uniform<GLint> u_image1 = {"u_image1", *this};
+ Uniform<GLfloat> u_opacity0 = {"u_opacity0", *this};
+ Uniform<GLfloat> u_opacity1 = {"u_opacity1", *this};
+ Uniform<GLfloat> u_buffer_scale = {"u_buffer_scale", *this};
Uniform<GLfloat> u_brightness_low = {"u_brightness_low", *this};
Uniform<GLfloat> u_brightness_high = {"u_brightness_high", *this};
Uniform<GLfloat> u_saturation_factor = {"u_saturation_factor", *this};
Uniform<GLfloat> u_contrast_factor = {"u_contrast_factor", *this};
Uniform<std::array<GLfloat, 3>> u_spin_weights = {"u_spin_weights", *this};
+ Uniform<std::array<GLfloat, 2>> u_tl_parent = {"u_tl_parent", *this};
+ Uniform<GLfloat> u_scale_parent = {"u_scale_parent", *this};
};
} // namespace mbgl
diff --git a/src/mbgl/shader/sdf_shader.cpp b/src/mbgl/shader/sdf_shader.cpp
index 76ea1e3b4d..24b6f77d68 100644
--- a/src/mbgl/shader/sdf_shader.cpp
+++ b/src/mbgl/shader/sdf_shader.cpp
@@ -3,18 +3,16 @@
#include <mbgl/shader/sdf.fragment.hpp>
#include <mbgl/gl/gl.hpp>
-#include <cstdio>
+namespace mbgl {
-using namespace mbgl;
-
-SDFShader::SDFShader(gl::ObjectStore& store)
- : Shader("sdf", shaders::sdf::vertex, shaders::sdf::fragment, store)
- , a_offset(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_offset")))
- , a_data1(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_data1")))
- , a_data2(MBGL_CHECK_ERROR(glGetAttribLocation(getID(), "a_data2"))) {
+SDFShader::SDFShader(gl::ObjectStore& store, Defines defines)
+ : Shader(shaders::sdf::name,
+ shaders::sdf::vertex,
+ shaders::sdf::fragment,
+ store, defines) {
}
-void SDFGlyphShader::bind(GLbyte* offset) {
+void SDFShader::bind(GLbyte* offset) {
const int stride = 16;
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
@@ -30,18 +28,4 @@ void SDFGlyphShader::bind(GLbyte* offset) {
MBGL_CHECK_ERROR(glVertexAttribPointer(a_data2, 4, GL_UNSIGNED_BYTE, false, stride, offset + 12));
}
-void SDFIconShader::bind(GLbyte* offset) {
- const int stride = 16;
-
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
- MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, stride, offset + 0));
-
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_offset));
- MBGL_CHECK_ERROR(glVertexAttribPointer(a_offset, 2, GL_SHORT, false, stride, offset + 4));
-
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_data1));
- MBGL_CHECK_ERROR(glVertexAttribPointer(a_data1, 4, GL_UNSIGNED_BYTE, false, stride, offset + 8));
-
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_data2));
- MBGL_CHECK_ERROR(glVertexAttribPointer(a_data2, 4, GL_UNSIGNED_BYTE, false, stride, offset + 12));
-}
+} // namespace mbgl
diff --git a/src/mbgl/shader/sdf_shader.hpp b/src/mbgl/shader/sdf_shader.hpp
index f067c30aeb..f2f79f1cc9 100644
--- a/src/mbgl/shader/sdf_shader.hpp
+++ b/src/mbgl/shader/sdf_shader.hpp
@@ -2,40 +2,30 @@
#include <mbgl/shader/shader.hpp>
#include <mbgl/shader/uniform.hpp>
+#include <mbgl/util/color.hpp>
namespace mbgl {
class SDFShader : public Shader {
public:
- SDFShader(gl::ObjectStore&);
+ SDFShader(gl::ObjectStore&, Defines defines = None);
UniformMatrix<4> u_matrix = {"u_matrix", *this};
Uniform<std::array<GLfloat, 2>> u_extrude_scale = {"u_extrude_scale", *this};
- Uniform<std::array<GLfloat, 4>> u_color = {"u_color", *this};
+ Uniform<Color> u_color = {"u_color", *this};
Uniform<GLfloat> u_opacity = {"u_opacity", *this};
Uniform<std::array<GLfloat, 2>> u_texsize = {"u_texsize", *this};
Uniform<GLfloat> u_buffer = {"u_buffer", *this};
Uniform<GLfloat> u_gamma = {"u_gamma", *this};
Uniform<GLfloat> u_zoom = {"u_zoom", *this};
- Uniform<GLint> u_skewed = {"u_skewed", *this};
+ Uniform<GLfloat> u_pitch = {"u_pitch", *this};
+ Uniform<GLfloat> u_bearing = {"u_bearing", *this};
+ Uniform<GLfloat> u_aspect_ratio = {"u_aspect_ratio", *this};
+ Uniform<GLint> u_rotate_with_map = {"u_rotate_with_map",*this};
+ Uniform<GLint> u_pitch_with_map = {"u_pitch_with_map",*this};
Uniform<GLint> u_texture = {"u_texture", *this};
Uniform<GLint> u_fadetexture = {"u_fadetexture", *this};
-protected:
- GLint a_offset = -1;
- GLint a_data1 = -1;
- GLint a_data2 = -1;
-};
-
-class SDFGlyphShader : public SDFShader {
-public:
- SDFGlyphShader(gl::ObjectStore& store) : SDFShader(store) {}
- void bind(GLbyte *offset) final;
-};
-
-class SDFIconShader : public SDFShader {
-public:
- SDFIconShader(gl::ObjectStore& store) : SDFShader(store) {}
void bind(GLbyte *offset) final;
};
diff --git a/src/mbgl/shader/shader.cpp b/src/mbgl/shader/shader.cpp
index 23ea043720..ae36d21740 100644
--- a/src/mbgl/shader/shader.cpp
+++ b/src/mbgl/shader/shader.cpp
@@ -8,12 +8,14 @@
#include <cstring>
#include <cassert>
#include <iostream>
+#include <string>
#include <fstream>
#include <cstdio>
+#include <cassert>
namespace mbgl {
-Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSource, gl::ObjectStore& store)
+Shader::Shader(const char* name_, const char* vertexSource, const char* fragmentSource, gl::ObjectStore& store, Defines defines)
: name(name_)
, program(store.createProgram())
, vertexShader(store.createShader(GL_VERTEX_SHADER))
@@ -21,13 +23,19 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
{
util::stopwatch stopwatch("shader compilation", Event::Shader);
- if (!compileShader(vertexShader, &vertSource)) {
- Log::Error(Event::Shader, "Vertex shader %s failed to compile: %s", name, vertSource);
+ if (!compileShader(vertexShader, vertexSource)) {
+ Log::Error(Event::Shader, "Vertex shader %s failed to compile: %s", name, vertexSource);
throw util::ShaderException(std::string { "Vertex shader " } + name + " failed to compile");
}
- if (!compileShader(fragmentShader, &fragSource)) {
- Log::Error(Event::Shader, "Fragment shader %s failed to compile: %s", name, fragSource);
+ std::string fragment(fragmentSource);
+ if (defines & Defines::Overdraw) {
+ assert(fragment.find("#ifdef OVERDRAW_INSPECTOR") != std::string::npos);
+ fragment.replace(fragment.find_first_of('\n'), 1, "\n#define OVERDRAW_INSPECTOR\n");
+ }
+
+ if (!compileShader(fragmentShader, fragment.c_str())) {
+ Log::Error(Event::Shader, "Fragment shader %s failed to compile: %s", name, fragmentSource);
throw util::ShaderException(std::string { "Fragment shader " } + name + " failed to compile");
}
@@ -35,32 +43,37 @@ Shader::Shader(const char *name_, const GLchar *vertSource, const GLchar *fragSo
MBGL_CHECK_ERROR(glAttachShader(program.get(), vertexShader.get()));
MBGL_CHECK_ERROR(glAttachShader(program.get(), fragmentShader.get()));
- {
- // Link program
- GLint status;
- MBGL_CHECK_ERROR(glLinkProgram(program.get()));
+ // Bind attribute variables
+ MBGL_CHECK_ERROR(glBindAttribLocation(program.get(), a_pos, "a_pos"));
+ MBGL_CHECK_ERROR(glBindAttribLocation(program.get(), a_extrude, "a_extrude"));
+ MBGL_CHECK_ERROR(glBindAttribLocation(program.get(), a_offset, "a_offset"));
+ MBGL_CHECK_ERROR(glBindAttribLocation(program.get(), a_data, "a_data"));
+ MBGL_CHECK_ERROR(glBindAttribLocation(program.get(), a_data1, "a_data1"));
+ MBGL_CHECK_ERROR(glBindAttribLocation(program.get(), a_data2, "a_data2"));
+ MBGL_CHECK_ERROR(glBindAttribLocation(program.get(), a_texture_pos, "a_texture_pos"));
- MBGL_CHECK_ERROR(glGetProgramiv(program.get(), GL_LINK_STATUS, &status));
- if (status == 0) {
- GLint logLength;
- MBGL_CHECK_ERROR(glGetProgramiv(program.get(), GL_INFO_LOG_LENGTH, &logLength));
- const auto log = std::make_unique<GLchar[]>(logLength);
- if (logLength > 0) {
- MBGL_CHECK_ERROR(glGetProgramInfoLog(program.get(), logLength, &logLength, log.get()));
- Log::Error(Event::Shader, "Program failed to link: %s", log.get());
- }
- throw util::ShaderException(std::string { "Program " } + name + " failed to link: " + log.get());
+ // Link program
+ GLint status;
+ MBGL_CHECK_ERROR(glLinkProgram(program.get()));
+
+ MBGL_CHECK_ERROR(glGetProgramiv(program.get(), GL_LINK_STATUS, &status));
+ if (status == 0) {
+ GLint logLength;
+ MBGL_CHECK_ERROR(glGetProgramiv(program.get(), GL_INFO_LOG_LENGTH, &logLength));
+ const auto log = std::make_unique<GLchar[]>(logLength);
+ if (logLength > 0) {
+ MBGL_CHECK_ERROR(glGetProgramInfoLog(program.get(), logLength, &logLength, log.get()));
+ Log::Error(Event::Shader, "Program failed to link: %s", log.get());
}
+ throw util::ShaderException(std::string { "Program " } + name + " failed to link: " + log.get());
}
-
- a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program.get(), "a_pos"));
}
-bool Shader::compileShader(gl::UniqueShader& shader, const GLchar *source[]) {
+bool Shader::compileShader(gl::UniqueShader& shader, const GLchar *source) {
GLint status = 0;
- const GLsizei lengths = static_cast<GLsizei>(std::strlen(*source));
- MBGL_CHECK_ERROR(glShaderSource(shader.get(), 1, source, &lengths));
+ const GLsizei lengths = static_cast<GLsizei>(std::strlen(source));
+ MBGL_CHECK_ERROR(glShaderSource(shader.get(), 1, &source, &lengths));
MBGL_CHECK_ERROR(glCompileShader(shader.get()));
diff --git a/src/mbgl/shader/shader.hpp b/src/mbgl/shader/shader.hpp
index 1cb2a5601f..f7da0c91ab 100644
--- a/src/mbgl/shader/shader.hpp
+++ b/src/mbgl/shader/shader.hpp
@@ -3,15 +3,14 @@
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/object_store.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/optional.hpp>
namespace mbgl {
class Shader : private util::noncopyable {
public:
- Shader(const GLchar *name, const GLchar *vertex, const GLchar *fragment, gl::ObjectStore&);
-
~Shader();
- const GLchar *name;
+ const char* name;
GLuint getID() const {
return program.get();
@@ -19,11 +18,28 @@ public:
virtual void bind(GLbyte *offset) = 0;
+ enum Defines : bool {
+ None = false,
+ Overdraw = true,
+ };
+
protected:
- GLint a_pos = -1;
+ Shader(const char* name_,
+ const char* vertex,
+ const char* fragment,
+ gl::ObjectStore&,
+ Defines defines = Defines::None);
+
+ static constexpr GLint a_pos = 0;
+ static constexpr GLint a_extrude = 1;
+ static constexpr GLint a_offset = 2;
+ static constexpr GLint a_data = 3;
+ static constexpr GLint a_data1 = 4;
+ static constexpr GLint a_data2 = 5;
+ static constexpr GLint a_texture_pos = 6;
private:
- bool compileShader(gl::UniqueShader&, const GLchar *source[]);
+ bool compileShader(gl::UniqueShader&, const GLchar *source);
gl::UniqueProgram program;
gl::UniqueShader vertexShader;
diff --git a/src/mbgl/shader/shaders.hpp b/src/mbgl/shader/shaders.hpp
new file mode 100644
index 0000000000..088e351d37
--- /dev/null
+++ b/src/mbgl/shader/shaders.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <mbgl/gl/gl.hpp>
+#include <mbgl/gl/object_store.hpp>
+
+#include <mbgl/shader/pattern_shader.hpp>
+#include <mbgl/shader/plain_shader.hpp>
+#include <mbgl/shader/outline_shader.hpp>
+#include <mbgl/shader/outlinepattern_shader.hpp>
+#include <mbgl/shader/line_shader.hpp>
+#include <mbgl/shader/linesdf_shader.hpp>
+#include <mbgl/shader/linepattern_shader.hpp>
+#include <mbgl/shader/icon_shader.hpp>
+#include <mbgl/shader/raster_shader.hpp>
+#include <mbgl/shader/sdf_shader.hpp>
+#include <mbgl/shader/collision_box_shader.hpp>
+#include <mbgl/shader/circle_shader.hpp>
+
+namespace mbgl {
+
+class Shaders {
+public:
+ Shaders(gl::ObjectStore& store, Shader::Defines defines = Shader::None)
+ : plain(store, defines),
+ outline(store, defines),
+ outlinePattern(store, defines),
+ line(store, defines),
+ linesdf(store, defines),
+ linepattern(store, defines),
+ pattern(store, defines),
+ icon(store, defines),
+ raster(store, defines),
+ sdfGlyph(store, defines),
+ sdfIcon(store, defines),
+ collisionBox(store),
+ circle(store, defines)
+ {}
+
+ PlainShader plain;
+ OutlineShader outline;
+ OutlinePatternShader outlinePattern;
+ LineShader line;
+ LineSDFShader linesdf;
+ LinepatternShader linepattern;
+ PatternShader pattern;
+ IconShader icon;
+ RasterShader raster;
+ SDFShader sdfGlyph;
+ SDFShader sdfIcon;
+ CollisionBoxShader collisionBox;
+ CircleShader circle;
+
+ VertexArrayObject coveringPlainArray;
+ VertexArrayObject coveringRasterArray;
+ VertexArrayObject backgroundPatternArray;
+ VertexArrayObject backgroundArray;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/shader/uniform.cpp b/src/mbgl/shader/uniform.cpp
index d43dd20016..4c7646119a 100644
--- a/src/mbgl/shader/uniform.cpp
+++ b/src/mbgl/shader/uniform.cpp
@@ -1,4 +1,5 @@
#include <mbgl/shader/uniform.hpp>
+#include <mbgl/util/color.hpp>
namespace mbgl {
@@ -28,6 +29,12 @@ void Uniform<std::array<GLfloat, 4>>::bind(const std::array<GLfloat, 4>& t) {
}
template <>
+void Uniform<Color>::bind(const Color& t) {
+ std::array<GLfloat, 4> a = {{ t.r, t.g, t.b, t.a }};
+ MBGL_CHECK_ERROR(glUniform4fv(location, 1, a.data()));
+}
+
+template <>
void UniformMatrix<2>::bind(const std::array<GLfloat, 4>& t) {
MBGL_CHECK_ERROR(glUniformMatrix2fv(location, 1, GL_FALSE, t.data()));
}
diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp
index 5d7d01deb3..05b713f454 100644
--- a/src/mbgl/sprite/sprite_atlas.cpp
+++ b/src/mbgl/sprite/sprite_atlas.cpp
@@ -1,6 +1,7 @@
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/sprite/sprite_store.hpp>
#include <mbgl/gl/gl.hpp>
+#include <mbgl/gl/gl_config.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/math.hpp>
@@ -11,7 +12,7 @@
#include <cmath>
#include <algorithm>
-using namespace mbgl;
+namespace mbgl {
SpriteAtlas::SpriteAtlas(dimension width_, dimension height_, float pixelRatio_, SpriteStore& store_)
: width(width_),
@@ -142,9 +143,9 @@ void SpriteAtlas::copy(const Holder& holder, const bool wrap) {
dirty = true;
}
-void SpriteAtlas::upload(gl::ObjectStore& objectStore) {
+void SpriteAtlas::upload(gl::ObjectStore& objectStore, gl::Config& config, uint32_t unit) {
if (dirty) {
- bind(false, objectStore);
+ bind(false, objectStore, config, unit);
}
}
@@ -179,14 +180,15 @@ void SpriteAtlas::updateDirty() {
}
}
-void SpriteAtlas::bind(bool linear, gl::ObjectStore& objectStore) {
+void SpriteAtlas::bind(bool linear, gl::ObjectStore& objectStore, gl::Config& config, uint32_t unit) {
if (!data) {
return; // Empty atlas
}
if (!texture) {
texture = objectStore.createTexture();
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
#ifndef GL_ES_VERSION_2_0
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
#endif
@@ -195,12 +197,14 @@ void SpriteAtlas::bind(bool linear, gl::ObjectStore& objectStore) {
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
fullUploadRequired = true;
- } else {
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
+ } else if (config.texture[unit] != *texture) {
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
}
GLuint filter_val = linear ? GL_LINEAR : GL_NEAREST;
if (filter_val != filter) {
+ config.activeTexture = unit;
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_val));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_val));
filter = filter_val;
@@ -209,6 +213,7 @@ void SpriteAtlas::bind(bool linear, gl::ObjectStore& objectStore) {
if (dirty) {
std::lock_guard<std::recursive_mutex> lock(mtx);
+ config.activeTexture = unit;
if (fullUploadRequired) {
MBGL_CHECK_ERROR(glTexImage2D(
GL_TEXTURE_2D, // GLenum target
@@ -243,14 +248,15 @@ void SpriteAtlas::bind(bool linear, gl::ObjectStore& objectStore) {
// pixelWidth, pixelHeight, pixelWidth, pixelHeight);
#endif
}
-};
+}
SpriteAtlas::~SpriteAtlas() = default;
-SpriteAtlas::Holder::Holder(const std::shared_ptr<const SpriteImage>& spriteImage_,
- const Rect<dimension>& pos_)
- : spriteImage(spriteImage_), pos(pos_) {
+SpriteAtlas::Holder::Holder(std::shared_ptr<const SpriteImage> spriteImage_, Rect<dimension> pos_)
+ : spriteImage(std::move(spriteImage_)), pos(std::move(pos_)) {
}
SpriteAtlas::Holder::Holder(Holder&& h) : spriteImage(std::move(h.spriteImage)), pos(h.pos) {
}
+
+} // namespace mbgl
diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp
index 3d91f5c9d8..990ba59e03 100644
--- a/src/mbgl/sprite/sprite_atlas.hpp
+++ b/src/mbgl/sprite/sprite_atlas.hpp
@@ -3,11 +3,11 @@
#include <mbgl/geometry/binpack.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/object_store.hpp>
-#include <mbgl/util/atomic.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/optional.hpp>
+#include <atomic>
#include <string>
#include <map>
#include <mutex>
@@ -16,18 +16,18 @@
namespace mbgl {
+namespace gl {
+class Config;
+} // namespace gl
+
class SpriteStore;
class SpriteImage;
class SpritePosition;
struct SpriteAtlasPosition {
- inline SpriteAtlasPosition(const std::array<float, 2> size_ = {{0, 0}},
- const std::array<float, 2> tl_ = {{0, 0}},
- const std::array<float, 2> br_ = {{0, 0}})
- : size(size_), tl(tl_), br(br_) {}
- std::array<float, 2> size;
- std::array<float, 2> tl;
- std::array<float, 2> br;
+ std::array<float, 2> size = {{ 0, 0 }};
+ std::array<float, 2> tl = {{ 0, 0 }};
+ std::array<float, 2> br = {{ 0, 0 }};
};
struct SpriteAtlasElement {
@@ -51,23 +51,23 @@ public:
optional<SpriteAtlasPosition> getPosition(const std::string& name, bool repeating = false);
// Binds the atlas texture to the GPU, and uploads data if it is out of date.
- void bind(bool linear, gl::ObjectStore&);
+ void bind(bool linear, gl::ObjectStore&, gl::Config&, uint32_t unit);
// Updates sprites in the atlas texture that may have changed in the source SpriteStore object.
void updateDirty();
// Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
// the texture is only bound when the data is out of date (=dirty).
- void upload(gl::ObjectStore&);
+ void upload(gl::ObjectStore&, gl::Config&, uint32_t unit);
- inline dimension getWidth() const { return width; }
- inline dimension getHeight() const { return height; }
- inline dimension getTextureWidth() const { return pixelWidth; }
- inline dimension getTextureHeight() const { return pixelHeight; }
- inline float getPixelRatio() const { return pixelRatio; }
+ dimension getWidth() const { return width; }
+ dimension getHeight() const { return height; }
+ dimension getTextureWidth() const { return pixelWidth; }
+ dimension getTextureHeight() const { return pixelHeight; }
+ float getPixelRatio() const { return pixelRatio; }
// Only for use in tests.
- inline const uint32_t* getData() const { return data.get(); }
+ const uint32_t* getData() const { return data.get(); }
private:
const GLsizei width, height;
@@ -75,8 +75,8 @@ private:
const float pixelRatio;
struct Holder : private util::noncopyable {
- inline Holder(const std::shared_ptr<const SpriteImage>&, const Rect<dimension>&);
- inline Holder(Holder&&);
+ Holder(std::shared_ptr<const SpriteImage>, Rect<dimension>);
+ Holder(Holder&&);
std::shared_ptr<const SpriteImage> spriteImage;
const Rect<dimension> pos;
};
@@ -92,7 +92,7 @@ private:
std::map<Key, Holder> images;
std::set<std::string> uninitialized;
std::unique_ptr<uint32_t[]> data;
- util::Atomic<bool> dirty;
+ std::atomic<bool> dirty;
bool fullUploadRequired = true;
mbgl::optional<gl::UniqueTexture> texture;
uint32_t filter = 0;
diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp
index 9fbca51112..190ac66e14 100644
--- a/src/mbgl/sprite/sprite_parser.cpp
+++ b/src/mbgl/sprite/sprite_parser.cpp
@@ -46,7 +46,7 @@ SpriteImagePtr createSpriteImage(const PremultipliedImage& image,
namespace {
-inline uint16_t getUInt16(const JSValue& value, const char* name, const uint16_t def = 0) {
+uint16_t getUInt16(const JSValue& value, const char* name, const uint16_t def = 0) {
if (value.HasMember(name)) {
auto& v = value[name];
if (v.IsUint() && v.GetUint() <= std::numeric_limits<uint16_t>::max()) {
@@ -60,7 +60,7 @@ inline uint16_t getUInt16(const JSValue& value, const char* name, const uint16_t
return def;
}
-inline double getDouble(const JSValue& value, const char* name, const double def = 0) {
+double getDouble(const JSValue& value, const char* name, const double def = 0) {
if (value.HasMember(name)) {
auto& v = value[name];
if (v.IsNumber()) {
@@ -73,7 +73,7 @@ inline double getDouble(const JSValue& value, const char* name, const double def
return def;
}
-inline bool getBoolean(const JSValue& value, const char* name, const bool def = false) {
+bool getBoolean(const JSValue& value, const char* name, const bool def = false) {
if (value.HasMember(name)) {
auto& v = value[name];
if (v.IsBool()) {
diff --git a/src/mbgl/storage/network_status.cpp b/src/mbgl/storage/network_status.cpp
index bc538a5f38..1ef5619bd6 100644
--- a/src/mbgl/storage/network_status.cpp
+++ b/src/mbgl/storage/network_status.cpp
@@ -9,7 +9,7 @@
namespace mbgl {
-util::Atomic<bool> NetworkStatus::online(true);
+std::atomic<bool> NetworkStatus::online(true);
std::mutex NetworkStatus::mtx;
std::set<util::AsyncTask *> NetworkStatus::observers;
diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp
index 3f5b4a3f71..bb587dcc33 100644
--- a/src/mbgl/storage/resource.cpp
+++ b/src/mbgl/storage/resource.cpp
@@ -1,10 +1,44 @@
+#include <mapbox/geometry/box.hpp>
#include <mbgl/storage/resource.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/token.hpp>
#include <mbgl/util/url.hpp>
+#include <cmath>
+
namespace mbgl {
+static std::string getQuadKey(int32_t x, int32_t y, int8_t z) {
+ std::string quadKey;
+ quadKey.reserve(z);
+ int32_t mask;
+ for (int8_t i = z; i > 0; i--) {
+ mask = 1 << (i - 1);
+ quadKey += '0' + ((x & mask ? 1 : 0) + (y & mask ? 2 : 0));
+ }
+ return quadKey;
+}
+
+static mapbox::geometry::point<double> getMercCoord(int32_t x, int32_t y, int8_t z) {
+ double resolution = (util::M2PI * util::EARTH_RADIUS_M / 256) / std::pow(2.0f, z);
+ return {
+ x * resolution - util::M2PI * util::EARTH_RADIUS_M / 2,
+ y * resolution - util::M2PI * util::EARTH_RADIUS_M / 2,
+ };
+}
+
+static std::string getTileBBox(int32_t x, int32_t y, int8_t z) {
+ // Alter the y for the Google/OSM tile scheme.
+ y = std::pow(2.0f, z) - y - 1;
+
+ auto min = getMercCoord(x * 256, y * 256, z);
+ auto max = getMercCoord((x + 1) * 256, (y + 1) * 256, z);
+
+ return (util::toString(min.x) + "," + util::toString(min.y) + "," +
+ util::toString(max.x) + "," + util::toString(max.y));
+}
+
Resource Resource::style(const std::string& url) {
return Resource {
Resource::Kind::Style,
@@ -64,6 +98,10 @@ Resource Resource::tile(const std::string& urlTemplate,
return util::toString(x);
} else if (token == "y") {
return util::toString(y);
+ } else if (token == "quadkey") {
+ return getQuadKey(x, y, z);
+ } else if (token == "bbox-epsg-3857") {
+ return getTileBBox(x, y, z);
} else if (token == "prefix") {
std::string prefix{ 2 };
prefix[0] = "0123456789abcdef"[x % 16];
@@ -86,4 +124,4 @@ Resource Resource::tile(const std::string& urlTemplate,
};
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp
index 644d73d286..6809220166 100644
--- a/src/mbgl/storage/response.cpp
+++ b/src/mbgl/storage/response.cpp
@@ -21,14 +21,14 @@ Response& Response::operator=(const Response& res) {
return *this;
}
-Response::Error::Error(Reason reason_, const std::string& message_)
- : reason(reason_), message(message_) {
+Response::Error::Error(Reason reason_, std::string message_)
+ : reason(reason_), message(std::move(message_)) {
}
std::ostream& operator<<(std::ostream& os, Response::Error::Reason r) {
switch (r) {
case Response::Error::Reason::Success:
- return os << "Response::Error::Reason::NotFound";
+ return os << "Response::Error::Reason::Success";
case Response::Error::Reason::NotFound:
return os << "Response::Error::Reason::NotFound";
case Response::Error::Reason::Server:
diff --git a/src/mbgl/style/bucket_parameters.cpp b/src/mbgl/style/bucket_parameters.cpp
index f3367d57e1..64f53babcd 100644
--- a/src/mbgl/style/bucket_parameters.cpp
+++ b/src/mbgl/style/bucket_parameters.cpp
@@ -1,6 +1,6 @@
#include <mbgl/style/bucket_parameters.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
namespace mbgl {
namespace style {
@@ -10,11 +10,8 @@ void BucketParameters::eachFilteredFeature(const Filter& filter,
auto name = layer.getName();
for (std::size_t i = 0; !cancelled() && i < layer.featureCount(); i++) {
auto feature = layer.getFeature(i);
-
- FilterEvaluator evaluator(*feature);
- if (!Filter::visit(filter, evaluator))
+ if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
continue;
-
function(*feature, i, name);
}
}
diff --git a/src/mbgl/style/bucket_parameters.hpp b/src/mbgl/style/bucket_parameters.hpp
index 8c1da72f25..3b42e7c41b 100644
--- a/src/mbgl/style/bucket_parameters.hpp
+++ b/src/mbgl/style/bucket_parameters.hpp
@@ -3,8 +3,8 @@
#include <mbgl/map/mode.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/style/filter.hpp>
-#include <mbgl/util/atomic.hpp>
+#include <atomic>
#include <functional>
namespace mbgl {
@@ -24,7 +24,7 @@ class BucketParameters {
public:
BucketParameters(const OverscaledTileID& tileID_,
const GeometryTileLayer& layer_,
- const util::Atomic<bool>& obsolete_,
+ const std::atomic<bool>& obsolete_,
uintptr_t tileUID_,
bool& partialParse_,
SpriteStore& spriteStore_,
@@ -51,7 +51,7 @@ public:
const OverscaledTileID& tileID;
const GeometryTileLayer& layer;
- const util::Atomic<bool>& obsolete;
+ const std::atomic<bool>& obsolete;
uintptr_t tileUID;
bool& partialParse;
SpriteStore& spriteStore;
diff --git a/src/mbgl/style/calculation_parameters.hpp b/src/mbgl/style/calculation_parameters.hpp
index 2afd7c4b34..e1f059c524 100644
--- a/src/mbgl/style/calculation_parameters.hpp
+++ b/src/mbgl/style/calculation_parameters.hpp
@@ -12,13 +12,13 @@ public:
: z(z_) {}
CalculationParameters(float z_,
- const TimePoint& now_,
- const ZoomHistory& zoomHistory_,
- const Duration& defaultFadeDuration_)
+ TimePoint now_,
+ ZoomHistory zoomHistory_,
+ Duration defaultFadeDuration_)
: z(z_),
- now(now_),
- zoomHistory(zoomHistory_),
- defaultFadeDuration(defaultFadeDuration_) {}
+ now(std::move(now_)),
+ zoomHistory(std::move(zoomHistory_)),
+ defaultFadeDuration(std::move(defaultFadeDuration_)) {}
float z;
TimePoint now;
diff --git a/src/mbgl/style/cascade_parameters.hpp b/src/mbgl/style/cascade_parameters.hpp
index 4ad6da2ce3..e0333741dd 100644
--- a/src/mbgl/style/cascade_parameters.hpp
+++ b/src/mbgl/style/cascade_parameters.hpp
@@ -9,8 +9,6 @@
namespace mbgl {
namespace style {
-class TransitionOptions;
-
class CascadeParameters {
public:
std::vector<ClassID> classes;
diff --git a/src/mbgl/style/layer.cpp b/src/mbgl/style/layer.cpp
index 342699a2c9..6eff64ae09 100644
--- a/src/mbgl/style/layer.cpp
+++ b/src/mbgl/style/layer.cpp
@@ -38,13 +38,5 @@ void Layer::setMaxZoom(float maxZoom) const {
baseImpl->maxZoom = maxZoom;
}
-std::unique_ptr<Layer> Layer::copy(const std::string& id,
- const std::string& ref) const {
- std::unique_ptr<Layer> result = baseImpl->clone();
- result->baseImpl->id = id;
- result->baseImpl->ref = ref;
- return result;
-}
-
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/layer_impl.cpp b/src/mbgl/style/layer_impl.cpp
index 56f66d1789..f50bf87339 100644
--- a/src/mbgl/style/layer_impl.cpp
+++ b/src/mbgl/style/layer_impl.cpp
@@ -3,6 +3,16 @@
namespace mbgl {
namespace style {
+std::unique_ptr<Layer> Layer::Impl::copy(const std::string& id_,
+ const std::string& ref_,
+ const std::string& source_) const {
+ std::unique_ptr<Layer> result = clone();
+ result->baseImpl->id = id_;
+ result->baseImpl->ref = ref_;
+ result->baseImpl->source = source_;
+ return result;
+}
+
const std::string& Layer::Impl::bucketName() const {
return ref.empty() ? id : ref;
}
@@ -12,11 +22,11 @@ bool Layer::Impl::hasRenderPass(RenderPass pass) const {
}
bool Layer::Impl::needsRendering(float zoom) const {
- return passes != RenderPass::None
+ return passes != RenderPass::None
&& visibility != VisibilityType::None
&& minZoom <= zoom
&& maxZoom >= zoom;
- }
+}
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/layer_impl.hpp b/src/mbgl/style/layer_impl.hpp
index c84e9b9dea..c1f04fe513 100644
--- a/src/mbgl/style/layer_impl.hpp
+++ b/src/mbgl/style/layer_impl.hpp
@@ -5,8 +5,7 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/rapidjson.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <memory>
#include <string>
@@ -37,11 +36,17 @@ class Layer::Impl {
public:
virtual ~Impl() = default;
+ // Create a new layer with the specified `id`, `ref`, and `sourceID`. All other properties
+ // are copied from this layer.
+ std::unique_ptr<Layer> copy(const std::string& id,
+ const std::string& ref,
+ const std::string& sourceID) const;
+
// Create an identical copy of this layer.
virtual std::unique_ptr<Layer> clone() const = 0;
- virtual void parseLayout(const JSValue& value) = 0;
- virtual void parsePaints(const JSValue& value) = 0;
+ // Create a layer, copying all properties except id, ref, and paint properties from this layer.
+ virtual std::unique_ptr<Layer> cloneRef(const std::string& id) const = 0;
// If the layer has a ref, the ref. Otherwise, the id.
const std::string& bucketName() const;
diff --git a/src/mbgl/style/layers/background_layer.cpp b/src/mbgl/style/layers/background_layer.cpp
index aeb4067503..42cd76d01f 100644
--- a/src/mbgl/style/layers/background_layer.cpp
+++ b/src/mbgl/style/layers/background_layer.cpp
@@ -23,6 +23,14 @@ std::unique_ptr<Layer> BackgroundLayer::Impl::clone() const {
return std::make_unique<BackgroundLayer>(*this);
}
+std::unique_ptr<Layer> BackgroundLayer::Impl::cloneRef(const std::string& id_) const {
+ auto result = std::make_unique<BackgroundLayer>(*this);
+ result->impl->id = id_;
+ result->impl->ref = this->id;
+ result->impl->paint = BackgroundPaintProperties();
+ return std::move(result);
+}
+
// Layout properties
@@ -33,24 +41,24 @@ PropertyValue<Color> BackgroundLayer::getBackgroundColor() const {
return impl->paint.backgroundColor.get();
}
-void BackgroundLayer::setBackgroundColor(PropertyValue<Color> value) {
- impl->paint.backgroundColor.set(value);
+void BackgroundLayer::setBackgroundColor(PropertyValue<Color> value, const optional<std::string>& klass) {
+ impl->paint.backgroundColor.set(value, klass);
}
PropertyValue<std::string> BackgroundLayer::getBackgroundPattern() const {
return impl->paint.backgroundPattern.get();
}
-void BackgroundLayer::setBackgroundPattern(PropertyValue<std::string> value) {
- impl->paint.backgroundPattern.set(value);
+void BackgroundLayer::setBackgroundPattern(PropertyValue<std::string> value, const optional<std::string>& klass) {
+ impl->paint.backgroundPattern.set(value, klass);
}
PropertyValue<float> BackgroundLayer::getBackgroundOpacity() const {
return impl->paint.backgroundOpacity.get();
}
-void BackgroundLayer::setBackgroundOpacity(PropertyValue<float> value) {
- impl->paint.backgroundOpacity.set(value);
+void BackgroundLayer::setBackgroundOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.backgroundOpacity.set(value, klass);
}
} // namespace style
diff --git a/src/mbgl/style/layers/background_layer_impl.cpp b/src/mbgl/style/layers/background_layer_impl.cpp
index 0c09c5d158..ea389b828e 100644
--- a/src/mbgl/style/layers/background_layer_impl.cpp
+++ b/src/mbgl/style/layers/background_layer_impl.cpp
@@ -4,10 +4,6 @@
namespace mbgl {
namespace style {
-void BackgroundLayer::Impl::parsePaints(const JSValue& layer) {
- paint.parse(layer);
-}
-
void BackgroundLayer::Impl::cascade(const CascadeParameters& parameters) {
paint.cascade(parameters);
}
diff --git a/src/mbgl/style/layers/background_layer_impl.hpp b/src/mbgl/style/layers/background_layer_impl.hpp
index 19e2a062a4..abbb740f42 100644
--- a/src/mbgl/style/layers/background_layer_impl.hpp
+++ b/src/mbgl/style/layers/background_layer_impl.hpp
@@ -10,9 +10,7 @@ namespace style {
class BackgroundLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
-
- void parseLayout(const JSValue&) override {};
- void parsePaints(const JSValue&) override;
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void cascade(const CascadeParameters&) override;
bool recalculate(const CalculationParameters&) override;
diff --git a/src/mbgl/style/layers/background_layer_properties.cpp b/src/mbgl/style/layers/background_layer_properties.cpp
index a20cedf12c..558093a255 100644
--- a/src/mbgl/style/layers/background_layer_properties.cpp
+++ b/src/mbgl/style/layers/background_layer_properties.cpp
@@ -5,12 +5,6 @@
namespace mbgl {
namespace style {
-void BackgroundPaintProperties::parse(const JSValue& value) {
- backgroundColor.parse("background-color", value);
- backgroundPattern.parse("background-pattern", value);
- backgroundOpacity.parse("background-opacity", value);
-}
-
void BackgroundPaintProperties::cascade(const CascadeParameters& parameters) {
backgroundColor.cascade(parameters);
backgroundPattern.cascade(parameters);
diff --git a/src/mbgl/style/layers/background_layer_properties.hpp b/src/mbgl/style/layers/background_layer_properties.hpp
index a1a1a3a5a7..78a35a4f0c 100644
--- a/src/mbgl/style/layers/background_layer_properties.hpp
+++ b/src/mbgl/style/layers/background_layer_properties.hpp
@@ -2,9 +2,9 @@
#pragma once
+#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
-#include <mbgl/util/rapidjson.hpp>
namespace mbgl {
namespace style {
@@ -14,11 +14,10 @@ class CalculationParameters;
class BackgroundPaintProperties {
public:
- void parse(const JSValue&);
void cascade(const CascadeParameters&);
bool recalculate(const CalculationParameters&);
- PaintProperty<Color> backgroundColor { {{ 0, 0, 0, 1 }} };
+ PaintProperty<Color> backgroundColor { Color::black() };
PaintProperty<std::string, CrossFadedPropertyEvaluator> backgroundPattern { "" };
PaintProperty<float> backgroundOpacity { 1 };
};
diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp
index bdfbf629e6..96cc0b610c 100644
--- a/src/mbgl/style/layers/circle_layer.cpp
+++ b/src/mbgl/style/layers/circle_layer.cpp
@@ -6,10 +6,11 @@
namespace mbgl {
namespace style {
-CircleLayer::CircleLayer(const std::string& layerID)
+CircleLayer::CircleLayer(const std::string& layerID, const std::string& sourceID)
: Layer(Type::Circle, std::make_unique<Impl>())
, impl(static_cast<Impl*>(baseImpl.get())) {
impl->id = layerID;
+ impl->source = sourceID;
}
CircleLayer::CircleLayer(const Impl& other)
@@ -23,17 +24,24 @@ std::unique_ptr<Layer> CircleLayer::Impl::clone() const {
return std::make_unique<CircleLayer>(*this);
}
-// Source
-
-void CircleLayer::setSource(const std::string& sourceID, const std::string& sourceLayer) {
- impl->source = sourceID;
- impl->sourceLayer = sourceLayer;
+std::unique_ptr<Layer> CircleLayer::Impl::cloneRef(const std::string& id_) const {
+ auto result = std::make_unique<CircleLayer>(*this);
+ result->impl->id = id_;
+ result->impl->ref = this->id;
+ result->impl->paint = CirclePaintProperties();
+ return std::move(result);
}
+// Source
+
const std::string& CircleLayer::getSourceID() const {
return impl->source;
}
+void CircleLayer::setSourceLayer(const std::string& sourceLayer) {
+ impl->sourceLayer = sourceLayer;
+}
+
const std::string& CircleLayer::getSourceLayer() const {
return impl->sourceLayer;
}
@@ -57,48 +65,56 @@ PropertyValue<float> CircleLayer::getCircleRadius() const {
return impl->paint.circleRadius.get();
}
-void CircleLayer::setCircleRadius(PropertyValue<float> value) {
- impl->paint.circleRadius.set(value);
+void CircleLayer::setCircleRadius(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.circleRadius.set(value, klass);
}
PropertyValue<Color> CircleLayer::getCircleColor() const {
return impl->paint.circleColor.get();
}
-void CircleLayer::setCircleColor(PropertyValue<Color> value) {
- impl->paint.circleColor.set(value);
+void CircleLayer::setCircleColor(PropertyValue<Color> value, const optional<std::string>& klass) {
+ impl->paint.circleColor.set(value, klass);
}
PropertyValue<float> CircleLayer::getCircleBlur() const {
return impl->paint.circleBlur.get();
}
-void CircleLayer::setCircleBlur(PropertyValue<float> value) {
- impl->paint.circleBlur.set(value);
+void CircleLayer::setCircleBlur(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.circleBlur.set(value, klass);
}
PropertyValue<float> CircleLayer::getCircleOpacity() const {
return impl->paint.circleOpacity.get();
}
-void CircleLayer::setCircleOpacity(PropertyValue<float> value) {
- impl->paint.circleOpacity.set(value);
+void CircleLayer::setCircleOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.circleOpacity.set(value, klass);
}
PropertyValue<std::array<float, 2>> CircleLayer::getCircleTranslate() const {
return impl->paint.circleTranslate.get();
}
-void CircleLayer::setCircleTranslate(PropertyValue<std::array<float, 2>> value) {
- impl->paint.circleTranslate.set(value);
+void CircleLayer::setCircleTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
+ impl->paint.circleTranslate.set(value, klass);
}
PropertyValue<TranslateAnchorType> CircleLayer::getCircleTranslateAnchor() const {
return impl->paint.circleTranslateAnchor.get();
}
-void CircleLayer::setCircleTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
- impl->paint.circleTranslateAnchor.set(value);
+void CircleLayer::setCircleTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
+ impl->paint.circleTranslateAnchor.set(value, klass);
+}
+
+PropertyValue<CirclePitchScaleType> CircleLayer::getCirclePitchScale() const {
+ return impl->paint.circlePitchScale.get();
+}
+
+void CircleLayer::setCirclePitchScale(PropertyValue<CirclePitchScaleType> value, const optional<std::string>& klass) {
+ impl->paint.circlePitchScale.set(value, klass);
}
} // namespace style
diff --git a/src/mbgl/style/layers/circle_layer_impl.cpp b/src/mbgl/style/layers/circle_layer_impl.cpp
index c2efac5cef..e08d9df146 100644
--- a/src/mbgl/style/layers/circle_layer_impl.cpp
+++ b/src/mbgl/style/layers/circle_layer_impl.cpp
@@ -8,10 +8,6 @@
namespace mbgl {
namespace style {
-void CircleLayer::Impl::parsePaints(const JSValue& layer) {
- paint.parse(layer);
-}
-
void CircleLayer::Impl::cascade(const CascadeParameters& parameters) {
paint.cascade(parameters);
}
@@ -19,7 +15,7 @@ void CircleLayer::Impl::cascade(const CascadeParameters& parameters) {
bool CircleLayer::Impl::recalculate(const CalculationParameters& parameters) {
bool hasTransitions = paint.recalculate(parameters);
- passes = (paint.circleRadius > 0 && paint.circleColor.value[3] > 0 && paint.circleOpacity > 0)
+ passes = (paint.circleRadius > 0 && paint.circleColor.value.a > 0 && paint.circleOpacity > 0)
? RenderPass::Translucent : RenderPass::None;
return hasTransitions;
diff --git a/src/mbgl/style/layers/circle_layer_impl.hpp b/src/mbgl/style/layers/circle_layer_impl.hpp
index 463f3ca18d..555691b6b4 100644
--- a/src/mbgl/style/layers/circle_layer_impl.hpp
+++ b/src/mbgl/style/layers/circle_layer_impl.hpp
@@ -10,9 +10,7 @@ namespace style {
class CircleLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
-
- void parseLayout(const JSValue&) override {};
- void parsePaints(const JSValue&) override;
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void cascade(const CascadeParameters&) override;
bool recalculate(const CalculationParameters&) override;
diff --git a/src/mbgl/style/layers/circle_layer_properties.cpp b/src/mbgl/style/layers/circle_layer_properties.cpp
index b21df1e2d0..7243cf87f4 100644
--- a/src/mbgl/style/layers/circle_layer_properties.cpp
+++ b/src/mbgl/style/layers/circle_layer_properties.cpp
@@ -5,15 +5,6 @@
namespace mbgl {
namespace style {
-void CirclePaintProperties::parse(const JSValue& value) {
- circleRadius.parse("circle-radius", value);
- circleColor.parse("circle-color", value);
- circleBlur.parse("circle-blur", value);
- circleOpacity.parse("circle-opacity", value);
- circleTranslate.parse("circle-translate", value);
- circleTranslateAnchor.parse("circle-translate-anchor", value);
-}
-
void CirclePaintProperties::cascade(const CascadeParameters& parameters) {
circleRadius.cascade(parameters);
circleColor.cascade(parameters);
@@ -21,6 +12,7 @@ void CirclePaintProperties::cascade(const CascadeParameters& parameters) {
circleOpacity.cascade(parameters);
circleTranslate.cascade(parameters);
circleTranslateAnchor.cascade(parameters);
+ circlePitchScale.cascade(parameters);
}
bool CirclePaintProperties::recalculate(const CalculationParameters& parameters) {
@@ -32,6 +24,7 @@ bool CirclePaintProperties::recalculate(const CalculationParameters& parameters)
hasTransitions |= circleOpacity.calculate(parameters);
hasTransitions |= circleTranslate.calculate(parameters);
hasTransitions |= circleTranslateAnchor.calculate(parameters);
+ hasTransitions |= circlePitchScale.calculate(parameters);
return hasTransitions;
}
diff --git a/src/mbgl/style/layers/circle_layer_properties.hpp b/src/mbgl/style/layers/circle_layer_properties.hpp
index 956e423c45..0166bc8be4 100644
--- a/src/mbgl/style/layers/circle_layer_properties.hpp
+++ b/src/mbgl/style/layers/circle_layer_properties.hpp
@@ -2,9 +2,9 @@
#pragma once
+#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
-#include <mbgl/util/rapidjson.hpp>
namespace mbgl {
namespace style {
@@ -14,16 +14,16 @@ class CalculationParameters;
class CirclePaintProperties {
public:
- void parse(const JSValue&);
void cascade(const CascadeParameters&);
bool recalculate(const CalculationParameters&);
PaintProperty<float> circleRadius { 5 };
- PaintProperty<Color> circleColor { {{ 0, 0, 0, 1 }} };
+ PaintProperty<Color> circleColor { Color::black() };
PaintProperty<float> circleBlur { 0 };
PaintProperty<float> circleOpacity { 1 };
PaintProperty<std::array<float, 2>> circleTranslate { {{ 0, 0 }} };
PaintProperty<TranslateAnchorType> circleTranslateAnchor { TranslateAnchorType::Map };
+ PaintProperty<CirclePitchScaleType> circlePitchScale { CirclePitchScaleType::Map };
};
} // namespace style
diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp
index 214d4ce663..a0686e353c 100644
--- a/src/mbgl/style/layers/custom_layer_impl.cpp
+++ b/src/mbgl/style/layers/custom_layer_impl.cpp
@@ -33,6 +33,11 @@ std::unique_ptr<Layer> CustomLayer::Impl::clone() const {
return std::make_unique<CustomLayer>(*this);
}
+std::unique_ptr<Layer> CustomLayer::Impl::cloneRef(const std::string&) const {
+ assert(false);
+ return std::make_unique<CustomLayer>(*this);
+}
+
void CustomLayer::Impl::initialize() {
assert(initializeFn);
initializeFn(context);
diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp
index 00e576b6a3..ffa892ddf8 100644
--- a/src/mbgl/style/layers/custom_layer_impl.hpp
+++ b/src/mbgl/style/layers/custom_layer_impl.hpp
@@ -25,9 +25,7 @@ public:
private:
std::unique_ptr<Layer> clone() const override;
-
- void parseLayout(const JSValue&) final {}
- void parsePaints(const JSValue&) final {}
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void cascade(const CascadeParameters&) final {}
bool recalculate(const CalculationParameters&) final;
diff --git a/src/mbgl/style/layers/fill_layer.cpp b/src/mbgl/style/layers/fill_layer.cpp
index 1deaabb5ef..44894aff33 100644
--- a/src/mbgl/style/layers/fill_layer.cpp
+++ b/src/mbgl/style/layers/fill_layer.cpp
@@ -6,10 +6,11 @@
namespace mbgl {
namespace style {
-FillLayer::FillLayer(const std::string& layerID)
+FillLayer::FillLayer(const std::string& layerID, const std::string& sourceID)
: Layer(Type::Fill, std::make_unique<Impl>())
, impl(static_cast<Impl*>(baseImpl.get())) {
impl->id = layerID;
+ impl->source = sourceID;
}
FillLayer::FillLayer(const Impl& other)
@@ -23,17 +24,24 @@ std::unique_ptr<Layer> FillLayer::Impl::clone() const {
return std::make_unique<FillLayer>(*this);
}
-// Source
-
-void FillLayer::setSource(const std::string& sourceID, const std::string& sourceLayer) {
- impl->source = sourceID;
- impl->sourceLayer = sourceLayer;
+std::unique_ptr<Layer> FillLayer::Impl::cloneRef(const std::string& id_) const {
+ auto result = std::make_unique<FillLayer>(*this);
+ result->impl->id = id_;
+ result->impl->ref = this->id;
+ result->impl->paint = FillPaintProperties();
+ return std::move(result);
}
+// Source
+
const std::string& FillLayer::getSourceID() const {
return impl->source;
}
+void FillLayer::setSourceLayer(const std::string& sourceLayer) {
+ impl->sourceLayer = sourceLayer;
+}
+
const std::string& FillLayer::getSourceLayer() const {
return impl->sourceLayer;
}
@@ -57,56 +65,56 @@ PropertyValue<bool> FillLayer::getFillAntialias() const {
return impl->paint.fillAntialias.get();
}
-void FillLayer::setFillAntialias(PropertyValue<bool> value) {
- impl->paint.fillAntialias.set(value);
+void FillLayer::setFillAntialias(PropertyValue<bool> value, const optional<std::string>& klass) {
+ impl->paint.fillAntialias.set(value, klass);
}
PropertyValue<float> FillLayer::getFillOpacity() const {
return impl->paint.fillOpacity.get();
}
-void FillLayer::setFillOpacity(PropertyValue<float> value) {
- impl->paint.fillOpacity.set(value);
+void FillLayer::setFillOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.fillOpacity.set(value, klass);
}
PropertyValue<Color> FillLayer::getFillColor() const {
return impl->paint.fillColor.get();
}
-void FillLayer::setFillColor(PropertyValue<Color> value) {
- impl->paint.fillColor.set(value);
+void FillLayer::setFillColor(PropertyValue<Color> value, const optional<std::string>& klass) {
+ impl->paint.fillColor.set(value, klass);
}
PropertyValue<Color> FillLayer::getFillOutlineColor() const {
return impl->paint.fillOutlineColor.get();
}
-void FillLayer::setFillOutlineColor(PropertyValue<Color> value) {
- impl->paint.fillOutlineColor.set(value);
+void FillLayer::setFillOutlineColor(PropertyValue<Color> value, const optional<std::string>& klass) {
+ impl->paint.fillOutlineColor.set(value, klass);
}
PropertyValue<std::array<float, 2>> FillLayer::getFillTranslate() const {
return impl->paint.fillTranslate.get();
}
-void FillLayer::setFillTranslate(PropertyValue<std::array<float, 2>> value) {
- impl->paint.fillTranslate.set(value);
+void FillLayer::setFillTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
+ impl->paint.fillTranslate.set(value, klass);
}
PropertyValue<TranslateAnchorType> FillLayer::getFillTranslateAnchor() const {
return impl->paint.fillTranslateAnchor.get();
}
-void FillLayer::setFillTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
- impl->paint.fillTranslateAnchor.set(value);
+void FillLayer::setFillTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
+ impl->paint.fillTranslateAnchor.set(value, klass);
}
PropertyValue<std::string> FillLayer::getFillPattern() const {
return impl->paint.fillPattern.get();
}
-void FillLayer::setFillPattern(PropertyValue<std::string> value) {
- impl->paint.fillPattern.set(value);
+void FillLayer::setFillPattern(PropertyValue<std::string> value, const optional<std::string>& klass) {
+ impl->paint.fillPattern.set(value, klass);
}
} // namespace style
diff --git a/src/mbgl/style/layers/fill_layer_impl.cpp b/src/mbgl/style/layers/fill_layer_impl.cpp
index c183617482..2992312514 100644
--- a/src/mbgl/style/layers/fill_layer_impl.cpp
+++ b/src/mbgl/style/layers/fill_layer_impl.cpp
@@ -8,10 +8,6 @@
namespace mbgl {
namespace style {
-void FillLayer::Impl::parsePaints(const JSValue& layer) {
- paint.parse(layer);
-}
-
void FillLayer::Impl::cascade(const CascadeParameters& parameters) {
paint.cascade(parameters);
}
@@ -25,7 +21,7 @@ bool FillLayer::Impl::recalculate(const CalculationParameters& parameters) {
passes |= RenderPass::Translucent;
}
- if (!paint.fillPattern.value.from.empty() || (paint.fillColor.value[3] * paint.fillOpacity) < 1.0f) {
+ if (!paint.fillPattern.value.from.empty() || (paint.fillColor.value.a * paint.fillOpacity) < 1.0f) {
passes |= RenderPass::Translucent;
} else {
passes |= RenderPass::Opaque;
diff --git a/src/mbgl/style/layers/fill_layer_impl.hpp b/src/mbgl/style/layers/fill_layer_impl.hpp
index a37dd76ace..fc6578ecd1 100644
--- a/src/mbgl/style/layers/fill_layer_impl.hpp
+++ b/src/mbgl/style/layers/fill_layer_impl.hpp
@@ -10,9 +10,7 @@ namespace style {
class FillLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
-
- void parseLayout(const JSValue&) override {};
- void parsePaints(const JSValue&) override;
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void cascade(const CascadeParameters&) override;
bool recalculate(const CalculationParameters&) override;
diff --git a/src/mbgl/style/layers/fill_layer_properties.cpp b/src/mbgl/style/layers/fill_layer_properties.cpp
index a4714689f9..9a55cbc145 100644
--- a/src/mbgl/style/layers/fill_layer_properties.cpp
+++ b/src/mbgl/style/layers/fill_layer_properties.cpp
@@ -5,16 +5,6 @@
namespace mbgl {
namespace style {
-void FillPaintProperties::parse(const JSValue& value) {
- fillAntialias.parse("fill-antialias", value);
- fillOpacity.parse("fill-opacity", value);
- fillColor.parse("fill-color", value);
- fillOutlineColor.parse("fill-outline-color", value);
- fillTranslate.parse("fill-translate", value);
- fillTranslateAnchor.parse("fill-translate-anchor", value);
- fillPattern.parse("fill-pattern", value);
-}
-
void FillPaintProperties::cascade(const CascadeParameters& parameters) {
fillAntialias.cascade(parameters);
fillOpacity.cascade(parameters);
diff --git a/src/mbgl/style/layers/fill_layer_properties.hpp b/src/mbgl/style/layers/fill_layer_properties.hpp
index 43396f45d2..d12eb8d6f4 100644
--- a/src/mbgl/style/layers/fill_layer_properties.hpp
+++ b/src/mbgl/style/layers/fill_layer_properties.hpp
@@ -2,9 +2,9 @@
#pragma once
+#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
-#include <mbgl/util/rapidjson.hpp>
namespace mbgl {
namespace style {
@@ -14,14 +14,13 @@ class CalculationParameters;
class FillPaintProperties {
public:
- void parse(const JSValue&);
void cascade(const CascadeParameters&);
bool recalculate(const CalculationParameters&);
PaintProperty<bool> fillAntialias { true };
PaintProperty<float> fillOpacity { 1 };
- PaintProperty<Color> fillColor { {{ 0, 0, 0, 1 }} };
- PaintProperty<Color> fillOutlineColor { {{ 0, 0, 0, -1 }} };
+ PaintProperty<Color> fillColor { Color::black() };
+ PaintProperty<Color> fillOutlineColor { {} };
PaintProperty<std::array<float, 2>> fillTranslate { {{ 0, 0 }} };
PaintProperty<TranslateAnchorType> fillTranslateAnchor { TranslateAnchorType::Map };
PaintProperty<std::string, CrossFadedPropertyEvaluator> fillPattern { "" };
diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs
new file mode 100644
index 0000000000..017691c8ec
--- /dev/null
+++ b/src/mbgl/style/layers/layer.cpp.ejs
@@ -0,0 +1,100 @@
+<%
+ const type = locals.type;
+ const layoutProperties = locals.layoutProperties;
+ const paintProperties = locals.paintProperties;
+-%>
+// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
+
+#include <mbgl/style/layers/<%- type %>_layer.hpp>
+#include <mbgl/style/layers/<%- type %>_layer_impl.hpp>
+
+namespace mbgl {
+namespace style {
+
+<% if (type === 'background') { -%>
+<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const std::string& layerID)
+ : Layer(Type::<%- camelize(type) %>, std::make_unique<Impl>())
+ , impl(static_cast<Impl*>(baseImpl.get())) {
+ impl->id = layerID;
+}
+<% } else { -%>
+<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const std::string& layerID, const std::string& sourceID)
+ : Layer(Type::<%- camelize(type) %>, std::make_unique<Impl>())
+ , impl(static_cast<Impl*>(baseImpl.get())) {
+ impl->id = layerID;
+ impl->source = sourceID;
+}
+<% } -%>
+
+<%- camelize(type) %>Layer::<%- camelize(type) %>Layer(const Impl& other)
+ : Layer(Type::<%- camelize(type) %>, std::make_unique<Impl>(other))
+ , impl(static_cast<Impl*>(baseImpl.get())) {
+}
+
+<%- camelize(type) %>Layer::~<%- camelize(type) %>Layer() = default;
+
+std::unique_ptr<Layer> <%- camelize(type) %>Layer::Impl::clone() const {
+ return std::make_unique<<%- camelize(type) %>Layer>(*this);
+}
+
+std::unique_ptr<Layer> <%- camelize(type) %>Layer::Impl::cloneRef(const std::string& id_) const {
+ auto result = std::make_unique<<%- camelize(type) %>Layer>(*this);
+ result->impl->id = id_;
+ result->impl->ref = this->id;
+ result->impl->paint = <%- camelize(type) %>PaintProperties();
+ return std::move(result);
+}
+
+<% if (type !== 'background') { -%>
+// Source
+
+const std::string& <%- camelize(type) %>Layer::getSourceID() const {
+ return impl->source;
+}
+
+<% if (type !== 'raster') { -%>
+void <%- camelize(type) %>Layer::setSourceLayer(const std::string& sourceLayer) {
+ impl->sourceLayer = sourceLayer;
+}
+
+const std::string& <%- camelize(type) %>Layer::getSourceLayer() const {
+ return impl->sourceLayer;
+}
+
+// Filter
+
+void <%- camelize(type) %>Layer::setFilter(const Filter& filter) {
+ impl->filter = filter;
+}
+
+const Filter& <%- camelize(type) %>Layer::getFilter() const {
+ return impl->filter;
+}
+<% } -%>
+<% } -%>
+
+// Layout properties
+
+<% for (const property of layoutProperties) { -%>
+PropertyValue<<%- propertyType(property) %>> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const {
+ return impl->layout.<%- camelizeWithLeadingLowercase(property.name) %>.get();
+}
+
+void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>> value) {
+ impl->layout.<%- camelizeWithLeadingLowercase(property.name) %>.set(value);
+}
+<% } -%>
+
+// Paint properties
+<% for (const property of paintProperties) { %>
+PropertyValue<<%- propertyType(property) %>> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const {
+ return impl->paint.<%- camelizeWithLeadingLowercase(property.name) %>.get();
+}
+
+void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>> value, const optional<std::string>& klass) {
+ impl->paint.<%- camelizeWithLeadingLowercase(property.name) %>.set(value, klass);
+}
+<% } -%>
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/layer_properties.cpp.ejs b/src/mbgl/style/layers/layer_properties.cpp.ejs
new file mode 100644
index 0000000000..b781a4a9d9
--- /dev/null
+++ b/src/mbgl/style/layers/layer_properties.cpp.ejs
@@ -0,0 +1,38 @@
+<%
+ const type = locals.type;
+ const layoutProperties = locals.layoutProperties;
+ const paintProperties = locals.paintProperties;
+-%>
+// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
+
+#include <mbgl/style/layers/<%- type %>_layer_properties.hpp>
+
+namespace mbgl {
+namespace style {
+
+<% if (layoutProperties.length) { -%>
+void <%- camelize(type) %>LayoutProperties::recalculate(const CalculationParameters& parameters) {
+<% for (const property of layoutProperties) { -%>
+ <%- camelizeWithLeadingLowercase(property.name) %>.calculate(parameters);
+<% } -%>
+}
+
+<% } -%>
+void <%- camelize(type) %>PaintProperties::cascade(const CascadeParameters& parameters) {
+<% for (const property of paintProperties) { -%>
+ <%- camelizeWithLeadingLowercase(property.name) %>.cascade(parameters);
+<% } -%>
+}
+
+bool <%- camelize(type) %>PaintProperties::recalculate(const CalculationParameters& parameters) {
+ bool hasTransitions = false;
+
+<% for (const property of paintProperties) { -%>
+ hasTransitions |= <%- camelizeWithLeadingLowercase(property.name) %>.calculate(parameters);
+<% } -%>
+
+ return hasTransitions;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs
new file mode 100644
index 0000000000..6ec92703f1
--- /dev/null
+++ b/src/mbgl/style/layers/layer_properties.hpp.ejs
@@ -0,0 +1,54 @@
+<%
+ const type = locals.type;
+ const layoutProperties = locals.layoutProperties;
+ const paintProperties = locals.paintProperties;
+-%>
+// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
+
+#pragma once
+
+#include <mbgl/style/types.hpp>
+#include <mbgl/style/layout_property.hpp>
+#include <mbgl/style/paint_property.hpp>
+
+namespace mbgl {
+namespace style {
+
+class CascadeParameters;
+class CalculationParameters;
+
+<% if (layoutProperties.length) { -%>
+class <%- camelize(type) %>LayoutProperties {
+public:
+ void recalculate(const CalculationParameters&);
+
+<% for (const property of layoutProperties) { -%>
+ LayoutProperty<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %> { <%- defaultValue(property) %> };
+<% } -%>
+};
+
+<% } -%>
+class <%- camelize(type) %>PaintProperties {
+public:
+ void cascade(const CascadeParameters&);
+ bool recalculate(const CalculationParameters&);
+
+<% for (const property of paintProperties) { -%>
+<% if (/-pattern$/.test(property.name) || property.name === 'line-dasharray') { -%>
+ PaintProperty<<%- propertyType(property) %>, CrossFadedPropertyEvaluator> <%- camelizeWithLeadingLowercase(property.name) %> { <%- defaultValue(property) %> };
+<% } else if (property.name === 'fill-outline-color') { -%>
+ PaintProperty<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %> { {} };
+<% } else if (property.name.endsWith('color') && defaultValue(property) === '{ 0, 0, 0, 0 }') { -%>
+ PaintProperty<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %> { {} };
+<% } else if (property.name.endsWith('color') && defaultValue(property) === '{ 0, 0, 0, 1 }') { -%>
+ PaintProperty<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %> { Color::black() };
+<% } else if (property.name.endsWith('color') && defaultValue(property) === '{ 1, 1, 1, 1 }') { -%>
+ PaintProperty<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %> { Color::white() };
+<% } else { -%>
+ PaintProperty<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %> { <%- defaultValue(property) %> };
+<% } -%>
+<% } -%>
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp
index abe326a672..3ad72cda16 100644
--- a/src/mbgl/style/layers/line_layer.cpp
+++ b/src/mbgl/style/layers/line_layer.cpp
@@ -6,10 +6,11 @@
namespace mbgl {
namespace style {
-LineLayer::LineLayer(const std::string& layerID)
+LineLayer::LineLayer(const std::string& layerID, const std::string& sourceID)
: Layer(Type::Line, std::make_unique<Impl>())
, impl(static_cast<Impl*>(baseImpl.get())) {
impl->id = layerID;
+ impl->source = sourceID;
}
LineLayer::LineLayer(const Impl& other)
@@ -23,17 +24,24 @@ std::unique_ptr<Layer> LineLayer::Impl::clone() const {
return std::make_unique<LineLayer>(*this);
}
-// Source
-
-void LineLayer::setSource(const std::string& sourceID, const std::string& sourceLayer) {
- impl->source = sourceID;
- impl->sourceLayer = sourceLayer;
+std::unique_ptr<Layer> LineLayer::Impl::cloneRef(const std::string& id_) const {
+ auto result = std::make_unique<LineLayer>(*this);
+ result->impl->id = id_;
+ result->impl->ref = this->id;
+ result->impl->paint = LinePaintProperties();
+ return std::move(result);
}
+// Source
+
const std::string& LineLayer::getSourceID() const {
return impl->source;
}
+void LineLayer::setSourceLayer(const std::string& sourceLayer) {
+ impl->sourceLayer = sourceLayer;
+}
+
const std::string& LineLayer::getSourceLayer() const {
return impl->sourceLayer;
}
@@ -85,80 +93,80 @@ PropertyValue<float> LineLayer::getLineOpacity() const {
return impl->paint.lineOpacity.get();
}
-void LineLayer::setLineOpacity(PropertyValue<float> value) {
- impl->paint.lineOpacity.set(value);
+void LineLayer::setLineOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.lineOpacity.set(value, klass);
}
PropertyValue<Color> LineLayer::getLineColor() const {
return impl->paint.lineColor.get();
}
-void LineLayer::setLineColor(PropertyValue<Color> value) {
- impl->paint.lineColor.set(value);
+void LineLayer::setLineColor(PropertyValue<Color> value, const optional<std::string>& klass) {
+ impl->paint.lineColor.set(value, klass);
}
PropertyValue<std::array<float, 2>> LineLayer::getLineTranslate() const {
return impl->paint.lineTranslate.get();
}
-void LineLayer::setLineTranslate(PropertyValue<std::array<float, 2>> value) {
- impl->paint.lineTranslate.set(value);
+void LineLayer::setLineTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
+ impl->paint.lineTranslate.set(value, klass);
}
PropertyValue<TranslateAnchorType> LineLayer::getLineTranslateAnchor() const {
return impl->paint.lineTranslateAnchor.get();
}
-void LineLayer::setLineTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
- impl->paint.lineTranslateAnchor.set(value);
+void LineLayer::setLineTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
+ impl->paint.lineTranslateAnchor.set(value, klass);
}
PropertyValue<float> LineLayer::getLineWidth() const {
return impl->paint.lineWidth.get();
}
-void LineLayer::setLineWidth(PropertyValue<float> value) {
- impl->paint.lineWidth.set(value);
+void LineLayer::setLineWidth(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.lineWidth.set(value, klass);
}
PropertyValue<float> LineLayer::getLineGapWidth() const {
return impl->paint.lineGapWidth.get();
}
-void LineLayer::setLineGapWidth(PropertyValue<float> value) {
- impl->paint.lineGapWidth.set(value);
+void LineLayer::setLineGapWidth(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.lineGapWidth.set(value, klass);
}
PropertyValue<float> LineLayer::getLineOffset() const {
return impl->paint.lineOffset.get();
}
-void LineLayer::setLineOffset(PropertyValue<float> value) {
- impl->paint.lineOffset.set(value);
+void LineLayer::setLineOffset(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.lineOffset.set(value, klass);
}
PropertyValue<float> LineLayer::getLineBlur() const {
return impl->paint.lineBlur.get();
}
-void LineLayer::setLineBlur(PropertyValue<float> value) {
- impl->paint.lineBlur.set(value);
+void LineLayer::setLineBlur(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.lineBlur.set(value, klass);
}
PropertyValue<std::vector<float>> LineLayer::getLineDasharray() const {
return impl->paint.lineDasharray.get();
}
-void LineLayer::setLineDasharray(PropertyValue<std::vector<float>> value) {
- impl->paint.lineDasharray.set(value);
+void LineLayer::setLineDasharray(PropertyValue<std::vector<float>> value, const optional<std::string>& klass) {
+ impl->paint.lineDasharray.set(value, klass);
}
PropertyValue<std::string> LineLayer::getLinePattern() const {
return impl->paint.linePattern.get();
}
-void LineLayer::setLinePattern(PropertyValue<std::string> value) {
- impl->paint.linePattern.set(value);
+void LineLayer::setLinePattern(PropertyValue<std::string> value, const optional<std::string>& klass) {
+ impl->paint.linePattern.set(value, klass);
}
} // namespace style
diff --git a/src/mbgl/style/layers/line_layer_impl.cpp b/src/mbgl/style/layers/line_layer_impl.cpp
index b7ee9dc5bf..3cdd90b7fd 100644
--- a/src/mbgl/style/layers/line_layer_impl.cpp
+++ b/src/mbgl/style/layers/line_layer_impl.cpp
@@ -8,14 +8,6 @@
namespace mbgl {
namespace style {
-void LineLayer::Impl::parseLayout(const JSValue& value) {
- layout.parse(value);
-}
-
-void LineLayer::Impl::parsePaints(const JSValue& layer) {
- paint.parse(layer);
-}
-
void LineLayer::Impl::cascade(const CascadeParameters& parameters) {
paint.cascade(parameters);
}
@@ -29,7 +21,7 @@ bool LineLayer::Impl::recalculate(const CalculationParameters& parameters) {
bool hasTransitions = paint.recalculate(parameters);
- passes = (paint.lineOpacity > 0 && paint.lineColor.value[3] > 0 && paint.lineWidth > 0)
+ passes = (paint.lineOpacity > 0 && paint.lineColor.value.a > 0 && paint.lineWidth > 0)
? RenderPass::Translucent : RenderPass::None;
return hasTransitions;
diff --git a/src/mbgl/style/layers/line_layer_impl.hpp b/src/mbgl/style/layers/line_layer_impl.hpp
index 3356dc2ceb..e130bc01bc 100644
--- a/src/mbgl/style/layers/line_layer_impl.hpp
+++ b/src/mbgl/style/layers/line_layer_impl.hpp
@@ -10,9 +10,7 @@ namespace style {
class LineLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
-
- void parseLayout(const JSValue&) override;
- void parsePaints(const JSValue&) override;
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void cascade(const CascadeParameters&) override;
bool recalculate(const CalculationParameters&) override;
diff --git a/src/mbgl/style/layers/line_layer_properties.cpp b/src/mbgl/style/layers/line_layer_properties.cpp
index 7c74f6de04..2d6092745e 100644
--- a/src/mbgl/style/layers/line_layer_properties.cpp
+++ b/src/mbgl/style/layers/line_layer_properties.cpp
@@ -5,13 +5,6 @@
namespace mbgl {
namespace style {
-void LineLayoutProperties::parse(const JSValue& value) {
- lineCap.parse("line-cap", value);
- lineJoin.parse("line-join", value);
- lineMiterLimit.parse("line-miter-limit", value);
- lineRoundLimit.parse("line-round-limit", value);
-}
-
void LineLayoutProperties::recalculate(const CalculationParameters& parameters) {
lineCap.calculate(parameters);
lineJoin.calculate(parameters);
@@ -19,19 +12,6 @@ void LineLayoutProperties::recalculate(const CalculationParameters& parameters)
lineRoundLimit.calculate(parameters);
}
-void LinePaintProperties::parse(const JSValue& value) {
- lineOpacity.parse("line-opacity", value);
- lineColor.parse("line-color", value);
- lineTranslate.parse("line-translate", value);
- lineTranslateAnchor.parse("line-translate-anchor", value);
- lineWidth.parse("line-width", value);
- lineGapWidth.parse("line-gap-width", value);
- lineOffset.parse("line-offset", value);
- lineBlur.parse("line-blur", value);
- lineDasharray.parse("line-dasharray", value);
- linePattern.parse("line-pattern", value);
-}
-
void LinePaintProperties::cascade(const CascadeParameters& parameters) {
lineOpacity.cascade(parameters);
lineColor.cascade(parameters);
diff --git a/src/mbgl/style/layers/line_layer_properties.hpp b/src/mbgl/style/layers/line_layer_properties.hpp
index 01a8534222..b73e3f2ef2 100644
--- a/src/mbgl/style/layers/line_layer_properties.hpp
+++ b/src/mbgl/style/layers/line_layer_properties.hpp
@@ -2,9 +2,9 @@
#pragma once
+#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
-#include <mbgl/util/rapidjson.hpp>
namespace mbgl {
namespace style {
@@ -14,7 +14,6 @@ class CalculationParameters;
class LineLayoutProperties {
public:
- void parse(const JSValue&);
void recalculate(const CalculationParameters&);
LayoutProperty<LineCapType> lineCap { LineCapType::Butt };
@@ -25,12 +24,11 @@ public:
class LinePaintProperties {
public:
- void parse(const JSValue&);
void cascade(const CascadeParameters&);
bool recalculate(const CalculationParameters&);
PaintProperty<float> lineOpacity { 1 };
- PaintProperty<Color> lineColor { {{ 0, 0, 0, 1 }} };
+ PaintProperty<Color> lineColor { Color::black() };
PaintProperty<std::array<float, 2>> lineTranslate { {{ 0, 0 }} };
PaintProperty<TranslateAnchorType> lineTranslateAnchor { TranslateAnchorType::Map };
PaintProperty<float> lineWidth { 1 };
diff --git a/src/mbgl/style/layers/raster_layer.cpp b/src/mbgl/style/layers/raster_layer.cpp
index fb7f08fbe9..d4e121babe 100644
--- a/src/mbgl/style/layers/raster_layer.cpp
+++ b/src/mbgl/style/layers/raster_layer.cpp
@@ -6,10 +6,11 @@
namespace mbgl {
namespace style {
-RasterLayer::RasterLayer(const std::string& layerID)
+RasterLayer::RasterLayer(const std::string& layerID, const std::string& sourceID)
: Layer(Type::Raster, std::make_unique<Impl>())
, impl(static_cast<Impl*>(baseImpl.get())) {
impl->id = layerID;
+ impl->source = sourceID;
}
RasterLayer::RasterLayer(const Impl& other)
@@ -23,16 +24,21 @@ std::unique_ptr<Layer> RasterLayer::Impl::clone() const {
return std::make_unique<RasterLayer>(*this);
}
-// Source
-
-void RasterLayer::setSource(const std::string& sourceID) {
- impl->source = sourceID;
+std::unique_ptr<Layer> RasterLayer::Impl::cloneRef(const std::string& id_) const {
+ auto result = std::make_unique<RasterLayer>(*this);
+ result->impl->id = id_;
+ result->impl->ref = this->id;
+ result->impl->paint = RasterPaintProperties();
+ return std::move(result);
}
+// Source
+
const std::string& RasterLayer::getSourceID() const {
return impl->source;
}
+
// Layout properties
@@ -42,56 +48,56 @@ PropertyValue<float> RasterLayer::getRasterOpacity() const {
return impl->paint.rasterOpacity.get();
}
-void RasterLayer::setRasterOpacity(PropertyValue<float> value) {
- impl->paint.rasterOpacity.set(value);
+void RasterLayer::setRasterOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.rasterOpacity.set(value, klass);
}
PropertyValue<float> RasterLayer::getRasterHueRotate() const {
return impl->paint.rasterHueRotate.get();
}
-void RasterLayer::setRasterHueRotate(PropertyValue<float> value) {
- impl->paint.rasterHueRotate.set(value);
+void RasterLayer::setRasterHueRotate(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.rasterHueRotate.set(value, klass);
}
PropertyValue<float> RasterLayer::getRasterBrightnessMin() const {
return impl->paint.rasterBrightnessMin.get();
}
-void RasterLayer::setRasterBrightnessMin(PropertyValue<float> value) {
- impl->paint.rasterBrightnessMin.set(value);
+void RasterLayer::setRasterBrightnessMin(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.rasterBrightnessMin.set(value, klass);
}
PropertyValue<float> RasterLayer::getRasterBrightnessMax() const {
return impl->paint.rasterBrightnessMax.get();
}
-void RasterLayer::setRasterBrightnessMax(PropertyValue<float> value) {
- impl->paint.rasterBrightnessMax.set(value);
+void RasterLayer::setRasterBrightnessMax(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.rasterBrightnessMax.set(value, klass);
}
PropertyValue<float> RasterLayer::getRasterSaturation() const {
return impl->paint.rasterSaturation.get();
}
-void RasterLayer::setRasterSaturation(PropertyValue<float> value) {
- impl->paint.rasterSaturation.set(value);
+void RasterLayer::setRasterSaturation(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.rasterSaturation.set(value, klass);
}
PropertyValue<float> RasterLayer::getRasterContrast() const {
return impl->paint.rasterContrast.get();
}
-void RasterLayer::setRasterContrast(PropertyValue<float> value) {
- impl->paint.rasterContrast.set(value);
+void RasterLayer::setRasterContrast(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.rasterContrast.set(value, klass);
}
PropertyValue<float> RasterLayer::getRasterFadeDuration() const {
return impl->paint.rasterFadeDuration.get();
}
-void RasterLayer::setRasterFadeDuration(PropertyValue<float> value) {
- impl->paint.rasterFadeDuration.set(value);
+void RasterLayer::setRasterFadeDuration(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.rasterFadeDuration.set(value, klass);
}
} // namespace style
diff --git a/src/mbgl/style/layers/raster_layer_impl.cpp b/src/mbgl/style/layers/raster_layer_impl.cpp
index 4854ec041d..879bfa4559 100644
--- a/src/mbgl/style/layers/raster_layer_impl.cpp
+++ b/src/mbgl/style/layers/raster_layer_impl.cpp
@@ -4,10 +4,6 @@
namespace mbgl {
namespace style {
-void RasterLayer::Impl::parsePaints(const JSValue& layer) {
- paint.parse(layer);
-}
-
void RasterLayer::Impl::cascade(const CascadeParameters& parameters) {
paint.cascade(parameters);
}
diff --git a/src/mbgl/style/layers/raster_layer_impl.hpp b/src/mbgl/style/layers/raster_layer_impl.hpp
index 6812b469a6..a5b396e2ed 100644
--- a/src/mbgl/style/layers/raster_layer_impl.hpp
+++ b/src/mbgl/style/layers/raster_layer_impl.hpp
@@ -10,9 +10,7 @@ namespace style {
class RasterLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
-
- void parseLayout(const JSValue&) override {};
- void parsePaints(const JSValue&) override;
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void cascade(const CascadeParameters&) override;
bool recalculate(const CalculationParameters&) override;
diff --git a/src/mbgl/style/layers/raster_layer_properties.cpp b/src/mbgl/style/layers/raster_layer_properties.cpp
index 0e6afc5e9c..68d9d1d35d 100644
--- a/src/mbgl/style/layers/raster_layer_properties.cpp
+++ b/src/mbgl/style/layers/raster_layer_properties.cpp
@@ -5,16 +5,6 @@
namespace mbgl {
namespace style {
-void RasterPaintProperties::parse(const JSValue& value) {
- rasterOpacity.parse("raster-opacity", value);
- rasterHueRotate.parse("raster-hue-rotate", value);
- rasterBrightnessMin.parse("raster-brightness-min", value);
- rasterBrightnessMax.parse("raster-brightness-max", value);
- rasterSaturation.parse("raster-saturation", value);
- rasterContrast.parse("raster-contrast", value);
- rasterFadeDuration.parse("raster-fade-duration", value);
-}
-
void RasterPaintProperties::cascade(const CascadeParameters& parameters) {
rasterOpacity.cascade(parameters);
rasterHueRotate.cascade(parameters);
diff --git a/src/mbgl/style/layers/raster_layer_properties.hpp b/src/mbgl/style/layers/raster_layer_properties.hpp
index 049da87312..ddfb833e12 100644
--- a/src/mbgl/style/layers/raster_layer_properties.hpp
+++ b/src/mbgl/style/layers/raster_layer_properties.hpp
@@ -2,9 +2,9 @@
#pragma once
+#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
-#include <mbgl/util/rapidjson.hpp>
namespace mbgl {
namespace style {
@@ -14,7 +14,6 @@ class CalculationParameters;
class RasterPaintProperties {
public:
- void parse(const JSValue&);
void cascade(const CascadeParameters&);
bool recalculate(const CalculationParameters&);
diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp
index d7d6a02ace..c26123f0ec 100644
--- a/src/mbgl/style/layers/symbol_layer.cpp
+++ b/src/mbgl/style/layers/symbol_layer.cpp
@@ -6,10 +6,11 @@
namespace mbgl {
namespace style {
-SymbolLayer::SymbolLayer(const std::string& layerID)
+SymbolLayer::SymbolLayer(const std::string& layerID, const std::string& sourceID)
: Layer(Type::Symbol, std::make_unique<Impl>())
, impl(static_cast<Impl*>(baseImpl.get())) {
impl->id = layerID;
+ impl->source = sourceID;
}
SymbolLayer::SymbolLayer(const Impl& other)
@@ -23,17 +24,24 @@ std::unique_ptr<Layer> SymbolLayer::Impl::clone() const {
return std::make_unique<SymbolLayer>(*this);
}
-// Source
-
-void SymbolLayer::setSource(const std::string& sourceID, const std::string& sourceLayer) {
- impl->source = sourceID;
- impl->sourceLayer = sourceLayer;
+std::unique_ptr<Layer> SymbolLayer::Impl::cloneRef(const std::string& id_) const {
+ auto result = std::make_unique<SymbolLayer>(*this);
+ result->impl->id = id_;
+ result->impl->ref = this->id;
+ result->impl->paint = SymbolPaintProperties();
+ return std::move(result);
}
+// Source
+
const std::string& SymbolLayer::getSourceID() const {
return impl->source;
}
+void SymbolLayer::setSourceLayer(const std::string& sourceLayer) {
+ impl->sourceLayer = sourceLayer;
+}
+
const std::string& SymbolLayer::getSourceLayer() const {
return impl->sourceLayer;
}
@@ -92,11 +100,11 @@ PropertyValue<bool> SymbolLayer::getIconOptional() const {
void SymbolLayer::setIconOptional(PropertyValue<bool> value) {
impl->layout.iconOptional.set(value);
}
-PropertyValue<RotationAlignmentType> SymbolLayer::getIconRotationAlignment() const {
+PropertyValue<AlignmentType> SymbolLayer::getIconRotationAlignment() const {
return impl->layout.iconRotationAlignment.get();
}
-void SymbolLayer::setIconRotationAlignment(PropertyValue<RotationAlignmentType> value) {
+void SymbolLayer::setIconRotationAlignment(PropertyValue<AlignmentType> value) {
impl->layout.iconRotationAlignment.set(value);
}
PropertyValue<float> SymbolLayer::getIconSize() const {
@@ -106,6 +114,20 @@ PropertyValue<float> SymbolLayer::getIconSize() const {
void SymbolLayer::setIconSize(PropertyValue<float> value) {
impl->layout.iconSize.set(value);
}
+PropertyValue<IconTextFitType> SymbolLayer::getIconTextFit() const {
+ return impl->layout.iconTextFit.get();
+}
+
+void SymbolLayer::setIconTextFit(PropertyValue<IconTextFitType> value) {
+ impl->layout.iconTextFit.set(value);
+}
+PropertyValue<std::array<float, 4>> SymbolLayer::getIconTextFitPadding() const {
+ return impl->layout.iconTextFitPadding.get();
+}
+
+void SymbolLayer::setIconTextFitPadding(PropertyValue<std::array<float, 4>> value) {
+ impl->layout.iconTextFitPadding.set(value);
+}
PropertyValue<std::string> SymbolLayer::getIconImage() const {
return impl->layout.iconImage.get();
}
@@ -141,11 +163,18 @@ PropertyValue<std::array<float, 2>> SymbolLayer::getIconOffset() const {
void SymbolLayer::setIconOffset(PropertyValue<std::array<float, 2>> value) {
impl->layout.iconOffset.set(value);
}
-PropertyValue<RotationAlignmentType> SymbolLayer::getTextRotationAlignment() const {
+PropertyValue<AlignmentType> SymbolLayer::getTextPitchAlignment() const {
+ return impl->layout.textPitchAlignment.get();
+}
+
+void SymbolLayer::setTextPitchAlignment(PropertyValue<AlignmentType> value) {
+ impl->layout.textPitchAlignment.set(value);
+}
+PropertyValue<AlignmentType> SymbolLayer::getTextRotationAlignment() const {
return impl->layout.textRotationAlignment.get();
}
-void SymbolLayer::setTextRotationAlignment(PropertyValue<RotationAlignmentType> value) {
+void SymbolLayer::setTextRotationAlignment(PropertyValue<AlignmentType> value) {
impl->layout.textRotationAlignment.set(value);
}
PropertyValue<std::string> SymbolLayer::getTextField() const {
@@ -274,112 +303,112 @@ PropertyValue<float> SymbolLayer::getIconOpacity() const {
return impl->paint.iconOpacity.get();
}
-void SymbolLayer::setIconOpacity(PropertyValue<float> value) {
- impl->paint.iconOpacity.set(value);
+void SymbolLayer::setIconOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.iconOpacity.set(value, klass);
}
PropertyValue<Color> SymbolLayer::getIconColor() const {
return impl->paint.iconColor.get();
}
-void SymbolLayer::setIconColor(PropertyValue<Color> value) {
- impl->paint.iconColor.set(value);
+void SymbolLayer::setIconColor(PropertyValue<Color> value, const optional<std::string>& klass) {
+ impl->paint.iconColor.set(value, klass);
}
PropertyValue<Color> SymbolLayer::getIconHaloColor() const {
return impl->paint.iconHaloColor.get();
}
-void SymbolLayer::setIconHaloColor(PropertyValue<Color> value) {
- impl->paint.iconHaloColor.set(value);
+void SymbolLayer::setIconHaloColor(PropertyValue<Color> value, const optional<std::string>& klass) {
+ impl->paint.iconHaloColor.set(value, klass);
}
PropertyValue<float> SymbolLayer::getIconHaloWidth() const {
return impl->paint.iconHaloWidth.get();
}
-void SymbolLayer::setIconHaloWidth(PropertyValue<float> value) {
- impl->paint.iconHaloWidth.set(value);
+void SymbolLayer::setIconHaloWidth(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.iconHaloWidth.set(value, klass);
}
PropertyValue<float> SymbolLayer::getIconHaloBlur() const {
return impl->paint.iconHaloBlur.get();
}
-void SymbolLayer::setIconHaloBlur(PropertyValue<float> value) {
- impl->paint.iconHaloBlur.set(value);
+void SymbolLayer::setIconHaloBlur(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.iconHaloBlur.set(value, klass);
}
PropertyValue<std::array<float, 2>> SymbolLayer::getIconTranslate() const {
return impl->paint.iconTranslate.get();
}
-void SymbolLayer::setIconTranslate(PropertyValue<std::array<float, 2>> value) {
- impl->paint.iconTranslate.set(value);
+void SymbolLayer::setIconTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
+ impl->paint.iconTranslate.set(value, klass);
}
PropertyValue<TranslateAnchorType> SymbolLayer::getIconTranslateAnchor() const {
return impl->paint.iconTranslateAnchor.get();
}
-void SymbolLayer::setIconTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
- impl->paint.iconTranslateAnchor.set(value);
+void SymbolLayer::setIconTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
+ impl->paint.iconTranslateAnchor.set(value, klass);
}
PropertyValue<float> SymbolLayer::getTextOpacity() const {
return impl->paint.textOpacity.get();
}
-void SymbolLayer::setTextOpacity(PropertyValue<float> value) {
- impl->paint.textOpacity.set(value);
+void SymbolLayer::setTextOpacity(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.textOpacity.set(value, klass);
}
PropertyValue<Color> SymbolLayer::getTextColor() const {
return impl->paint.textColor.get();
}
-void SymbolLayer::setTextColor(PropertyValue<Color> value) {
- impl->paint.textColor.set(value);
+void SymbolLayer::setTextColor(PropertyValue<Color> value, const optional<std::string>& klass) {
+ impl->paint.textColor.set(value, klass);
}
PropertyValue<Color> SymbolLayer::getTextHaloColor() const {
return impl->paint.textHaloColor.get();
}
-void SymbolLayer::setTextHaloColor(PropertyValue<Color> value) {
- impl->paint.textHaloColor.set(value);
+void SymbolLayer::setTextHaloColor(PropertyValue<Color> value, const optional<std::string>& klass) {
+ impl->paint.textHaloColor.set(value, klass);
}
PropertyValue<float> SymbolLayer::getTextHaloWidth() const {
return impl->paint.textHaloWidth.get();
}
-void SymbolLayer::setTextHaloWidth(PropertyValue<float> value) {
- impl->paint.textHaloWidth.set(value);
+void SymbolLayer::setTextHaloWidth(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.textHaloWidth.set(value, klass);
}
PropertyValue<float> SymbolLayer::getTextHaloBlur() const {
return impl->paint.textHaloBlur.get();
}
-void SymbolLayer::setTextHaloBlur(PropertyValue<float> value) {
- impl->paint.textHaloBlur.set(value);
+void SymbolLayer::setTextHaloBlur(PropertyValue<float> value, const optional<std::string>& klass) {
+ impl->paint.textHaloBlur.set(value, klass);
}
PropertyValue<std::array<float, 2>> SymbolLayer::getTextTranslate() const {
return impl->paint.textTranslate.get();
}
-void SymbolLayer::setTextTranslate(PropertyValue<std::array<float, 2>> value) {
- impl->paint.textTranslate.set(value);
+void SymbolLayer::setTextTranslate(PropertyValue<std::array<float, 2>> value, const optional<std::string>& klass) {
+ impl->paint.textTranslate.set(value, klass);
}
PropertyValue<TranslateAnchorType> SymbolLayer::getTextTranslateAnchor() const {
return impl->paint.textTranslateAnchor.get();
}
-void SymbolLayer::setTextTranslateAnchor(PropertyValue<TranslateAnchorType> value) {
- impl->paint.textTranslateAnchor.set(value);
+void SymbolLayer::setTextTranslateAnchor(PropertyValue<TranslateAnchorType> value, const optional<std::string>& klass) {
+ impl->paint.textTranslateAnchor.set(value, klass);
}
} // namespace style
diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp
index a4dc264ed2..0243b1dfa5 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.cpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.cpp
@@ -5,14 +5,6 @@
namespace mbgl {
namespace style {
-void SymbolLayer::Impl::parseLayout(const JSValue& value) {
- layout.parse(value);
-}
-
-void SymbolLayer::Impl::parsePaints(const JSValue& layer) {
- paint.parse(layer);
-}
-
void SymbolLayer::Impl::cascade(const CascadeParameters& parameters) {
paint.cascade(parameters);
}
@@ -26,8 +18,8 @@ bool SymbolLayer::Impl::recalculate(const CalculationParameters& parameters) {
iconSize = layout.iconSize;
textSize = layout.textSize;
- passes = ((paint.iconOpacity > 0 && (paint.iconColor.value[3] > 0 || paint.iconHaloColor.value[3] > 0) && iconSize > 0)
- || (paint.textOpacity > 0 && (paint.textColor.value[3] > 0 || paint.textHaloColor.value[3] > 0) && textSize > 0))
+ passes = ((paint.iconOpacity > 0 && (paint.iconColor.value.a > 0 || paint.iconHaloColor.value.a > 0) && iconSize > 0)
+ || (paint.textOpacity > 0 && (paint.textColor.value.a > 0 || paint.textHaloColor.value.a > 0) && textSize > 0))
? RenderPass::Translucent : RenderPass::None;
return hasTransitions;
@@ -45,8 +37,13 @@ std::unique_ptr<Bucket> SymbolLayer::Impl::createBucket(BucketParameters& parame
CalculationParameters p(parameters.tileID.overscaledZ);
bucket->layout.symbolPlacement.calculate(p);
if (bucket->layout.symbolPlacement.value == SymbolPlacementType::Line) {
- bucket->layout.iconRotationAlignment.value = RotationAlignmentType::Map;
- bucket->layout.textRotationAlignment.value = RotationAlignmentType::Map;
+ bucket->layout.iconRotationAlignment.value = AlignmentType::Map;
+ bucket->layout.textRotationAlignment.value = AlignmentType::Map;
+ };
+
+ // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment`
+ if (bucket->layout.textPitchAlignment.value == AlignmentType::Undefined) {
+ bucket->layout.textPitchAlignment.value = bucket->layout.textRotationAlignment.value;
};
bucket->layout.recalculate(p);
diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp
index 9727cc6480..7765d6790e 100644
--- a/src/mbgl/style/layers/symbol_layer_impl.hpp
+++ b/src/mbgl/style/layers/symbol_layer_impl.hpp
@@ -13,9 +13,7 @@ namespace style {
class SymbolLayer::Impl : public Layer::Impl {
public:
std::unique_ptr<Layer> clone() const override;
-
- void parseLayout(const JSValue&) override;
- void parsePaints(const JSValue&) override;
+ std::unique_ptr<Layer> cloneRef(const std::string& id) const override;
void cascade(const CascadeParameters&) override;
bool recalculate(const CalculationParameters&) override;
diff --git a/src/mbgl/style/layers/symbol_layer_properties.cpp b/src/mbgl/style/layers/symbol_layer_properties.cpp
index ce16ae2e50..59a73d3d59 100644
--- a/src/mbgl/style/layers/symbol_layer_properties.cpp
+++ b/src/mbgl/style/layers/symbol_layer_properties.cpp
@@ -5,40 +5,6 @@
namespace mbgl {
namespace style {
-void SymbolLayoutProperties::parse(const JSValue& value) {
- symbolPlacement.parse("symbol-placement", value);
- symbolSpacing.parse("symbol-spacing", value);
- symbolAvoidEdges.parse("symbol-avoid-edges", value);
- iconAllowOverlap.parse("icon-allow-overlap", value);
- iconIgnorePlacement.parse("icon-ignore-placement", value);
- iconOptional.parse("icon-optional", value);
- iconRotationAlignment.parse("icon-rotation-alignment", value);
- iconSize.parse("icon-size", value);
- iconImage.parse("icon-image", value);
- iconRotate.parse("icon-rotate", value);
- iconPadding.parse("icon-padding", value);
- iconKeepUpright.parse("icon-keep-upright", value);
- iconOffset.parse("icon-offset", value);
- textRotationAlignment.parse("text-rotation-alignment", value);
- textField.parse("text-field", value);
- textFont.parse("text-font", value);
- textSize.parse("text-size", value);
- textMaxWidth.parse("text-max-width", value);
- textLineHeight.parse("text-line-height", value);
- textLetterSpacing.parse("text-letter-spacing", value);
- textJustify.parse("text-justify", value);
- textAnchor.parse("text-anchor", value);
- textMaxAngle.parse("text-max-angle", value);
- textRotate.parse("text-rotate", value);
- textPadding.parse("text-padding", value);
- textKeepUpright.parse("text-keep-upright", value);
- textTransform.parse("text-transform", value);
- textOffset.parse("text-offset", value);
- textAllowOverlap.parse("text-allow-overlap", value);
- textIgnorePlacement.parse("text-ignore-placement", value);
- textOptional.parse("text-optional", value);
-}
-
void SymbolLayoutProperties::recalculate(const CalculationParameters& parameters) {
symbolPlacement.calculate(parameters);
symbolSpacing.calculate(parameters);
@@ -48,11 +14,14 @@ void SymbolLayoutProperties::recalculate(const CalculationParameters& parameters
iconOptional.calculate(parameters);
iconRotationAlignment.calculate(parameters);
iconSize.calculate(parameters);
+ iconTextFit.calculate(parameters);
+ iconTextFitPadding.calculate(parameters);
iconImage.calculate(parameters);
iconRotate.calculate(parameters);
iconPadding.calculate(parameters);
iconKeepUpright.calculate(parameters);
iconOffset.calculate(parameters);
+ textPitchAlignment.calculate(parameters);
textRotationAlignment.calculate(parameters);
textField.calculate(parameters);
textFont.calculate(parameters);
@@ -73,23 +42,6 @@ void SymbolLayoutProperties::recalculate(const CalculationParameters& parameters
textOptional.calculate(parameters);
}
-void SymbolPaintProperties::parse(const JSValue& value) {
- iconOpacity.parse("icon-opacity", value);
- iconColor.parse("icon-color", value);
- iconHaloColor.parse("icon-halo-color", value);
- iconHaloWidth.parse("icon-halo-width", value);
- iconHaloBlur.parse("icon-halo-blur", value);
- iconTranslate.parse("icon-translate", value);
- iconTranslateAnchor.parse("icon-translate-anchor", value);
- textOpacity.parse("text-opacity", value);
- textColor.parse("text-color", value);
- textHaloColor.parse("text-halo-color", value);
- textHaloWidth.parse("text-halo-width", value);
- textHaloBlur.parse("text-halo-blur", value);
- textTranslate.parse("text-translate", value);
- textTranslateAnchor.parse("text-translate-anchor", value);
-}
-
void SymbolPaintProperties::cascade(const CascadeParameters& parameters) {
iconOpacity.cascade(parameters);
iconColor.cascade(parameters);
diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp
index 38455b5cac..e4560ab486 100644
--- a/src/mbgl/style/layers/symbol_layer_properties.hpp
+++ b/src/mbgl/style/layers/symbol_layer_properties.hpp
@@ -2,9 +2,9 @@
#pragma once
+#include <mbgl/style/types.hpp>
#include <mbgl/style/layout_property.hpp>
#include <mbgl/style/paint_property.hpp>
-#include <mbgl/util/rapidjson.hpp>
namespace mbgl {
namespace style {
@@ -14,7 +14,6 @@ class CalculationParameters;
class SymbolLayoutProperties {
public:
- void parse(const JSValue&);
void recalculate(const CalculationParameters&);
LayoutProperty<SymbolPlacementType> symbolPlacement { SymbolPlacementType::Point };
@@ -23,14 +22,17 @@ public:
LayoutProperty<bool> iconAllowOverlap { false };
LayoutProperty<bool> iconIgnorePlacement { false };
LayoutProperty<bool> iconOptional { false };
- LayoutProperty<RotationAlignmentType> iconRotationAlignment { RotationAlignmentType::Viewport };
+ LayoutProperty<AlignmentType> iconRotationAlignment { AlignmentType::Viewport };
LayoutProperty<float> iconSize { 1 };
+ LayoutProperty<IconTextFitType> iconTextFit { IconTextFitType::None };
+ LayoutProperty<std::array<float, 4>> iconTextFitPadding { {{ 0, 0, 0, 0 }} };
LayoutProperty<std::string> iconImage { "" };
LayoutProperty<float> iconRotate { 0 };
LayoutProperty<float> iconPadding { 2 };
LayoutProperty<bool> iconKeepUpright { false };
LayoutProperty<std::array<float, 2>> iconOffset { {{ 0, 0 }} };
- LayoutProperty<RotationAlignmentType> textRotationAlignment { RotationAlignmentType::Viewport };
+ LayoutProperty<AlignmentType> textPitchAlignment { AlignmentType::Undefined };
+ LayoutProperty<AlignmentType> textRotationAlignment { AlignmentType::Viewport };
LayoutProperty<std::string> textField { "" };
LayoutProperty<std::vector<std::string>> textFont { { "Open Sans Regular", "Arial Unicode MS Regular" } };
LayoutProperty<float> textSize { 16 };
@@ -52,20 +54,19 @@ public:
class SymbolPaintProperties {
public:
- void parse(const JSValue&);
void cascade(const CascadeParameters&);
bool recalculate(const CalculationParameters&);
PaintProperty<float> iconOpacity { 1 };
- PaintProperty<Color> iconColor { {{ 0, 0, 0, 1 }} };
- PaintProperty<Color> iconHaloColor { {{ 0, 0, 0, 0 }} };
+ PaintProperty<Color> iconColor { Color::black() };
+ PaintProperty<Color> iconHaloColor { {} };
PaintProperty<float> iconHaloWidth { 0 };
PaintProperty<float> iconHaloBlur { 0 };
PaintProperty<std::array<float, 2>> iconTranslate { {{ 0, 0 }} };
PaintProperty<TranslateAnchorType> iconTranslateAnchor { TranslateAnchorType::Map };
PaintProperty<float> textOpacity { 1 };
- PaintProperty<Color> textColor { {{ 0, 0, 0, 1 }} };
- PaintProperty<Color> textHaloColor { {{ 0, 0, 0, 0 }} };
+ PaintProperty<Color> textColor { Color::black() };
+ PaintProperty<Color> textHaloColor { {} };
PaintProperty<float> textHaloWidth { 0 };
PaintProperty<float> textHaloBlur { 0 };
PaintProperty<std::array<float, 2>> textTranslate { {{ 0, 0 }} };
diff --git a/src/mbgl/style/layout_property.hpp b/src/mbgl/style/layout_property.hpp
index f5045b47fc..db1a1ebf28 100644
--- a/src/mbgl/style/layout_property.hpp
+++ b/src/mbgl/style/layout_property.hpp
@@ -3,7 +3,6 @@
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/property_parsing.hpp>
#include <mbgl/style/property_evaluator.hpp>
-#include <mbgl/util/rapidjson.hpp>
#include <utility>
@@ -25,12 +24,6 @@ public:
currentValue = value_;
}
- void parse(const char * name, const JSValue& layout) {
- if (layout.HasMember(name)) {
- currentValue = parseProperty<T>(name, layout[name]);
- }
- }
-
void calculate(const CalculationParameters& parameters) {
if (currentValue) {
PropertyEvaluator<T> evaluator(parameters, defaultValue);
diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp
index c19f58904f..2c48114669 100644
--- a/src/mbgl/style/observer.hpp
+++ b/src/mbgl/style/observer.hpp
@@ -15,10 +15,10 @@ public:
* In addition to the individual glyph, sprite, and source events, the
* following "rollup" events are provided for convenience. They are
* strictly additive; e.g. when a source is loaded, both `onSourceLoaded`
- * and `onResourceLoaded` will be called.
+ * and `onNeedsRepaint` will be called.
*/
- virtual void onResourceLoaded() {};
- virtual void onResourceError(std::exception_ptr) {};
+ void onNeedsRepaint() override {}
+ virtual void onResourceError(std::exception_ptr) {}
};
} // namespace style
diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp
index 62fd59684e..6da1ed53b4 100644
--- a/src/mbgl/style/paint_property.hpp
+++ b/src/mbgl/style/paint_property.hpp
@@ -6,8 +6,8 @@
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/cascade_parameters.hpp>
#include <mbgl/style/calculation_parameters.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/util/interpolate.hpp>
-#include <mbgl/util/std.hpp>
#include <mbgl/util/rapidjson.hpp>
#include <map>
@@ -27,47 +27,34 @@ public:
}
PaintProperty(const PaintProperty& other)
- : values(other.values),
+ : defaultValue(other.defaultValue),
+ values(other.values),
transitions(other.transitions) {
}
- const PropertyValue<T>& get() const {
- return values.at(ClassID::Default);
+ PaintProperty& operator=(const PaintProperty& other) {
+ defaultValue = other.defaultValue;
+ values = other.values;
+ transitions = other.transitions;
+ return *this;
}
- void set(const PropertyValue<T>& value_) {
- values.emplace(ClassID::Default, value_);
+ bool isUndefined() const {
+ return values.find(ClassID::Default) == values.end();
}
- void parse(const char* name, const JSValue& layer) {
- mbgl::util::erase_if(values, [] (const auto& p) { return p.first != ClassID::Fallback; });
-
- std::string transitionName = { name };
- transitionName += "-transition";
-
- for (auto it = layer.MemberBegin(); it != layer.MemberEnd(); ++it) {
- const std::string paintName { it->name.GetString(), it->name.GetStringLength() };
- if (paintName.compare(0, 5, "paint") != 0)
- continue;
-
- bool isClass = paintName.compare(0, 6, "paint.") == 0;
- if (isClass && paintName.length() <= 6)
- continue;
-
- ClassID classID = isClass ? ClassDictionary::Get().lookup(paintName.substr(6)) : ClassID::Default;
+ const PropertyValue<T>& get() const {
+ static const PropertyValue<T> staticValue;
+ const auto it = values.find(ClassID::Default);
+ return it == values.end() ? staticValue : it->second;
+ }
- if (it->value.HasMember(name)) {
- if (auto v = parseProperty<T>(name, it->value[name])) {
- values.emplace(classID, v);
- }
- }
+ void set(const PropertyValue<T>& value_, const optional<std::string>& klass) {
+ values[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = value_;
+ }
- if (it->value.HasMember(transitionName.c_str())) {
- if (auto v = parseTransitionOptions(name, it->value[transitionName.c_str()])) {
- transitions.emplace(classID, *v);
- }
- }
- }
+ void setTransition(const TransitionOptions& transition, const optional<std::string>& klass) {
+ transitions[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = transition;
}
void cascade(const CascadeParameters& params) {
@@ -118,24 +105,24 @@ private:
TimePoint end_,
PropertyValue<T> value_)
: prior(std::move(prior_)),
- begin(begin_),
- end(end_),
+ begin(std::move(begin_)),
+ end(std::move(end_)),
value(std::move(value_)) {
}
Result calculate(const Evaluator<T>& evaluator, const TimePoint& now) {
- Result final = PropertyValue<T>::visit(value, evaluator);
+ Result finalValue = PropertyValue<T>::visit(value, evaluator);
if (!prior) {
// No prior value.
- return final;
+ return finalValue;
} else if (now >= end) {
// Transition from prior value is now complete.
prior.reset();
- return final;
+ return finalValue;
} else {
// Interpolate between recursively-calculated prior value and final.
float t = std::chrono::duration<float>(now - begin) / (end - begin);
- return util::interpolate(prior->calculate(evaluator, now), final, t);
+ return util::interpolate(prior->calculate(evaluator, now), finalValue, util::DEFAULT_TRANSITION_EASE.solve(t, 0.001));
}
}
diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp
index 28a7d2e81b..2746b8af92 100644
--- a/src/mbgl/style/parser.cpp
+++ b/src/mbgl/style/parser.cpp
@@ -1,107 +1,23 @@
#include <mbgl/style/parser.hpp>
#include <mbgl/style/layer_impl.hpp>
-#include <mbgl/style/layers/fill_layer.hpp>
-#include <mbgl/style/layers/line_layer.hpp>
-#include <mbgl/style/layers/circle_layer.hpp>
-#include <mbgl/style/layers/symbol_layer.hpp>
-#include <mbgl/style/layers/raster_layer.hpp>
-#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/source.hpp>
+#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/platform/log.hpp>
#include <mapbox/geojsonvt.hpp>
-#include <mapbox/geojsonvt/convert.hpp>
-
-#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/util/mapbox.hpp>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <algorithm>
-#include <sstream>
#include <set>
namespace mbgl {
namespace style {
-namespace {
-
-void parseTileJSONMember(const JSValue& value, std::vector<std::string>& target, const char* name) {
- if (!value.HasMember(name)) {
- return;
- }
-
- const JSValue& property = value[name];
- if (!property.IsArray()) {
- return;
- }
-
- for (rapidjson::SizeType i = 0; i < property.Size(); i++) {
- if (!property[i].IsString()) {
- return;
- }
- }
-
- for (rapidjson::SizeType i = 0; i < property.Size(); i++) {
- target.emplace_back(std::string(property[i].GetString(), property[i].GetStringLength()));
- }
-}
-
-void parseTileJSONMember(const JSValue& value, std::string& target, const char* name) {
- if (!value.HasMember(name)) {
- return;
- }
-
- const JSValue& property = value[name];
- if (!property.IsString()) {
- return;
- }
-
- target = { property.GetString(), property.GetStringLength() };
-}
-
-void parseTileJSONMember(const JSValue& value, uint8_t& target, const char* name) {
- if (!value.HasMember(name)) {
- return;
- }
-
- const JSValue& property = value[name];
- if (!property.IsUint()) {
- return;
- }
-
- unsigned int uint = property.GetUint();
- if (uint > std::numeric_limits<uint8_t>::max()) {
- return;
- }
-
- target = uint;
-}
-
-void parseTileJSONMember(const JSValue& value, std::array<double, 4>& target, const char* name) {
- if (!value.HasMember(name)) {
- return;
- }
-
- const JSValue& property = value[name];
- if (!property.IsArray() || property.Size() > 4) {
- return;
- }
-
- for (rapidjson::SizeType i = 0; i < property.Size(); i++) {
- if (!property[i].IsNumber()) {
- return;
- }
- }
-
- for (rapidjson::SizeType i = 0; i < property.Size(); i++) {
- target[i] = property[i].GetDouble();
- }
-}
-
-} // end namespace
-
Parser::~Parser() = default;
void Parser::parse(const std::string& json) {
@@ -113,8 +29,14 @@ void Parser::parse(const std::string& json) {
return;
}
+ if (!document.IsObject()) {
+ Log::Error(Event::ParseStyle, "Style JSON must be an object");
+ return;
+ }
+
if (document.HasMember("version")) {
- int version = document["version"].GetInt();
+ const JSValue& versionValue = document["version"];
+ const int version = versionValue.IsNumber() ? versionValue.GetInt() : 0;
if (version != 8) {
Log::Warning(Event::ParseStyle, "current renderer implementation only supports style spec version 8; using an outdated style will cause rendering errors");
}
@@ -149,154 +71,19 @@ void Parser::parseSources(const JSValue& value) {
return;
}
- JSValue::ConstMemberIterator itr = value.MemberBegin();
- for (; itr != value.MemberEnd(); ++itr) {
- const JSValue& nameVal = itr->name;
- const JSValue& sourceVal = itr->value;
-
- if (!sourceVal.HasMember("type")) {
- Log::Warning(Event::ParseStyle, "source must have a type");
- continue;
- }
+ for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
+ std::string id = *conversion::toString(it->name);
- const JSValue& typeVal = sourceVal["type"];
- if (!typeVal.IsString()) {
- Log::Warning(Event::ParseStyle, "source type must have one of the enum values");
+ conversion::Result<std::unique_ptr<Source>> source
+ = conversion::convert<std::unique_ptr<Source>>(it->value, id);
+ if (!source) {
+ Log::Warning(Event::ParseStyle, source.error().message);
continue;
}
- const auto type = SourceTypeClass({ typeVal.GetString(), typeVal.GetStringLength() });
-
- // Sources can have URLs, either because they reference an external TileJSON file, or
- // because reference a GeoJSON file. They don't have to have one though when all source
- // parameters are specified inline.
- std::string url;
-
- uint16_t tileSize = util::tileSize;
-
- std::unique_ptr<Tileset> tileset;
- std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> geojsonvt;
-
- switch (type) {
- case SourceType::Raster:
- if (sourceVal.HasMember("tileSize")) {
- const JSValue& tileSizeVal = sourceVal["tileSize"];
- if (tileSizeVal.IsNumber() && tileSizeVal.GetUint64() <= std::numeric_limits<uint16_t>::max()) {
- tileSize = tileSizeVal.GetUint64();
- } else {
- Log::Error(Event::ParseStyle, "invalid tileSize");
- continue;
- }
- }
- // Fall through. Vector sources are forbidden from having a tileSize.
-
- case SourceType::Vector:
- if (sourceVal.HasMember("url")) {
- const JSValue& urlVal = sourceVal["url"];
- if (urlVal.IsString()) {
- url = { urlVal.GetString(), urlVal.GetStringLength() };
- } else {
- Log::Error(Event::ParseStyle, "source url must be a string");
- continue;
- }
- } else {
- tileset = parseTileJSON(sourceVal);
- }
- break;
-
- case SourceType::GeoJSON:
- tileset = std::make_unique<Tileset>();
-
- // We should probably split this up to have URLs in the url property, and actual data
- // in the data property. Until then, we're going to detect the content based on the
- // object type.
- if (sourceVal.HasMember("data")) {
- const JSValue& dataVal = sourceVal["data"];
- if (dataVal.IsString()) {
- // We need to load an external GeoJSON file
- url = { dataVal.GetString(), dataVal.GetStringLength() };
- } else if (dataVal.IsObject()) {
- // We need to parse dataVal as a GeoJSON object
- geojsonvt = parseGeoJSON(dataVal);
- tileset->maxZoom = geojsonvt->options.maxZoom;
- } else {
- Log::Error(Event::ParseStyle, "GeoJSON data must be a URL or an object");
- continue;
- }
- } else {
- Log::Error(Event::ParseStyle, "GeoJSON source must have a data value");
- continue;
- }
-
- break;
-
- default:
- Log::Error(Event::ParseStyle, "source type '%s' is not supported", typeVal.GetString());
- continue;
- }
-
- const std::string id { nameVal.GetString(), nameVal.GetStringLength() };
- std::unique_ptr<Source> source = std::make_unique<Source>(type, id, url, tileSize, std::move(tileset), std::move(geojsonvt));
-
- sourcesMap.emplace(id, source.get());
- sources.emplace_back(std::move(source));
- }
-}
-
-std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> parseGeoJSON(const JSValue& value) {
- using namespace mapbox::geojsonvt;
-
- Options options;
- options.buffer = util::EXTENT / util::tileSize * 128;
- options.extent = util::EXTENT;
-
- try {
- return std::make_unique<GeoJSONVT>(Convert::convert(value, 0), options);
- } catch (const std::exception& ex) {
- Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", ex.what());
- // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for
- // tiles to load.
- return std::make_unique<GeoJSONVT>(std::vector<ProjectedFeature>{}, options);
- }
-}
-
-std::unique_ptr<Tileset> parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType type, uint16_t tileSize) {
- rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> document;
- document.Parse<0>(json.c_str());
-
- if (document.HasParseError()) {
- std::stringstream message;
- message << document.GetErrorOffset() << " - " << rapidjson::GetParseError_En(document.GetParseError());
- throw std::runtime_error(message.str());
- }
-
- std::unique_ptr<Tileset> result = parseTileJSON(document);
-
- // TODO: Remove this hack by delivering proper URLs in the TileJSON to begin with.
- if (util::mapbox::isMapboxURL(sourceURL)) {
- for (auto& url : result->tiles) {
- url = util::mapbox::canonicalizeTileURL(url, type, tileSize);
- }
+ sourcesMap.emplace(id, (*source).get());
+ sources.emplace_back(std::move(*source));
}
-
- return result;
-}
-
-std::unique_ptr<Tileset> parseTileJSON(const JSValue& value) {
- auto tileset = std::make_unique<Tileset>();
- parseTileJSONMember(value, tileset->tiles, "tiles");
- parseTileJSONMember(value, tileset->minZoom, "minzoom");
- parseTileJSONMember(value, tileset->maxZoom, "maxzoom");
- parseTileJSONMember(value, tileset->attribution, "attribution");
-
- std::array<double, 4> array;
- parseTileJSONMember(value, array, "center");
- tileset->center = { array[0], array[1] };
- tileset->zoom = array[2];
- parseTileJSONMember(value, array, "bounds");
- tileset->bounds = LatLngBounds::hull({ array[0], array[1] }, { array[2], array[3] });
-
- return tileset;
}
void Parser::parseLayers(const JSValue& value) {
@@ -392,275 +179,15 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique
return;
}
- layer = reference->copy(id, ref);
- layer->baseImpl->parsePaints(value);
+ layer = reference->baseImpl->cloneRef(id);
+ conversion::setPaintProperties(*layer, value);
} else {
- // Otherwise, parse the source/source-layer/filter/render keys to form the bucket.
- if (!value.HasMember("type")) {
- Log::Warning(Event::ParseStyle, "layer '%s' is missing a type", id.c_str());
- return;
- }
-
- const JSValue& typeVal = value["type"];
- if (!typeVal.IsString()) {
- Log::Warning(Event::ParseStyle, "layer '%s' has an invalid type", id.c_str());
+ conversion::Result<std::unique_ptr<Layer>> converted = conversion::convert<std::unique_ptr<Layer>>(value);
+ if (!converted) {
+ Log::Warning(Event::ParseStyle, converted.error().message);
return;
}
-
- std::string type { typeVal.GetString(), typeVal.GetStringLength() };
-
- if (type == "fill") {
- layer = std::make_unique<FillLayer>(id);
- } else if (type == "line") {
- layer = std::make_unique<LineLayer>(id);
- } else if (type == "circle") {
- layer = std::make_unique<CircleLayer>(id);
- } else if (type == "symbol") {
- layer = std::make_unique<SymbolLayer>(id);
- } else if (type == "raster") {
- layer = std::make_unique<RasterLayer>(id);
- } else if (type == "background") {
- layer = std::make_unique<BackgroundLayer>(id);
- } else {
- Log::Warning(Event::ParseStyle, "unknown type '%s' for layer '%s'", type.c_str(), id.c_str());
- return;
- }
-
- Layer::Impl* impl = layer->baseImpl.get();
-
- if (value.HasMember("source")) {
- const JSValue& value_source = value["source"];
- if (value_source.IsString()) {
- impl->source = { value_source.GetString(), value_source.GetStringLength() };
- auto source_it = sourcesMap.find(impl->source);
- if (source_it == sourcesMap.end()) {
- Log::Warning(Event::ParseStyle, "can't find source '%s' required for layer '%s'", impl->source.c_str(), impl->id.c_str());
- }
- } else {
- Log::Warning(Event::ParseStyle, "source of layer '%s' must be a string", impl->id.c_str());
- }
- }
-
- if (value.HasMember("source-layer")) {
- const JSValue& value_source_layer = value["source-layer"];
- if (value_source_layer.IsString()) {
- impl->sourceLayer = { value_source_layer.GetString(), value_source_layer.GetStringLength() };
- } else {
- Log::Warning(Event::ParseStyle, "source-layer of layer '%s' must be a string", impl->id.c_str());
- }
- }
-
- if (value.HasMember("filter")) {
- impl->filter = parseFilter(value["filter"]);
- }
-
- if (value.HasMember("minzoom")) {
- const JSValue& min_zoom = value["minzoom"];
- if (min_zoom.IsNumber()) {
- impl->minZoom = min_zoom.GetDouble();
- } else {
- Log::Warning(Event::ParseStyle, "minzoom of layer %s must be numeric", impl->id.c_str());
- }
- }
-
- if (value.HasMember("maxzoom")) {
- const JSValue& max_zoom = value["maxzoom"];
- if (max_zoom.IsNumber()) {
- impl->maxZoom = max_zoom.GetDouble();
- } else {
- Log::Warning(Event::ParseStyle, "maxzoom of layer %s must be numeric", impl->id.c_str());
- }
- }
-
- if (value.HasMember("layout")) {
- parseVisibility(*layer, value["layout"]);
- impl->parseLayout(value["layout"]);
- }
-
- impl->parsePaints(value);
- }
-}
-
-MBGL_DEFINE_ENUM_CLASS(VisibilityTypeClass, VisibilityType, {
- { VisibilityType::Visible, "visible" },
- { VisibilityType::None, "none" },
-});
-
-void Parser::parseVisibility(Layer& layer, const JSValue& value) {
- Layer::Impl& impl = *layer.baseImpl;
- if (!value.HasMember("visibility")) {
- return;
- } else if (!value["visibility"].IsString()) {
- Log::Warning(Event::ParseStyle, "value of 'visibility' must be a string");
- impl.visibility = VisibilityType::Visible;
- return;
- }
- impl.visibility = VisibilityTypeClass({ value["visibility"].GetString(), value["visibility"].GetStringLength() });
-}
-
-Value parseFeatureType(const Value& value) {
- if (value == std::string("Point")) {
- return Value(uint64_t(FeatureType::Point));
- } else if (value == std::string("LineString")) {
- return Value(uint64_t(FeatureType::LineString));
- } else if (value == std::string("Polygon")) {
- return Value(uint64_t(FeatureType::Polygon));
- } else {
- Log::Warning(Event::ParseStyle, "value for $type filter must be Point, LineString, or Polygon");
- return Value(uint64_t(FeatureType::Unknown));
- }
-}
-
-Value parseValue(const JSValue& value) {
- switch (value.GetType()) {
- case rapidjson::kNullType:
- case rapidjson::kFalseType:
- return false;
-
- case rapidjson::kTrueType:
- return true;
-
- case rapidjson::kStringType:
- return std::string { value.GetString(), value.GetStringLength() };
-
- case rapidjson::kNumberType:
- if (value.IsUint64()) return value.GetUint64();
- if (value.IsInt64()) return value.GetInt64();
- return value.GetDouble();
-
- default:
- return false;
- }
-}
-
-template <class Expression>
-Filter parseUnaryFilter(const JSValue& value) {
- Filter empty;
-
- if (value.Size() < 2) {
- Log::Warning(Event::ParseStyle, "filter expression must have 2 elements");
- return empty;
- }
-
- if (!value[1u].IsString()) {
- Log::Warning(Event::ParseStyle, "filter expression key must be a string");
- return empty;
- }
-
- Expression expression;
- expression.key = { value[1u].GetString(), value[1u].GetStringLength() };
- return expression;
-}
-
-template <class Expression>
-Filter parseBinaryFilter(const JSValue& value) {
- Filter empty;
-
- if (value.Size() < 3) {
- Log::Warning(Event::ParseStyle, "filter expression must have 3 elements");
- return empty;
- }
-
- if (!value[1u].IsString()) {
- Log::Warning(Event::ParseStyle, "filter expression key must be a string");
- return empty;
- }
-
- Expression expression;
- expression.key = { value[1u].GetString(), value[1u].GetStringLength() };
- expression.value = parseValue(value[2u]);
-
- if (expression.key == "$type") {
- expression.value = parseFeatureType(expression.value);
- }
-
- return expression;
-}
-
-template <class Expression>
-Filter parseSetFilter(const JSValue& value) {
- Filter empty;
-
- if (value.Size() < 2) {
- Log::Warning(Event::ParseStyle, "filter expression must at least 2 elements");
- return empty;
- }
-
- if (!value[1u].IsString()) {
- Log::Warning(Event::ParseStyle, "filter expression key must be a string");
- return empty;
- }
-
- Expression expression;
- expression.key = { value[1u].GetString(), value[1u].GetStringLength() };
- for (rapidjson::SizeType i = 2; i < value.Size(); ++i) {
- Value parsedValue = parseValue(value[i]);
- if (expression.key == "$type") {
- parsedValue = parseFeatureType(parsedValue);
- }
- expression.values.push_back(parsedValue);
- }
- return expression;
-}
-
-template <class Expression>
-Filter parseCompoundFilter(const JSValue& value) {
- Expression expression;
- for (rapidjson::SizeType i = 1; i < value.Size(); ++i) {
- expression.filters.push_back(parseFilter(value[i]));
- }
- return expression;
-}
-
-Filter parseFilter(const JSValue& value) {
- Filter empty;
-
- if (!value.IsArray()) {
- Log::Warning(Event::ParseStyle, "filter expression must be an array");
- return empty;
- }
-
- if (value.Size() < 1) {
- Log::Warning(Event::ParseStyle, "filter expression must have at least 1 element");
- return empty;
- }
-
- if (!value[0u].IsString()) {
- Log::Warning(Event::ParseStyle, "filter operator must be a string");
- return empty;
- }
-
- std::string op = { value[0u].GetString(), value[0u].GetStringLength() };
-
- if (op == "==") {
- return parseBinaryFilter<EqualsFilter>(value);
- } else if (op == "!=") {
- return parseBinaryFilter<NotEqualsFilter>(value);
- } else if (op == ">") {
- return parseBinaryFilter<GreaterThanFilter>(value);
- } else if (op == ">=") {
- return parseBinaryFilter<GreaterThanEqualsFilter>(value);
- } else if (op == "<") {
- return parseBinaryFilter<LessThanFilter>(value);
- } else if (op == "<=") {
- return parseBinaryFilter<LessThanEqualsFilter>(value);
- } else if (op == "in") {
- return parseSetFilter<InFilter>(value);
- } else if (op == "!in") {
- return parseSetFilter<NotInFilter>(value);
- } else if (op == "all") {
- return parseCompoundFilter<AllFilter>(value);
- } else if (op == "any") {
- return parseCompoundFilter<AnyFilter>(value);
- } else if (op == "none") {
- return parseCompoundFilter<NoneFilter>(value);
- } else if (op == "has") {
- return parseUnaryFilter<HasFilter>(value);
- } else if (op == "!has") {
- return parseUnaryFilter<NotHasFilter>(value);
- } else {
- Log::Warning(Event::ParseStyle, "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"");
- return empty;
+ layer = std::move(*converted);
}
}
diff --git a/src/mbgl/style/parser.hpp b/src/mbgl/style/parser.hpp
index ea821fabde..faf80cee93 100644
--- a/src/mbgl/style/parser.hpp
+++ b/src/mbgl/style/parser.hpp
@@ -2,7 +2,6 @@
#include <mbgl/style/layer.hpp>
#include <mbgl/style/source.hpp>
-#include <mbgl/style/filter.hpp>
#include <mbgl/util/rapidjson.hpp>
#include <mbgl/util/font_stack.hpp>
@@ -16,13 +15,6 @@
namespace mbgl {
namespace style {
-std::unique_ptr<Tileset> parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType, uint16_t tileSize);
-std::unique_ptr<Tileset> parseTileJSON(const JSValue&);
-
-std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> parseGeoJSON(const JSValue&);
-
-Filter parseFilter(const JSValue&);
-
class Parser {
public:
~Parser();
@@ -42,7 +34,6 @@ private:
void parseSources(const JSValue&);
void parseLayers(const JSValue&);
void parseLayer(const std::string& id, const JSValue&, std::unique_ptr<Layer>&);
- void parseVisibility(Layer&, const JSValue& value);
std::unordered_map<std::string, const Source*> sourcesMap;
std::unordered_map<std::string, std::pair<const JSValue&, std::unique_ptr<Layer>>> layersMap;
diff --git a/src/mbgl/style/property_evaluator.cpp b/src/mbgl/style/property_evaluator.cpp
index d2e633c782..abb3681efa 100644
--- a/src/mbgl/style/property_evaluator.cpp
+++ b/src/mbgl/style/property_evaluator.cpp
@@ -11,25 +11,28 @@ namespace mbgl {
namespace style {
template <typename T>
-inline T defaultStopsValue();
-
-template <> inline bool defaultStopsValue() { return true; }
-template <> inline float defaultStopsValue() { return 1.0f; }
-template <> inline Color defaultStopsValue() { return {{ 0, 0, 0, 1 }}; }
-template <> inline std::vector<float> defaultStopsValue() { return {{ 1, 0 }}; }
-template <> inline std::vector<std::string> defaultStopsValue() { return {{}}; }
-template <> inline std::array<float, 2> defaultStopsValue() { return {{ 0, 0 }}; }
-
-template <> inline std::string defaultStopsValue() { return {}; }
-template <> inline TranslateAnchorType defaultStopsValue() { return {}; };
-template <> inline RotateAnchorType defaultStopsValue() { return {}; };
-template <> inline LineCapType defaultStopsValue() { return {}; };
-template <> inline LineJoinType defaultStopsValue() { return {}; };
-template <> inline SymbolPlacementType defaultStopsValue() { return {}; };
-template <> inline TextAnchorType defaultStopsValue() { return {}; };
-template <> inline TextJustifyType defaultStopsValue() { return {}; };
-template <> inline TextTransformType defaultStopsValue() { return {}; };
-template <> inline RotationAlignmentType defaultStopsValue() { return {}; };
+T defaultStopsValue();
+
+template <> bool defaultStopsValue() { return true; }
+template <> float defaultStopsValue() { return 1.0f; }
+template <> Color defaultStopsValue() { return { 0, 0, 0, 1 }; }
+template <> std::vector<float> defaultStopsValue() { return {{ 1, 0 }}; }
+template <> std::vector<std::string> defaultStopsValue() { return {{}}; }
+template <> std::array<float, 2> defaultStopsValue() { return {{ 0, 0 }}; }
+template <> std::array<float, 4> defaultStopsValue() { return {{ 0, 0, 0, 0 }}; }
+
+template <> std::string defaultStopsValue() { return {}; }
+template <> TranslateAnchorType defaultStopsValue() { return {}; }
+template <> RotateAnchorType defaultStopsValue() { return {}; }
+template <> CirclePitchScaleType defaultStopsValue() { return {}; }
+template <> LineCapType defaultStopsValue() { return {}; }
+template <> LineJoinType defaultStopsValue() { return {}; }
+template <> SymbolPlacementType defaultStopsValue() { return {}; }
+template <> TextAnchorType defaultStopsValue() { return {}; }
+template <> TextJustifyType defaultStopsValue() { return {}; }
+template <> TextTransformType defaultStopsValue() { return {}; }
+template <> AlignmentType defaultStopsValue() { return {}; }
+template <> IconTextFitType defaultStopsValue() { return {}; };
template <typename T>
T PropertyEvaluator<T>::operator()(const Function<T>& fn) const {
@@ -87,17 +90,20 @@ template class PropertyEvaluator<Color>;
template class PropertyEvaluator<std::vector<float>>;
template class PropertyEvaluator<std::vector<std::string>>;
template class PropertyEvaluator<std::array<float, 2>>;
+template class PropertyEvaluator<std::array<float, 4>>;
template class PropertyEvaluator<std::string>;
template class PropertyEvaluator<TranslateAnchorType>;
template class PropertyEvaluator<RotateAnchorType>;
+template class PropertyEvaluator<CirclePitchScaleType>;
template class PropertyEvaluator<LineCapType>;
template class PropertyEvaluator<LineJoinType>;
template class PropertyEvaluator<SymbolPlacementType>;
template class PropertyEvaluator<TextAnchorType>;
template class PropertyEvaluator<TextJustifyType>;
template class PropertyEvaluator<TextTransformType>;
-template class PropertyEvaluator<RotationAlignmentType>;
+template class PropertyEvaluator<AlignmentType>;
+template class PropertyEvaluator<IconTextFitType>;
template <typename T>
Faded<T> CrossFadedPropertyEvaluator<T>::operator()(const Undefined&) const {
@@ -110,7 +116,7 @@ Faded<T> CrossFadedPropertyEvaluator<T>::operator()(const T& constant) const {
}
template <typename T>
-inline T getBiggestStopLessThan(const Function<T>& function, float z) {
+T getBiggestStopLessThan(const Function<T>& function, float z) {
const auto& stops = function.getStops();
for (uint32_t i = 0; i < stops.size(); i++) {
if (stops[i].first > z) {
diff --git a/src/mbgl/style/property_evaluator.hpp b/src/mbgl/style/property_evaluator.hpp
index 77100bda46..a0bce2f499 100644
--- a/src/mbgl/style/property_evaluator.hpp
+++ b/src/mbgl/style/property_evaluator.hpp
@@ -13,9 +13,9 @@ class PropertyEvaluator {
public:
using ResultType = T;
- PropertyEvaluator(const CalculationParameters& parameters_, const T& defaultValue_)
+ PropertyEvaluator(const CalculationParameters& parameters_, T defaultValue_)
: parameters(parameters_),
- defaultValue(defaultValue_) {}
+ defaultValue(std::move(defaultValue_)) {}
T operator()(const Undefined&) const { return defaultValue; }
T operator()(const T& constant) const { return constant; }
@@ -40,9 +40,9 @@ class CrossFadedPropertyEvaluator {
public:
using ResultType = Faded<T>;
- CrossFadedPropertyEvaluator(const CalculationParameters& parameters_, const T& defaultValue_)
+ CrossFadedPropertyEvaluator(const CalculationParameters& parameters_, T defaultValue_)
: parameters(parameters_),
- defaultValue(defaultValue_) {}
+ defaultValue(std::move(defaultValue_)) {}
Faded<T> operator()(const Undefined&) const;
Faded<T> operator()(const T& constant) const;
@@ -61,6 +61,6 @@ namespace util {
template <typename T>
struct Interpolator<style::Faded<T>>
: Uninterpolated {};
-}
+} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/style/property_parsing.cpp b/src/mbgl/style/property_parsing.cpp
index 9f60ddf3b6..16ce0f4adc 100644
--- a/src/mbgl/style/property_parsing.cpp
+++ b/src/mbgl/style/property_parsing.cpp
@@ -1,269 +1,8 @@
#include <mbgl/style/property_parsing.hpp>
-#include <mbgl/platform/log.hpp>
-
-#include <csscolorparser/csscolorparser.hpp>
-
namespace mbgl {
namespace style {
-template <>
-optional<bool> parseConstant(const char* name, const JSValue& value) {
- if (!value.IsBool()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a boolean", name);
- return {};
- }
-
- return value.GetBool();
-}
-
-template <>
-optional<float> parseConstant(const char* name, const JSValue& value) {
- if (!value.IsNumber()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", name);
- return {};
- }
-
- return value.GetDouble();
-}
-
-template <>
-optional<std::string> parseConstant(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return std::string { value.GetString(), value.GetStringLength() };
-}
-
-template <>
-optional<Color> parseConstant(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- CSSColorParser::Color css_color = CSSColorParser::parse({ value.GetString(), value.GetStringLength() });
-
- // Premultiply the color.
- const float factor = css_color.a / 255;
-
- return Color{{(float)css_color.r * factor,
- (float)css_color.g * factor,
- (float)css_color.b * factor,
- css_color.a}};
-}
-
-MBGL_DEFINE_ENUM_CLASS(TranslateAnchorTypeClass, TranslateAnchorType, {
- { TranslateAnchorType::Map, "map" },
- { TranslateAnchorType::Viewport, "viewport" },
-});
-
-template <>
-optional<TranslateAnchorType> parseConstant(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return { TranslateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) };
-}
-
-MBGL_DEFINE_ENUM_CLASS(RotateAnchorTypeClass, RotateAnchorType, {
- { RotateAnchorType::Map, "map" },
- { RotateAnchorType::Viewport, "viewport" },
-});
-
-template <>
-optional<RotateAnchorType> parseConstant<RotateAnchorType>(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return { RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) };
-}
-
-MBGL_DEFINE_ENUM_CLASS(LineCapTypeClass, LineCapType, {
- { LineCapType::Round, "round" },
- { LineCapType::Butt, "butt" },
- { LineCapType::Square, "square" },
-});
-
-template <>
-optional<LineCapType> parseConstant<LineCapType>(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return { LineCapTypeClass({ value.GetString(), value.GetStringLength() }) };
-}
-
-MBGL_DEFINE_ENUM_CLASS(LineJoinTypeClass, LineJoinType, {
- { LineJoinType::Miter, "miter" },
- { LineJoinType::Bevel, "bevel" },
- { LineJoinType::Round, "round" },
- { LineJoinType::FakeRound, "fakeround" },
- { LineJoinType::FlipBevel, "flipbevel" },
-});
-
-template <>
-optional<LineJoinType> parseConstant<LineJoinType>(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return { LineJoinTypeClass({ value.GetString(), value.GetStringLength() }) };
-}
-
-MBGL_DEFINE_ENUM_CLASS(SymbolPlacementTypeClass, SymbolPlacementType, {
- { SymbolPlacementType::Point, "point" },
- { SymbolPlacementType::Line, "line" },
-});
-
-template <>
-optional<SymbolPlacementType> parseConstant<SymbolPlacementType>(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return { SymbolPlacementTypeClass({ value.GetString(), value.GetStringLength() }) };
-}
-
-MBGL_DEFINE_ENUM_CLASS(TextAnchorTypeClass, 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" }
-});
-
-template <>
-optional<TextAnchorType> parseConstant<TextAnchorType>(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return { TextAnchorTypeClass({ value.GetString(), value.GetStringLength() }) };
-}
-
-MBGL_DEFINE_ENUM_CLASS(TextJustifyTypeClass, TextJustifyType, {
- { TextJustifyType::Center, "center" },
- { TextJustifyType::Left, "left" },
- { TextJustifyType::Right, "right" },
-});
-
-template <>
-optional<TextJustifyType> parseConstant<TextJustifyType>(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return { TextJustifyTypeClass({ value.GetString(), value.GetStringLength() }) };
-}
-
-MBGL_DEFINE_ENUM_CLASS(TextTransformTypeClass, TextTransformType, {
- { TextTransformType::None, "none" },
- { TextTransformType::Uppercase, "uppercase" },
- { TextTransformType::Lowercase, "lowercase" },
-});
-
-template <>
-optional<TextTransformType> parseConstant<TextTransformType>(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return { TextTransformTypeClass({ value.GetString(), value.GetStringLength() }) };
-}
-
-MBGL_DEFINE_ENUM_CLASS(RotationAlignmentTypeClass, RotationAlignmentType, {
- { RotationAlignmentType::Map, "map" },
- { RotationAlignmentType::Viewport, "viewport" },
-});
-
-template <>
-optional<RotationAlignmentType> parseConstant<RotationAlignmentType>(const char* name, const JSValue& value) {
- if (!value.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name);
- return {};
- }
-
- return { RotationAlignmentTypeClass({ value.GetString(), value.GetStringLength() }) };
-}
-
-template <>
-optional<std::array<float, 2>> parseConstant(const char* name, const JSValue& value) {
- if (value.IsArray() && value.Size() == 2 &&
- value[rapidjson::SizeType(0)].IsNumber() &&
- value[rapidjson::SizeType(1)].IsNumber()) {
-
- float first = value[rapidjson::SizeType(0)].GetDouble();
- float second = value[rapidjson::SizeType(1)].GetDouble();
- return { {{ first, second }} };
- } else {
- Log::Warning(Event::ParseStyle, "value of '%s' must be an array of two numbers", name);
- return {};
- }
-}
-
-template <>
-optional<std::vector<float>> parseConstant(const char* name, const JSValue& value) {
- if (!value.IsArray()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be an array of numbers", name);
- return {};
- }
-
- std::vector<float> result;
-
- for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
- const JSValue& part = value[i];
-
- if (!part.IsNumber()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be an array of numbers", name);
- return {};
- }
-
- result.push_back(part.GetDouble());
- }
-
- return result;
-}
-
-template <>
-optional<std::vector<std::string>> parseConstant(const char* name, const JSValue& value) {
- if (!value.IsArray()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be an array of strings", name);
- return {};
- }
-
- std::vector<std::string> result;
-
- for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
- const JSValue& part = value[i];
-
- if (!part.IsString()) {
- Log::Warning(Event::ParseStyle, "value of '%s' must be an array of strings", name);
- return {};
- }
-
- result.push_back({ part.GetString(), part.GetStringLength() });
- }
-
- return result;
-}
-
optional<TransitionOptions> parseTransitionOptions(const char *, const JSValue& value) {
if (!value.IsObject()) {
return {};
@@ -283,7 +22,7 @@ optional<TransitionOptions> parseTransitionOptions(const char *, const JSValue&
return {};
}
- return TransitionOptions(duration, delay);
+ return TransitionOptions { duration, delay };
}
} // namespace style
diff --git a/src/mbgl/style/property_parsing.hpp b/src/mbgl/style/property_parsing.hpp
index bb894c2407..8c2bd2c0f4 100644
--- a/src/mbgl/style/property_parsing.hpp
+++ b/src/mbgl/style/property_parsing.hpp
@@ -1,109 +1,23 @@
#pragma once
-#include <mbgl/style/types.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/transition_options.hpp>
-
-#include <mbgl/util/rapidjson.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/color.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion/property_value.hpp>
#include <mbgl/platform/log.hpp>
-#include <string>
-#include <array>
-#include <vector>
-
namespace mbgl {
namespace style {
template <typename T>
-optional<T> parseConstant(const char* name, const JSValue&);
-
-template <> optional<bool> parseConstant(const char*, const JSValue&);
-template <> optional<float> parseConstant(const char*, const JSValue&);
-template <> optional<std::string> parseConstant(const char*, const JSValue&);
-template <> optional<Color> parseConstant(const char*, const JSValue&);
-template <> optional<TranslateAnchorType> parseConstant(const char*, const JSValue&);
-template <> optional<RotateAnchorType> parseConstant(const char*, const JSValue&);
-template <> optional<LineCapType> parseConstant(const char*, const JSValue&);
-template <> optional<LineJoinType> parseConstant(const char*, const JSValue&);
-template <> optional<SymbolPlacementType> parseConstant(const char*, const JSValue&);
-template <> optional<TextAnchorType> parseConstant(const char*, const JSValue&);
-template <> optional<TextJustifyType> parseConstant(const char*, const JSValue&);
-template <> optional<TextTransformType> parseConstant(const char*, const JSValue&);
-template <> optional<RotationAlignmentType> parseConstant(const char*, const JSValue&);
-template <> optional<std::array<float, 2>> parseConstant(const char*, const JSValue&);
-template <> optional<std::vector<float>> parseConstant(const char*, const JSValue&);
-template <> optional<std::vector<std::string>> parseConstant(const char*, const JSValue&);
-
-template <typename T>
PropertyValue<T> parseProperty(const char* name, const JSValue& value) {
- if (!value.IsObject()) {
- auto constant = parseConstant<T>(name, value);
-
- if (!constant) {
- return {};
- }
-
- return *constant;
- }
-
- if (!value.HasMember("stops")) {
- Log::Warning(Event::ParseStyle, "function must specify a function type");
+ conversion::Result<PropertyValue<T>> result = conversion::convert<PropertyValue<T>>(value);
+ if (!result) {
+ Log::Warning(Event::ParseStyle, "%s: %s", name, result.error().message);
return {};
}
-
- float base = 1.0f;
-
- if (value.HasMember("base")) {
- const JSValue& value_base = value["base"];
-
- if (!value_base.IsNumber()) {
- Log::Warning(Event::ParseStyle, "base must be numeric");
- return {};
- }
-
- base = value_base.GetDouble();
- }
-
- const JSValue& stopsValue = value["stops"];
-
- if (!stopsValue.IsArray()) {
- Log::Warning(Event::ParseStyle, "stops function must specify a stops array");
- return {};
- }
-
- std::vector<std::pair<float, T>> stops;
-
- for (rapidjson::SizeType i = 0; i < stopsValue.Size(); ++i) {
- const JSValue& stop = stopsValue[i];
-
- if (!stop.IsArray()) {
- Log::Warning(Event::ParseStyle, "function argument must be a numeric value");
- return {};
- }
-
- if (stop.Size() != 2) {
- Log::Warning(Event::ParseStyle, "stop must have zoom level and value specification");
- return {};
- }
-
- const JSValue& z = stop[rapidjson::SizeType(0)];
- if (!z.IsNumber()) {
- Log::Warning(Event::ParseStyle, "zoom level in stop must be a number");
- return {};
- }
-
- optional<T> v = parseConstant<T>(name, stop[rapidjson::SizeType(1)]);
- if (!v) {
- return {};
- }
-
- stops.emplace_back(z.GetDouble(), *v);
- }
-
- return Function<T>(stops, base);
+ return *result;
}
optional<TransitionOptions> parseTransitionOptions(const char * name, const JSValue&);
diff --git a/src/mbgl/style/rapidjson_conversion.hpp b/src/mbgl/style/rapidjson_conversion.hpp
new file mode 100644
index 0000000000..ecf044fb64
--- /dev/null
+++ b/src/mbgl/style/rapidjson_conversion.hpp
@@ -0,0 +1,94 @@
+#pragma once
+
+#include <mbgl/util/rapidjson.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/style/conversion.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+inline bool isUndefined(const JSValue& value) {
+ return value.IsNull();
+}
+
+inline bool isArray(const JSValue& value) {
+ return value.IsArray();
+}
+
+inline std::size_t arrayLength(const JSValue& value) {
+ return value.Size();
+}
+
+inline const JSValue& arrayMember(const JSValue& value, std::size_t i) {
+ return value[rapidjson::SizeType(i)];
+}
+
+inline bool isObject(const JSValue& value) {
+ return value.IsObject();
+}
+
+inline const JSValue* objectMember(const JSValue& value, const char * name) {
+ if (!value.HasMember(name)) {
+ return nullptr;
+ }
+ return &value[name];
+}
+
+template <class Fn>
+optional<Error> eachMember(const JSValue& value, Fn&& fn) {
+ for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
+ optional<Error> result = fn({it->name.GetString(), it->name.GetStringLength()}, it->value);
+ if (result) {
+ return result;
+ }
+ }
+ return {};
+}
+
+inline optional<bool> toBool(const JSValue& value) {
+ if (!value.IsBool()) {
+ return {};
+ }
+ return value.GetBool();
+}
+
+inline optional<float> toNumber(const JSValue& value) {
+ if (!value.IsNumber()) {
+ return {};
+ }
+ return value.GetDouble();
+}
+
+inline optional<std::string> toString(const JSValue& value) {
+ if (!value.IsString()) {
+ return {};
+ }
+ return {{ value.GetString(), value.GetStringLength() }};
+}
+
+inline optional<Value> toValue(const JSValue& value) {
+ switch (value.GetType()) {
+ case rapidjson::kNullType:
+ case rapidjson::kFalseType:
+ return { false };
+
+ case rapidjson::kTrueType:
+ return { true };
+
+ case rapidjson::kStringType:
+ return { std::string { value.GetString(), value.GetStringLength() } };
+
+ case rapidjson::kNumberType:
+ if (value.IsUint64()) return { value.GetUint64() };
+ if (value.IsInt64()) return { value.GetInt64() };
+ return { value.GetDouble() };
+
+ default:
+ return {};
+ }
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/source.cpp b/src/mbgl/style/source.cpp
index 8e5973f412..4c83a3ce0e 100644
--- a/src/mbgl/style/source.cpp
+++ b/src/mbgl/style/source.cpp
@@ -1,427 +1,17 @@
#include <mbgl/style/source.hpp>
-#include <mbgl/style/source_observer.hpp>
-#include <mbgl/map/transform.hpp>
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/tile/vector_tile.hpp>
-#include <mbgl/annotation/annotation_tile.hpp>
-#include <mbgl/tile/geojson_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/util/exception.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/style/layer.hpp>
-#include <mbgl/style/update_parameters.hpp>
-#include <mbgl/style/query_parameters.hpp>
-#include <mbgl/platform/log.hpp>
-#include <mbgl/math/minmax.hpp>
-#include <mbgl/math/clamp.hpp>
-#include <mbgl/util/std.hpp>
-#include <mbgl/util/token.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/util/tile_cover.hpp>
-
-#include <mbgl/tile/vector_tile_data.hpp>
-#include <mbgl/tile/raster_tile_data.hpp>
-#include <mbgl/style/parser.hpp>
-#include <mbgl/gl/debugging.hpp>
-
-#include <mbgl/algorithm/update_renderables.hpp>
-
-#include <mapbox/geojsonvt.hpp>
-#include <mapbox/geojsonvt/convert.hpp>
-#include <mapbox/geometry/envelope.hpp>
-
-#include <rapidjson/error/en.h>
-
-#include <algorithm>
-#include <sstream>
+#include <mbgl/style/source_impl.hpp>
namespace mbgl {
namespace style {
-static SourceObserver nullObserver;
-
-Source::Source(SourceType type_,
- const std::string& id_,
- const std::string& url_,
- uint16_t tileSize_,
- std::unique_ptr<Tileset>&& tileset_,
- std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>&& geojsonvt_)
- : type(type_),
- id(id_),
- url(url_),
- tileSize(tileSize_),
- tileset(std::move(tileset_)),
- geojsonvt(std::move(geojsonvt_)),
- observer(&nullObserver) {
+Source::Source(SourceType type_, std::unique_ptr<Impl> baseImpl_)
+ : baseImpl(std::move(baseImpl_)), type(type_) {
}
Source::~Source() = default;
-bool Source::isLoaded() const {
- if (!loaded) return false;
-
- for (const auto& pair : tileDataMap) {
- if (!pair.second->isComplete()) {
- return false;
- }
- }
-
- return true;
-}
-
-bool Source::isLoading() const {
- return !loaded && req.operator bool();
-}
-
-void Source::load(FileSource& fileSource) {
- if (url.empty()) {
- // In case there is no URL set, we assume that we already have all of the data because the
- // TileJSON was specified inline in the stylesheet.
- loaded = true;
- return;
- }
-
- if (req) {
- // We don't have a Tileset object yet, but there's already a request underway to load
- // the data.
- return;
- }
-
- // URL may either be a TileJSON file, or a GeoJSON file.
- req = fileSource.request(Resource::source(url), [this](Response res) {
- if (res.error) {
- observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message)));
- } else if (res.notModified) {
- return;
- } else if (res.noContent) {
- observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty source")));
- } else {
- bool reloadTiles = false;
-
- if (type == SourceType::Vector || type == SourceType::Raster) {
- std::unique_ptr<Tileset> newTileset;
-
- // Create a new copy of the Tileset object that holds the base values we've parsed
- // from the stylesheet. Then merge in the values parsed from the TileJSON we retrieved
- // via the URL.
- try {
- newTileset = style::parseTileJSON(*res.data, url, type, tileSize);
- } catch (...) {
- observer->onSourceError(*this, std::current_exception());
- return;
- }
-
- // Check whether previous information specifies different tile
- if (tileset && tileset->tiles != newTileset->tiles) {
- reloadTiles = true;
-
- // Tile size changed: We need to recalculate the tiles we need to load because we
- // might have to load tiles for a different zoom level
- // This is done automatically when we trigger the onSourceLoaded observer below.
-
- // Min/Max zoom changed: We need to recalculate what tiles to load, if we have tiles
- // loaded that are outside the new zoom range
- // This is done automatically when we trigger the onSourceLoaded observer below.
-
- // Attribution changed: We need to notify the embedding application that this
- // changed. See https://github.com/mapbox/mapbox-gl-native/issues/2723
- // This is not yet implemented.
-
- // Center/bounds changed: We're not using these values currently
- }
-
- tileset = std::move(newTileset);
- } else if (type == SourceType::GeoJSON) {
- std::unique_ptr<Tileset> newTileset = std::make_unique<Tileset>();
-
- rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> d;
- d.Parse<0>(res.data->c_str());
-
- if (d.HasParseError()) {
- std::stringstream message;
- message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError());
- observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(message.str())));
- return;
- }
-
- geojsonvt = style::parseGeoJSON(d);
- reloadTiles = true;
-
- newTileset->maxZoom = geojsonvt->options.maxZoom;
- tileset = std::move(newTileset);
- }
-
- if (reloadTiles) {
- // Tile information changed because we got new GeoJSON data, or a new tile URL.
- tileDataMap.clear();
- tiles.clear();
- cache.clear();
- }
-
- loaded = true;
- observer->onSourceLoaded(*this);
- }
- });
-}
-
-void Source::updateMatrices(const mat4 &projMatrix, const TransformState &transform) {
- for (auto& pair : tiles) {
- auto& tile = pair.second;
- transform.matrixFor(tile.matrix, tile.id);
- matrix::multiply(tile.matrix, projMatrix, tile.matrix);
- }
-}
-
-void Source::finishRender(Painter &painter) {
- for (auto& pair : tiles) {
- auto& tile = pair.second;
- painter.renderTileDebug(tile);
- }
-}
-
-const std::map<UnwrappedTileID, Tile>& Source::getTiles() const {
- return tiles;
-}
-
-std::unique_ptr<TileData> Source::createTile(const OverscaledTileID& overscaledTileID,
- const UpdateParameters& parameters) {
- std::unique_ptr<TileData> data = cache.get(overscaledTileID);
- if (data) {
- return data;
- }
-
- auto callback = std::bind(&Source::tileLoadingCallback, this, overscaledTileID,
- std::placeholders::_1, true);
-
- // If we don't find working tile data, we're just going to load it.
- if (type == SourceType::Raster) {
- data = std::make_unique<RasterTileData>(overscaledTileID, parameters.pixelRatio,
- tileset->tiles.at(0), parameters.texturePool,
- parameters.worker, parameters.fileSource, callback);
- } else {
- std::unique_ptr<GeometryTileMonitor> monitor;
-
- if (type == SourceType::Vector) {
- monitor = std::make_unique<VectorTileMonitor>(overscaledTileID, parameters.pixelRatio, tileset->tiles.at(0), parameters.fileSource);
- } else if (type == SourceType::Annotations) {
- monitor = std::make_unique<AnnotationTileMonitor>(overscaledTileID, parameters.annotationManager);
- } else if (type == SourceType::GeoJSON) {
- monitor = std::make_unique<GeoJSONTileMonitor>(geojsonvt.get(), overscaledTileID);
- } else {
- Log::Warning(Event::Style, "Source type '%s' is not implemented", SourceTypeClass(type).c_str());
- return nullptr;
- }
-
- data = std::make_unique<VectorTileData>(overscaledTileID, std::move(monitor), id,
- parameters.style, parameters.mode, callback);
- }
-
- return data;
-}
-
-TileData* Source::getTileData(const OverscaledTileID& overscaledTileID) const {
- auto it = tileDataMap.find(overscaledTileID);
- if (it != tileDataMap.end()) {
- return it->second.get();
- } else {
- return nullptr;
- }
-}
-
-bool Source::update(const UpdateParameters& parameters) {
- bool allTilesUpdated = true;
-
- if (!loaded || parameters.animationTime <= updated) {
- return allTilesUpdated;
- }
-
- // Determine the overzooming/underzooming amounts and required tiles.
- int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize);
- int32_t dataTileZoom = overscaledZoom;
-
- std::vector<UnwrappedTileID> idealTiles;
- if (overscaledZoom >= tileset->minZoom) {
- int32_t idealZoom = std::min<int32_t>(tileset->maxZoom, overscaledZoom);
-
- // Make sure we're not reparsing overzoomed raster tiles.
- if (type == SourceType::Raster) {
- dataTileZoom = idealZoom;
- }
-
- idealTiles = util::tileCover(parameters.transformState, idealZoom);
- }
-
- // Stores a list of all the data tiles that we're definitely going to retain. There are two
- // kinds of tiles we need: the ideal tiles determined by the tile cover. They may not yet be in
- // use because they're still loading. In addition to that, we also need to retain all tiles that
- // we're actively using, e.g. as a replacement for tile that aren't loaded yet.
- std::set<OverscaledTileID> retain;
-
- auto retainTileDataFn = [&retain](const TileData& tileData) -> void {
- retain.emplace(tileData.id);
- };
- auto getTileDataFn = [this](const OverscaledTileID& dataTileID) -> TileData* {
- return getTileData(dataTileID);
- };
- auto createTileDataFn = [this, &parameters](const OverscaledTileID& dataTileID) -> TileData* {
- if (auto data = createTile(dataTileID, parameters)) {
- return tileDataMap.emplace(dataTileID, std::move(data)).first->second.get();
- } else {
- return nullptr;
- }
- };
- auto renderTileFn = [this](const UnwrappedTileID& renderTileID, TileData& tileData) {
- tiles.emplace(renderTileID, Tile{ renderTileID, tileData });
- };
-
- tiles.clear();
- algorithm::updateRenderables(getTileDataFn, createTileDataFn, retainTileDataFn, renderTileFn,
- idealTiles, *tileset, dataTileZoom);
-
- if (type != SourceType::Raster && type != SourceType::Annotations && cache.getSize() == 0) {
- size_t conservativeCacheSize =
- ((float)parameters.transformState.getWidth() / util::tileSize) *
- ((float)parameters.transformState.getHeight() / util::tileSize) *
- (parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) *
- 0.5;
- cache.setSize(conservativeCacheSize);
- }
-
- // Remove stale data tiles from the active set of tiles.
- // This goes through the (sorted!) tileDataMap and retain set in lockstep and removes items from
- // tileDataMap that don't have the corresponding key in the retain set.
- auto dataIt = tileDataMap.begin();
- auto retainIt = retain.begin();
- while (dataIt != tileDataMap.end()) {
- if (retainIt == retain.end() || dataIt->first < *retainIt) {
- cache.add(dataIt->first, std::move(dataIt->second));
- tileDataMap.erase(dataIt++);
- } else {
- if (!(*retainIt < dataIt->first)) {
- ++dataIt;
- }
- ++retainIt;
- }
- }
-
- for (auto& pair : tileDataMap) {
- const auto& dataTileID = pair.first;
- auto tileData = pair.second.get();
- if (parameters.shouldReparsePartialTiles && tileData->isIncomplete()) {
- auto callback = std::bind(&Source::tileLoadingCallback, this, dataTileID,
- std::placeholders::_1, false);
-
- if (!tileData->parsePending(callback)) {
- allTilesUpdated = false;
- }
- } else {
- tileData->redoPlacement({ parameters.transformState.getAngle(),
- parameters.transformState.getPitch(),
- parameters.debugOptions & MapDebugOptions::Collision },
- [this]() { observer->onPlacementRedone(); });
- }
- }
-
- updated = parameters.animationTime;
-
- return allTilesUpdated;
-}
-
-static Point<int16_t> coordinateToTilePoint(const UnwrappedTileID& tileID, const Point<double>& p) {
- auto zoomedCoord = TileCoordinate { p, 0 }.zoomTo(tileID.canonical.z);
- return {
- int16_t(util::clamp<int64_t>((zoomedCoord.p.x - tileID.canonical.x - tileID.wrap * std::pow(2, tileID.canonical.z)) * util::EXTENT,
- std::numeric_limits<int16_t>::min(),
- std::numeric_limits<int16_t>::max())),
- int16_t(util::clamp<int64_t>((zoomedCoord.p.y - tileID.canonical.y) * util::EXTENT,
- std::numeric_limits<int16_t>::min(),
- std::numeric_limits<int16_t>::max()))
- };
-}
-
-std::unordered_map<std::string, std::vector<Feature>> Source::queryRenderedFeatures(const QueryParameters& parameters) const {
- LineString<double> queryGeometry;
-
- for (const auto& p : parameters.geometry) {
- queryGeometry.push_back(TileCoordinate::fromScreenCoordinate(
- parameters.transformState, 0, { p.x, parameters.transformState.getHeight() - p.y }).p);
- }
-
- mapbox::geometry::box<double> box = mapbox::geometry::envelope(queryGeometry);
-
- std::unordered_map<std::string, std::vector<Feature>> result;
-
- for (const auto& tilePtr : tiles) {
- const Tile& tile = tilePtr.second;
-
- Point<int16_t> tileSpaceBoundsMin = coordinateToTilePoint(tile.id, box.min);
- Point<int16_t> tileSpaceBoundsMax = coordinateToTilePoint(tile.id, box.max);
-
- if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT ||
- tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) continue;
-
- GeometryCoordinates tileSpaceQueryGeometry;
-
- for (const auto& c : queryGeometry) {
- tileSpaceQueryGeometry.push_back(coordinateToTilePoint(tile.id, c));
- }
-
- tile.data.queryRenderedFeatures(result,
- tileSpaceQueryGeometry,
- parameters.transformState,
- parameters.layerIDs);
- }
-
- return result;
-}
-
-void Source::setCacheSize(size_t size) {
- cache.setSize(size);
-}
-
-void Source::onLowMemory() {
- cache.clear();
-}
-
-void Source::setObserver(SourceObserver* observer_) {
- observer = observer_;
-}
-
-void Source::tileLoadingCallback(const OverscaledTileID& tileID,
- std::exception_ptr error,
- bool isNewTile) {
- auto it = tileDataMap.find(tileID);
- if (it == tileDataMap.end()) {
- return;
- }
-
- auto& tileData = it->second;
- if (!tileData) {
- return;
- }
-
- if (error) {
- observer->onTileError(*this, tileID, error);
- return;
- }
-
- tileData->redoPlacement([this]() {
- observer->onPlacementRedone();
- });
- observer->onTileLoaded(*this, tileID, isNewTile);
-}
-
-void Source::dumpDebugLogs() const {
- Log::Info(Event::General, "Source::id: %s", id.c_str());
- Log::Info(Event::General, "Source::loaded: %d", loaded);
-
- for (const auto& pair : tileDataMap) {
- auto& tileData = pair.second;
- tileData->dumpDebugLogs();
- }
+const std::string& Source::getID() const {
+ return baseImpl->id;
}
} // namespace style
diff --git a/src/mbgl/style/source.hpp b/src/mbgl/style/source.hpp
deleted file mode 100644
index 07e1aeaf58..0000000000
--- a/src/mbgl/style/source.hpp
+++ /dev/null
@@ -1,112 +0,0 @@
-#pragma once
-
-#include <mbgl/tile/tile_id.hpp>
-#include <mbgl/tile/tile_data.hpp>
-#include <mbgl/tile/tile_cache.hpp>
-#include <mbgl/style/types.hpp>
-
-#include <mbgl/util/mat4.hpp>
-#include <mbgl/util/rapidjson.hpp>
-#include <mbgl/util/feature.hpp>
-#include <mbgl/util/tileset.hpp>
-
-#include <forward_list>
-#include <vector>
-#include <map>
-
-namespace mapbox {
-namespace geojsonvt {
-class GeoJSONVT;
-} // namespace geojsonvt
-} // namespace mapbox
-
-namespace mbgl {
-
-class Painter;
-class FileSource;
-class AsyncRequest;
-class TransformState;
-class Tile;
-struct ClipID;
-
-namespace style {
-
-class Style;
-class UpdateParameters;
-class QueryParameters;
-class SourceObserver;
-
-class Source : private util::noncopyable {
-public:
- Source(SourceType,
- const std::string& id,
- const std::string& url,
- uint16_t tileSize,
- std::unique_ptr<Tileset>&&,
- std::unique_ptr<mapbox::geojsonvt::GeoJSONVT>&&);
- ~Source();
-
- bool loaded = false;
- void load(FileSource&);
- bool isLoading() const;
- bool isLoaded() const;
-
- const Tileset* getTileset() const { return tileset.get(); }
-
- // Request or parse all the tiles relevant for the "TransformState". This method
- // will return true if all the tiles were scheduled for updating of false if
- // they were not. shouldReparsePartialTiles must be set to "true" if there is
- // new data available that a tile in the "partial" state might be interested at.
- bool update(const UpdateParameters&);
-
- template <typename ClipIDGenerator>
- void updateClipIDs(ClipIDGenerator& generator) {
- generator.update(tiles);
- }
-
- void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
- void finishRender(Painter &painter);
-
- const std::map<UnwrappedTileID, Tile>& getTiles() const;
-
- TileData* getTileData(const OverscaledTileID&) const;
-
- std::unordered_map<std::string, std::vector<Feature>>
- queryRenderedFeatures(const QueryParameters&) const;
-
- void setCacheSize(size_t);
- void onLowMemory();
-
- void setObserver(SourceObserver* observer);
- void dumpDebugLogs() const;
-
- const SourceType type;
- const std::string id;
- const std::string url;
- uint16_t tileSize = util::tileSize;
- bool enabled = false;
-
-private:
- void tileLoadingCallback(const OverscaledTileID&, std::exception_ptr, bool isNewTile);
-
- std::unique_ptr<TileData> createTile(const OverscaledTileID&, const UpdateParameters&);
-
-private:
- std::unique_ptr<const Tileset> tileset;
-
- std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> geojsonvt;
-
- // Stores the time when this source was most recently updated.
- TimePoint updated = TimePoint::min();
-
- std::map<UnwrappedTileID, Tile> tiles;
- std::map<OverscaledTileID, std::unique_ptr<TileData>> tileDataMap;
- TileCache cache;
-
- std::unique_ptr<AsyncRequest> req;
-
- SourceObserver* observer = nullptr;
-};
-
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/style/source_impl.cpp b/src/mbgl/style/source_impl.cpp
new file mode 100644
index 0000000000..95363ca699
--- /dev/null
+++ b/src/mbgl/style/source_impl.cpp
@@ -0,0 +1,287 @@
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/source_observer.hpp>
+#include <mbgl/map/transform.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/painter.hpp>
+#include <mbgl/style/update_parameters.hpp>
+#include <mbgl/style/query_parameters.hpp>
+#include <mbgl/platform/log.hpp>
+#include <mbgl/math/clamp.hpp>
+#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/util/enum.hpp>
+
+#include <mbgl/algorithm/update_renderables.hpp>
+#include <mbgl/algorithm/generate_clip_ids.hpp>
+#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
+
+#include <mapbox/geometry/envelope.hpp>
+
+#include <algorithm>
+
+namespace mbgl {
+namespace style {
+
+static SourceObserver nullObserver;
+
+Source::Impl::Impl(SourceType type_, std::string id_, Source& base_)
+ : type(type_),
+ id(std::move(id_)),
+ base(base_),
+ observer(&nullObserver) {
+}
+
+Source::Impl::~Impl() = default;
+
+bool Source::Impl::isLoaded() const {
+ if (!loaded) return false;
+
+ for (const auto& pair : tiles) {
+ if (!pair.second->isComplete()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void Source::Impl::invalidateTiles() {
+ tiles.clear();
+ renderTiles.clear();
+ cache.clear();
+}
+
+void Source::Impl::startRender(algorithm::ClipIDGenerator& generator,
+ const mat4& projMatrix,
+ const TransformState& transform) {
+ if (type == SourceType::Vector ||
+ type == SourceType::GeoJSON ||
+ type == SourceType::Annotations) {
+ generator.update(renderTiles);
+ }
+
+ for (auto& pair : renderTiles) {
+ auto& tile = pair.second;
+ transform.matrixFor(tile.matrix, tile.id);
+ matrix::multiply(tile.matrix, projMatrix, tile.matrix);
+ }
+}
+
+void Source::Impl::finishRender(Painter& painter) {
+ for (auto& pair : renderTiles) {
+ auto& tile = pair.second;
+ painter.renderTileDebug(tile);
+ }
+}
+
+const std::map<UnwrappedTileID, RenderTile>& Source::Impl::getRenderTiles() const {
+ return renderTiles;
+}
+
+Tile* Source::Impl::getTile(const OverscaledTileID& overscaledTileID) const {
+ auto it = tiles.find(overscaledTileID);
+ if (it != tiles.end()) {
+ return it->second.get();
+ } else {
+ return nullptr;
+ }
+}
+
+bool Source::Impl::update(const UpdateParameters& parameters) {
+ bool allTilesUpdated = true;
+
+ if (!loaded || parameters.animationTime <= updated) {
+ return allTilesUpdated;
+ }
+
+ const uint16_t tileSize = getTileSize();
+ const Range<uint8_t> zoomRange = getZoomRange();
+
+ // Determine the overzooming/underzooming amounts and required tiles.
+ int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize);
+ int32_t dataTileZoom = overscaledZoom;
+
+ std::vector<UnwrappedTileID> idealTiles;
+ if (overscaledZoom >= zoomRange.min) {
+ int32_t idealZoom = std::min<int32_t>(zoomRange.max, overscaledZoom);
+
+ // Make sure we're not reparsing overzoomed raster tiles.
+ if (type == SourceType::Raster) {
+ dataTileZoom = idealZoom;
+ }
+
+ idealTiles = util::tileCover(parameters.transformState, idealZoom);
+ }
+
+ // Stores a list of all the data tiles that we're definitely going to retain. There are two
+ // kinds of tiles we need: the ideal tiles determined by the tile cover. They may not yet be in
+ // use because they're still loading. In addition to that, we also need to retain all tiles that
+ // we're actively using, e.g. as a replacement for tile that aren't loaded yet.
+ std::set<OverscaledTileID> retain;
+
+ auto retainTileFn = [&retain](Tile& tile, bool required) -> void {
+ retain.emplace(tile.id);
+ tile.setNecessity(required ? Tile::Necessity::Required
+ : Tile::Necessity::Optional);
+ };
+ auto getTileFn = [this](const OverscaledTileID& dataTileID) -> Tile* {
+ return getTile(dataTileID);
+ };
+ auto createTileFn = [this, &parameters](const OverscaledTileID& dataTileID) -> Tile* {
+ std::unique_ptr<Tile> data = cache.get(dataTileID);
+ if (!data) {
+ data = createTile(dataTileID, parameters);
+ if (data) {
+ data->setObserver(this);
+ }
+ }
+ if (data) {
+ return tiles.emplace(dataTileID, std::move(data)).first->second.get();
+ } else {
+ return nullptr;
+ }
+ };
+ auto renderTileFn = [this](const UnwrappedTileID& renderTileID, Tile& tile) {
+ renderTiles.emplace(renderTileID, RenderTile{ renderTileID, tile });
+ };
+
+ renderTiles.clear();
+ algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn,
+ idealTiles, zoomRange, dataTileZoom);
+
+ if (type != SourceType::Raster && type != SourceType::Annotations && cache.getSize() == 0) {
+ size_t conservativeCacheSize =
+ ((float)parameters.transformState.getWidth() / util::tileSize) *
+ ((float)parameters.transformState.getHeight() / util::tileSize) *
+ (parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) *
+ 0.5;
+ cache.setSize(conservativeCacheSize);
+ }
+
+ // Remove stale data tiles from the active set of tiles.
+ // This goes through the (sorted!) tiles and retain set in lockstep and removes items from
+ // tiles that don't have the corresponding key in the retain set.
+ auto dataIt = tiles.begin();
+ auto retainIt = retain.begin();
+ while (dataIt != tiles.end()) {
+ if (retainIt == retain.end() || dataIt->first < *retainIt) {
+ dataIt->second->setNecessity(Tile::Necessity::Optional);
+ cache.add(dataIt->first, std::move(dataIt->second));
+ tiles.erase(dataIt++);
+ } else {
+ if (!(*retainIt < dataIt->first)) {
+ ++dataIt;
+ }
+ ++retainIt;
+ }
+ }
+
+ const PlacementConfig newConfig{ parameters.transformState.getAngle(),
+ parameters.transformState.getPitch(),
+ parameters.debugOptions & MapDebugOptions::Collision };
+ for (auto& pair : tiles) {
+ auto tile = pair.second.get();
+ if (parameters.shouldReparsePartialTiles && tile->isIncomplete()) {
+ if (!tile->parsePending()) {
+ allTilesUpdated = false;
+ }
+ } else {
+ tile->redoPlacement(newConfig);
+ }
+ }
+
+ updated = parameters.animationTime;
+
+ return allTilesUpdated;
+}
+
+static Point<int16_t> coordinateToTilePoint(const UnwrappedTileID& tileID, const Point<double>& p) {
+ auto zoomedCoord = TileCoordinate { p, 0 }.zoomTo(tileID.canonical.z);
+ return {
+ int16_t(util::clamp<int64_t>((zoomedCoord.p.x - tileID.canonical.x - tileID.wrap * std::pow(2, tileID.canonical.z)) * util::EXTENT,
+ std::numeric_limits<int16_t>::min(),
+ std::numeric_limits<int16_t>::max())),
+ int16_t(util::clamp<int64_t>((zoomedCoord.p.y - tileID.canonical.y) * util::EXTENT,
+ std::numeric_limits<int16_t>::min(),
+ std::numeric_limits<int16_t>::max()))
+ };
+}
+
+std::unordered_map<std::string, std::vector<Feature>> Source::Impl::queryRenderedFeatures(const QueryParameters& parameters) const {
+ std::unordered_map<std::string, std::vector<Feature>> result;
+ if (renderTiles.empty()) {
+ return result;
+ }
+
+ LineString<double> queryGeometry;
+
+ for (const auto& p : parameters.geometry) {
+ queryGeometry.push_back(TileCoordinate::fromScreenCoordinate(
+ parameters.transformState, 0, { p.x, parameters.transformState.getHeight() - p.y }).p);
+ }
+
+ if (queryGeometry.empty()) {
+ return result;
+ }
+
+ mapbox::geometry::box<double> box = mapbox::geometry::envelope(queryGeometry);
+
+ for (const auto& tilePtr : renderTiles) {
+ const RenderTile& tile = tilePtr.second;
+
+ Point<int16_t> tileSpaceBoundsMin = coordinateToTilePoint(tile.id, box.min);
+ Point<int16_t> tileSpaceBoundsMax = coordinateToTilePoint(tile.id, box.max);
+
+ if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT ||
+ tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) continue;
+
+ GeometryCoordinates tileSpaceQueryGeometry;
+
+ for (const auto& c : queryGeometry) {
+ tileSpaceQueryGeometry.push_back(coordinateToTilePoint(tile.id, c));
+ }
+
+ tile.tile.queryRenderedFeatures(result,
+ tileSpaceQueryGeometry,
+ parameters.transformState,
+ parameters.layerIDs);
+ }
+
+ return result;
+}
+
+void Source::Impl::setCacheSize(size_t size) {
+ cache.setSize(size);
+}
+
+void Source::Impl::onLowMemory() {
+ cache.clear();
+}
+
+void Source::Impl::setObserver(SourceObserver* observer_) {
+ observer = observer_;
+}
+
+void Source::Impl::onTileLoaded(Tile& tile, bool isNewTile) {
+ observer->onTileLoaded(base, tile.id, isNewTile);
+}
+
+void Source::Impl::onTileError(Tile& tile, std::exception_ptr error) {
+ observer->onTileError(base, tile.id, error);
+}
+
+void Source::Impl::onNeedsRepaint() {
+ observer->onNeedsRepaint();
+}
+
+void Source::Impl::dumpDebugLogs() const {
+ Log::Info(Event::General, "Source::id: %s", base.getID().c_str());
+ Log::Info(Event::General, "Source::loaded: %d", loaded);
+
+ for (const auto& pair : tiles) {
+ auto& tile = pair.second;
+ tile->dumpDebugLogs();
+ }
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/source_impl.hpp b/src/mbgl/style/source_impl.hpp
new file mode 100644
index 0000000000..64651d7e3f
--- /dev/null
+++ b/src/mbgl/style/source_impl.hpp
@@ -0,0 +1,101 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/tile/tile_observer.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/tile/tile_cache.hpp>
+#include <mbgl/style/types.hpp>
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/range.hpp>
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+#include <map>
+
+namespace mbgl {
+
+class Painter;
+class FileSource;
+class TransformState;
+class RenderTile;
+
+namespace algorithm {
+class ClipIDGenerator;
+} // namespace algorithm
+
+namespace style {
+
+class UpdateParameters;
+class QueryParameters;
+class SourceObserver;
+
+class Source::Impl : public TileObserver, private util::noncopyable {
+public:
+ Impl(SourceType, std::string id, Source&);
+ ~Impl() override;
+
+ virtual void load(FileSource&) = 0;
+ bool isLoaded() const;
+
+ // Request or parse all the tiles relevant for the "TransformState". This method
+ // will return true if all the tiles were scheduled for updating of false if
+ // they were not. shouldReparsePartialTiles must be set to "true" if there is
+ // new data available that a tile in the "partial" state might be interested at.
+ bool update(const UpdateParameters&);
+
+ void startRender(algorithm::ClipIDGenerator&,
+ const mat4& projMatrix,
+ const TransformState&);
+ void finishRender(Painter&);
+
+ const std::map<UnwrappedTileID, RenderTile>& getRenderTiles() const;
+
+ Tile* getTile(const OverscaledTileID&) const;
+
+ std::unordered_map<std::string, std::vector<Feature>>
+ queryRenderedFeatures(const QueryParameters&) const;
+
+ void setCacheSize(size_t);
+ void onLowMemory();
+
+ void setObserver(SourceObserver*);
+ void dumpDebugLogs() const;
+
+ const SourceType type;
+ const std::string id;
+
+ bool loaded = false;
+ bool enabled = false;
+
+protected:
+ void invalidateTiles();
+
+ Source& base;
+ SourceObserver* observer = nullptr;
+
+private:
+ // TileObserver implementation.
+ void onTileLoaded(Tile&, bool isNewTile) override;
+ void onTileError(Tile&, std::exception_ptr) override;
+ void onNeedsRepaint() override;
+
+ virtual uint16_t getTileSize() const = 0;
+ virtual Range<uint8_t> getZoomRange() = 0;
+ virtual std::unique_ptr<Tile> createTile(const OverscaledTileID&, const UpdateParameters&) = 0;
+
+ // Stores the time when this source was most recently updated.
+ TimePoint updated = TimePoint::min();
+
+ std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles;
+ std::map<UnwrappedTileID, RenderTile> renderTiles;
+ TileCache cache;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/source_observer.hpp b/src/mbgl/style/source_observer.hpp
index a669e8e756..020ead4dba 100644
--- a/src/mbgl/style/source_observer.hpp
+++ b/src/mbgl/style/source_observer.hpp
@@ -18,7 +18,7 @@ public:
virtual void onSourceError(Source&, std::exception_ptr) {}
virtual void onTileLoaded(Source&, const OverscaledTileID&, bool /* isNewTile */) {}
virtual void onTileError(Source&, const OverscaledTileID&, std::exception_ptr) {}
- virtual void onPlacementRedone() {}
+ virtual void onNeedsRepaint() {}
};
} // namespace style
diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp
new file mode 100644
index 0000000000..2bf27880b4
--- /dev/null
+++ b/src/mbgl/style/sources/geojson_source.cpp
@@ -0,0 +1,26 @@
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/sources/geojson_source_impl.hpp>
+
+namespace mbgl {
+namespace style {
+
+GeoJSONSource::GeoJSONSource(const std::string& id, const GeoJSONOptions options)
+ : Source(SourceType::GeoJSON,
+ std::make_unique<GeoJSONSource::Impl>(std::move(id), *this, options)),
+ impl(static_cast<Impl*>(baseImpl.get())) {
+}
+
+void GeoJSONSource::setURL(const std::string& url) {
+ impl->setURL(url);
+}
+
+void GeoJSONSource::setGeoJSON(const mapbox::geojson::geojson& geoJSON) {
+ impl->setGeoJSON(geoJSON);
+}
+
+std::string GeoJSONSource::getURL() {
+ return impl->getURL();
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp
new file mode 100644
index 0000000000..0821ac0232
--- /dev/null
+++ b/src/mbgl/style/sources/geojson_source_impl.cpp
@@ -0,0 +1,142 @@
+#include <mbgl/platform/log.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/style/source_observer.hpp>
+#include <mbgl/style/sources/geojson_source_impl.hpp>
+#include <mbgl/tile/geojson_tile.hpp>
+#include <mbgl/util/rapidjson.hpp>
+
+#include <mapbox/geojson.hpp>
+#include <mapbox/geojson/rapidjson.hpp>
+#include <mapbox/geojsonvt.hpp>
+#include <mapbox/geojsonvt/convert.hpp>
+#include <supercluster.hpp>
+
+#include <rapidjson/error/en.h>
+
+#include <sstream>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+Result<GeoJSON> convertGeoJSON(const JSValue& value) {
+ try {
+ return mapbox::geojson::convert(value);
+ } catch (const std::exception& ex) {
+ return Error{ ex.what() };
+ }
+}
+} // namespace conversion
+
+GeoJSONSource::Impl::Impl(std::string id_, Source& base_, const GeoJSONOptions options_)
+ : Source::Impl(SourceType::GeoJSON, std::move(id_), base_), options(options_) {
+}
+
+GeoJSONSource::Impl::~Impl() = default;
+
+void GeoJSONSource::Impl::setURL(std::string url) {
+ urlOrGeoJSON = std::move(url);
+}
+
+std::string GeoJSONSource::Impl::getURL() {
+ assert(urlOrGeoJSON.is<std::string>());
+ return urlOrGeoJSON.get<std::string>();
+}
+
+void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) {
+ double scale = util::EXTENT / util::tileSize;
+
+ if (!options.cluster) {
+ mapbox::geojsonvt::Options vtOptions;
+ vtOptions.maxZoom = options.maxzoom;
+ vtOptions.extent = util::EXTENT;
+ vtOptions.buffer = std::round(scale * options.buffer);
+ vtOptions.tolerance = scale * options.tolerance;
+ urlOrGeoJSON = std::make_unique<mapbox::geojsonvt::GeoJSONVT>(geoJSON, vtOptions);
+
+ } else {
+ mapbox::supercluster::Options clusterOptions;
+ clusterOptions.maxZoom = options.clusterMaxZoom;
+ clusterOptions.extent = util::EXTENT;
+ clusterOptions.radius = std::round(scale * options.clusterRadius);
+
+ const auto& features = geoJSON.get<mapbox::geometry::feature_collection<double>>();
+ urlOrGeoJSON =
+ std::make_unique<mapbox::supercluster::Supercluster>(features, clusterOptions);
+ }
+}
+
+void GeoJSONSource::Impl::load(FileSource& fileSource) {
+ if (!urlOrGeoJSON.is<std::string>()) {
+ loaded = true;
+ return;
+ }
+
+ if (req) {
+ return;
+ }
+
+ const std::string& url = urlOrGeoJSON.get<std::string>();
+ req = fileSource.request(Resource::source(url), [this](Response res) {
+ if (res.error) {
+ observer->onSourceError(
+ base, std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ observer->onSourceError(
+ base, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON")));
+ } else {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> d;
+ d.Parse<0>(res.data->c_str());
+
+ if (d.HasParseError()) {
+ std::stringstream message;
+ message << d.GetErrorOffset() << " - "
+ << rapidjson::GetParseError_En(d.GetParseError());
+ observer->onSourceError(base,
+ std::make_exception_ptr(std::runtime_error(message.str())));
+ return;
+ }
+
+ invalidateTiles();
+
+ conversion::Result<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d);
+ if (!geoJSON) {
+ Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s",
+ geoJSON.error().message.c_str());
+ // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for
+ // tiles to load.
+ setGeoJSON(GeoJSON{ FeatureCollection{} });
+ } else {
+ setGeoJSON(*geoJSON);
+ }
+
+ loaded = true;
+ observer->onSourceLoaded(base);
+ }
+ });
+}
+
+Range<uint8_t> GeoJSONSource::Impl::getZoomRange() {
+ assert(loaded);
+ return { 0, options.maxzoom };
+}
+
+std::unique_ptr<Tile> GeoJSONSource::Impl::createTile(const OverscaledTileID& tileID,
+ const UpdateParameters& parameters) {
+ assert(loaded);
+ if (urlOrGeoJSON.is<GeoJSONVTPointer>()) {
+ return std::make_unique<GeoJSONTile>(tileID, base.getID(), parameters,
+ *urlOrGeoJSON.get<GeoJSONVTPointer>());
+ } else {
+ assert(urlOrGeoJSON.is<SuperclusterPointer>());
+ return std::make_unique<GeoJSONTile>(tileID, base.getID(), parameters,
+ *urlOrGeoJSON.get<SuperclusterPointer>());
+ }
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/geojson_source_impl.hpp b/src/mbgl/style/sources/geojson_source_impl.hpp
new file mode 100644
index 0000000000..eb3563e85a
--- /dev/null
+++ b/src/mbgl/style/sources/geojson_source_impl.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/util/variant.hpp>
+
+namespace mbgl {
+
+class AsyncRequest;
+
+namespace style {
+
+class GeoJSONSource::Impl : public Source::Impl {
+public:
+ Impl(std::string id, Source&, const GeoJSONOptions);
+ ~Impl() final;
+
+ void setURL(std::string);
+ void setGeoJSON(const GeoJSON&);
+
+ std::string getURL();
+
+ void load(FileSource&) final;
+
+ uint16_t getTileSize() const final {
+ return util::tileSize;
+ }
+
+private:
+ Range<uint8_t> getZoomRange() final;
+ std::unique_ptr<Tile> createTile(const OverscaledTileID&, const UpdateParameters&) final;
+
+ variant<std::string, GeoJSONVTPointer, SuperclusterPointer> urlOrGeoJSON;
+ std::unique_ptr<AsyncRequest> req;
+
+ GeoJSONOptions options;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/raster_source.cpp b/src/mbgl/style/sources/raster_source.cpp
new file mode 100644
index 0000000000..3c65476df0
--- /dev/null
+++ b/src/mbgl/style/sources/raster_source.cpp
@@ -0,0 +1,12 @@
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/raster_source_impl.hpp>
+
+namespace mbgl {
+namespace style {
+
+RasterSource::RasterSource(std::string id, variant<std::string, Tileset> urlOrTileset, uint16_t tileSize)
+ : Source(SourceType::Raster, std::make_unique<RasterSource::Impl>(std::move(id), *this, std::move(urlOrTileset), tileSize)) {
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/raster_source_impl.cpp b/src/mbgl/style/sources/raster_source_impl.cpp
new file mode 100644
index 0000000000..b727651260
--- /dev/null
+++ b/src/mbgl/style/sources/raster_source_impl.cpp
@@ -0,0 +1,19 @@
+#include <mbgl/style/sources/raster_source_impl.hpp>
+#include <mbgl/tile/raster_tile.hpp>
+
+namespace mbgl {
+namespace style {
+
+RasterSource::Impl::Impl(std::string id_, Source& base_,
+ variant<std::string, Tileset> urlOrTileset_,
+ uint16_t tileSize_)
+ : TileSourceImpl(SourceType::Raster, std::move(id_), base_, std::move(urlOrTileset_), tileSize_) {
+}
+
+std::unique_ptr<Tile> RasterSource::Impl::createTile(const OverscaledTileID& tileID,
+ const UpdateParameters& parameters) {
+ return std::make_unique<RasterTile>(tileID, parameters, tileset);
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/raster_source_impl.hpp b/src/mbgl/style/sources/raster_source_impl.hpp
new file mode 100644
index 0000000000..6f34a050bb
--- /dev/null
+++ b/src/mbgl/style/sources/raster_source_impl.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/tile_source_impl.hpp>
+
+namespace mbgl {
+namespace style {
+
+class RasterSource::Impl : public TileSourceImpl {
+public:
+ Impl(std::string id, Source&, variant<std::string, Tileset>, uint16_t tileSize);
+
+private:
+ std::unique_ptr<Tile> createTile(const OverscaledTileID&, const UpdateParameters&) final;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/vector_source.cpp b/src/mbgl/style/sources/vector_source.cpp
new file mode 100644
index 0000000000..f2a6b166a9
--- /dev/null
+++ b/src/mbgl/style/sources/vector_source.cpp
@@ -0,0 +1,12 @@
+#include <mbgl/style/sources/vector_source.hpp>
+#include <mbgl/style/sources/vector_source_impl.hpp>
+
+namespace mbgl {
+namespace style {
+
+VectorSource::VectorSource(std::string id, variant<std::string, Tileset> urlOrTileset)
+ : Source(SourceType::Vector, std::make_unique<VectorSource::Impl>(std::move(id), *this, std::move(urlOrTileset))) {
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/vector_source_impl.cpp b/src/mbgl/style/sources/vector_source_impl.cpp
new file mode 100644
index 0000000000..efe8afbbea
--- /dev/null
+++ b/src/mbgl/style/sources/vector_source_impl.cpp
@@ -0,0 +1,17 @@
+#include <mbgl/style/sources/vector_source_impl.hpp>
+#include <mbgl/tile/vector_tile.hpp>
+
+namespace mbgl {
+namespace style {
+
+VectorSource::Impl::Impl(std::string id_, Source& base_, variant<std::string, Tileset> urlOrTileset_)
+ : TileSourceImpl(SourceType::Vector, std::move(id_), base_, std::move(urlOrTileset_), util::tileSize) {
+}
+
+std::unique_ptr<Tile> VectorSource::Impl::createTile(const OverscaledTileID& tileID,
+ const UpdateParameters& parameters) {
+ return std::make_unique<VectorTile>(tileID, base.getID(), parameters, tileset);
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/sources/vector_source_impl.hpp b/src/mbgl/style/sources/vector_source_impl.hpp
new file mode 100644
index 0000000000..6726fa6955
--- /dev/null
+++ b/src/mbgl/style/sources/vector_source_impl.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <mbgl/style/sources/vector_source.hpp>
+#include <mbgl/style/tile_source_impl.hpp>
+
+namespace mbgl {
+namespace style {
+
+class VectorSource::Impl : public TileSourceImpl {
+public:
+ Impl(std::string id, Source&, variant<std::string, Tileset>);
+
+private:
+ std::unique_ptr<Tile> createTile(const OverscaledTileID&, const UpdateParameters&) final;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index d9b49d4309..ce33a409f7 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -1,6 +1,6 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/observer.hpp>
-#include <mbgl/style/source.hpp>
+#include <mbgl/style/source_impl.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
@@ -19,7 +19,7 @@
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/renderer/render_item.hpp>
-#include <mbgl/tile/tile.hpp>
+#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/platform/log.hpp>
@@ -47,7 +47,7 @@ Style::Style(FileSource& fileSource_, float pixelRatio)
Style::~Style() {
for (const auto& source : sources) {
- source->setObserver(nullptr);
+ source->baseImpl->setObserver(nullptr);
}
glyphStore->setObserver(nullptr);
@@ -107,15 +107,24 @@ void Style::setJSON(const std::string& json) {
}
void Style::addSource(std::unique_ptr<Source> source) {
- source->setObserver(this);
+ source->baseImpl->setObserver(this);
sources.emplace_back(std::move(source));
}
-std::vector<std::unique_ptr<Layer>> Style::getLayers() const {
- std::vector<std::unique_ptr<Layer>> result;
+void Style::removeSource(const std::string& id) {
+ auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& source) {
+ return source->getID() == id;
+ });
+ if (it == sources.end())
+ throw std::runtime_error("no such source");
+ sources.erase(it);
+}
+
+std::vector<const Layer*> Style::getLayers() const {
+ std::vector<const Layer*> result;
result.reserve(layers.size());
for (const auto& layer : layers) {
- result.push_back(layer->baseImpl->clone());
+ result.push_back(layer.get());
}
return result;
}
@@ -132,6 +141,8 @@ Layer* Style::getLayer(const std::string& id) const {
}
void Style::addLayer(std::unique_ptr<Layer> layer, optional<std::string> before) {
+ // TODO: verify source
+
if (SymbolLayer* symbolLayer = layer->as<SymbolLayer>()) {
if (!symbolLayer->impl->spriteAtlas) {
symbolLayer->impl->spriteAtlas = spriteAtlas.get();
@@ -156,7 +167,7 @@ void Style::update(const UpdateParameters& parameters) {
bool allTilesUpdated = true;
for (const auto& source : sources) {
- if (!source->update(parameters)) {
+ if (!source->baseImpl->update(parameters)) {
allTilesUpdated = false;
}
}
@@ -171,7 +182,7 @@ void Style::update(const UpdateParameters& parameters) {
void Style::cascade(const TimePoint& timePoint, MapMode mode) {
// When in continuous mode, we can either have user- or style-defined
// transitions. Still mode is always immediate.
- static const TransitionOptions immediateTransition;
+ static const TransitionOptions immediateTransition {};
std::vector<ClassID> classIDs;
for (const auto& className : classes) {
@@ -195,7 +206,7 @@ void Style::cascade(const TimePoint& timePoint, MapMode mode) {
void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) {
for (const auto& source : sources) {
- source->enabled = false;
+ source->baseImpl->enabled = false;
}
zoomHistory.update(z, timePoint);
@@ -213,9 +224,9 @@ void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) {
Source* source = getSource(layer->baseImpl->source);
if (source && layer->baseImpl->needsRendering(z)) {
- source->enabled = true;
- if (!source->loaded && !source->isLoading()) {
- source->load(fileSource);
+ source->baseImpl->enabled = true;
+ if (!source->baseImpl->loaded) {
+ source->baseImpl->load(fileSource);
}
}
}
@@ -223,7 +234,7 @@ void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) {
Source* Style::getSource(const std::string& id) const {
const auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& source) {
- return source->id == id;
+ return source->getID() == id;
});
return it != sources.end() ? it->get() : nullptr;
@@ -239,7 +250,7 @@ bool Style::isLoaded() const {
}
for (const auto& source: sources) {
- if (source->enabled && !source->isLoaded()) return false;
+ if (source->baseImpl->enabled && !source->baseImpl->isLoaded()) return false;
}
if (!spriteStore->isLoaded()) {
@@ -249,11 +260,11 @@ bool Style::isLoaded() const {
return true;
}
-RenderData Style::getRenderData() const {
+RenderData Style::getRenderData(MapDebugOptions debugOptions) const {
RenderData result;
for (const auto& source : sources) {
- if (source->enabled) {
+ if (source->baseImpl->enabled) {
result.sources.insert(source.get());
}
}
@@ -263,14 +274,15 @@ RenderData Style::getRenderData() const {
continue;
if (const BackgroundLayer* background = layer->as<BackgroundLayer>()) {
+ if (debugOptions & MapDebugOptions::Overdraw) {
+ // We want to skip glClear optimization in overdraw mode.
+ result.order.emplace_back(*layer);
+ continue;
+ }
const BackgroundPaintProperties& paint = background->impl->paint;
if (layer.get() == layers[0].get() && paint.backgroundPattern.value.from.empty()) {
// This is a solid background. We can use glClear().
- result.backgroundColor = paint.backgroundColor;
- result.backgroundColor[0] *= paint.backgroundOpacity;
- result.backgroundColor[1] *= paint.backgroundOpacity;
- result.backgroundColor[2] *= paint.backgroundOpacity;
- result.backgroundColor[3] *= paint.backgroundOpacity;
+ result.backgroundColor = paint.backgroundColor * paint.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);
@@ -289,9 +301,9 @@ RenderData Style::getRenderData() const {
continue;
}
- for (auto& pair : source->getTiles()) {
+ for (auto& pair : source->baseImpl->getRenderTiles()) {
auto& tile = pair.second;
- if (!tile.data.isRenderable()) {
+ if (!tile.tile.isRenderable()) {
continue;
}
@@ -304,7 +316,7 @@ RenderData Style::getRenderData() const {
// 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 = result.order.rbegin(); it != result.order.rend() && (&it->layer == layer.get()); ++it) {
- if (tile.data.id.isChildOf(it->tile->data.id)) {
+ if (tile.tile.id.isChildOf(it->tile->tile.id)) {
skip = true;
break;
}
@@ -314,7 +326,7 @@ RenderData Style::getRenderData() const {
}
}
- auto bucket = tile.data.getBucket(*layer);
+ auto bucket = tile.tile.getBucket(*layer);
if (bucket) {
result.order.emplace_back(*layer, &tile, bucket);
}
@@ -325,14 +337,17 @@ RenderData Style::getRenderData() const {
}
std::vector<Feature> Style::queryRenderedFeatures(const QueryParameters& parameters) const {
+ std::vector<Feature> result;
std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
for (const auto& source : sources) {
- auto sourceResults = source->queryRenderedFeatures(parameters);
+ auto sourceResults = source->baseImpl->queryRenderedFeatures(parameters);
std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
}
- std::vector<Feature> result;
+ if (resultsByLayer.empty()) {
+ return result;
+ }
// Combine all results based on the style layer order.
for (const auto& layer : layers) {
@@ -356,13 +371,13 @@ float Style::getQueryRadius() const {
void Style::setSourceTileCacheSize(size_t size) {
for (const auto& source : sources) {
- source->setCacheSize(size);
+ source->baseImpl->setCacheSize(size);
}
}
void Style::onLowMemory() {
for (const auto& source : sources) {
- source->onLowMemory();
+ source->baseImpl->onLowMemory();
}
}
@@ -373,7 +388,7 @@ void Style::setObserver(style::Observer* observer_) {
void Style::onGlyphsLoaded(const FontStack& fontStack, const GlyphRange& glyphRange) {
shouldReparsePartialTiles = true;
observer->onGlyphsLoaded(fontStack, glyphRange);
- observer->onResourceLoaded();
+ observer->onNeedsRepaint();
}
void Style::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
@@ -386,13 +401,13 @@ void Style::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRan
void Style::onSourceLoaded(Source& source) {
observer->onSourceLoaded(source);
- observer->onResourceLoaded();
+ observer->onNeedsRepaint();
}
void Style::onSourceError(Source& source, std::exception_ptr error) {
lastError = error;
Log::Error(Event::Style, "Failed to load source %s: %s",
- source.id.c_str(), util::toString(error).c_str());
+ source.getID().c_str(), util::toString(error).c_str());
observer->onSourceError(source, error);
observer->onResourceError(error);
}
@@ -403,25 +418,25 @@ void Style::onTileLoaded(Source& source, const OverscaledTileID& tileID, bool is
}
observer->onTileLoaded(source, tileID, isNewTile);
- observer->onResourceLoaded();
+ observer->onNeedsRepaint();
}
void Style::onTileError(Source& source, const OverscaledTileID& tileID, std::exception_ptr error) {
lastError = error;
Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
- util::toString(tileID).c_str(), source.id.c_str(), util::toString(error).c_str());
+ util::toString(tileID).c_str(), source.getID().c_str(), util::toString(error).c_str());
observer->onTileError(source, tileID, error);
observer->onResourceError(error);
}
-void Style::onPlacementRedone() {
- observer->onResourceLoaded();
+void Style::onNeedsRepaint() {
+ observer->onNeedsRepaint();
}
void Style::onSpriteLoaded() {
shouldReparsePartialTiles = true;
observer->onSpriteLoaded();
- observer->onResourceLoaded();
+ observer->onNeedsRepaint();
}
void Style::onSpriteError(std::exception_ptr error) {
@@ -433,7 +448,7 @@ void Style::onSpriteError(std::exception_ptr error) {
void Style::dumpDebugLogs() const {
for (const auto& source : sources) {
- source->dumpDebugLogs();
+ source->baseImpl->dumpDebugLogs();
}
spriteStore->dumpDebugLogs();
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
index 05a975a95a..35369ffca3 100644
--- a/src/mbgl/style/style.hpp
+++ b/src/mbgl/style/style.hpp
@@ -37,7 +37,7 @@ class Style : public GlyphStoreObserver,
public util::noncopyable {
public:
Style(FileSource&, float pixelRatio);
- ~Style();
+ ~Style() override;
void setJSON(const std::string&);
@@ -60,8 +60,9 @@ public:
Source* getSource(const std::string& id) const;
void addSource(std::unique_ptr<Source>);
+ void removeSource(const std::string& sourceID);
- std::vector<std::unique_ptr<Layer>> getLayers() const;
+ std::vector<const Layer*> getLayers() const;
Layer* getLayer(const std::string& id) const;
void addLayer(std::unique_ptr<Layer>,
optional<std::string> beforeLayerID = {});
@@ -73,7 +74,7 @@ public:
void setClasses(const std::vector<std::string>&, const TransitionOptions& = {});
std::vector<std::string> getClasses() const;
- RenderData getRenderData() const;
+ RenderData getRenderData(MapDebugOptions) const;
std::vector<Feature> queryRenderedFeatures(const QueryParameters&) const;
@@ -112,7 +113,7 @@ private:
void onSourceError(Source&, std::exception_ptr) override;
void onTileLoaded(Source&, const OverscaledTileID&, bool isNewTile) override;
void onTileError(Source&, const OverscaledTileID&, std::exception_ptr) override;
- void onPlacementRedone() override;
+ void onNeedsRepaint() override;
Observer nullObserver;
Observer* observer = &nullObserver;
diff --git a/src/mbgl/style/tile_source_impl.cpp b/src/mbgl/style/tile_source_impl.cpp
new file mode 100644
index 0000000000..3999a88a07
--- /dev/null
+++ b/src/mbgl/style/tile_source_impl.cpp
@@ -0,0 +1,118 @@
+#include <mbgl/style/tile_source_impl.hpp>
+#include <mbgl/style/source_observer.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion/tileset.hpp>
+#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/mapbox.hpp>
+#include <mbgl/storage/file_source.hpp>
+
+#include <rapidjson/document.h>
+#include <rapidjson/error/en.h>
+
+#include <sstream>
+
+namespace mbgl {
+namespace style {
+
+Tileset TileSourceImpl::parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType type, uint16_t tileSize) {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> document;
+ document.Parse<0>(json.c_str());
+
+ if (document.HasParseError()) {
+ std::stringstream message;
+ message << document.GetErrorOffset() << " - " << rapidjson::GetParseError_En(document.GetParseError());
+ throw std::runtime_error(message.str());
+ }
+
+ conversion::Result<Tileset> result = conversion::convert<Tileset>(document);
+ if (!result) {
+ throw std::runtime_error(result.error().message);
+ }
+
+ // TODO: Remove this hack by delivering proper URLs in the TileJSON to begin with.
+ if (util::mapbox::isMapboxURL(sourceURL)) {
+ for (auto& url : (*result).tiles) {
+ url = util::mapbox::canonicalizeTileURL(url, type, tileSize);
+ }
+ }
+
+ return *result;
+}
+
+TileSourceImpl::TileSourceImpl(SourceType type_, std::string id_, Source& base_,
+ variant<std::string, Tileset> urlOrTileset_,
+ uint16_t tileSize_)
+ : Impl(type_, std::move(id_), base_),
+ urlOrTileset(std::move(urlOrTileset_)),
+ tileSize(tileSize_) {
+}
+
+TileSourceImpl::~TileSourceImpl() = default;
+
+void TileSourceImpl::load(FileSource& fileSource) {
+ if (urlOrTileset.is<Tileset>()) {
+ tileset = urlOrTileset.get<Tileset>();
+ loaded = true;
+ return;
+ }
+
+ if (req) {
+ return;
+ }
+
+ const std::string& url = urlOrTileset.get<std::string>();
+ req = fileSource.request(Resource::source(url), [this, url](Response res) {
+ if (res.error) {
+ observer->onSourceError(base, std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ return;
+ } else if (res.noContent) {
+ observer->onSourceError(base, std::make_exception_ptr(std::runtime_error("unexpectedly empty TileJSON")));
+ } else {
+ Tileset newTileset;
+
+ // Create a new copy of the Tileset object that holds the base values we've parsed
+ // from the stylesheet. Then merge in the values parsed from the TileJSON we retrieved
+ // via the URL.
+ try {
+ newTileset = parseTileJSON(*res.data, url, type, tileSize);
+ } catch (...) {
+ observer->onSourceError(base, std::current_exception());
+ return;
+ }
+
+ // Check whether previous information specifies different tile
+ if (tileset.tiles != newTileset.tiles) {
+ // Tile URLs changed: force tiles to be reloaded.
+ invalidateTiles();
+
+ // Tile size changed: We need to recalculate the tiles we need to load because we
+ // might have to load tiles for a different zoom level
+ // This is done automatically when we trigger the onSourceLoaded observer below.
+
+ // Min/Max zoom changed: We need to recalculate what tiles to load, if we have tiles
+ // loaded that are outside the new zoom range
+ // This is done automatically when we trigger the onSourceLoaded observer below.
+
+ // Attribution changed: We need to notify the embedding application that this
+ // changed. See https://github.com/mapbox/mapbox-gl-native/issues/2723
+ // This is not yet implemented.
+
+ // Center/bounds changed: We're not using these values currently
+ }
+
+ tileset = newTileset;
+ loaded = true;
+
+ observer->onSourceLoaded(base);
+ }
+ });
+}
+
+Range<uint8_t> TileSourceImpl::getZoomRange() {
+ assert(loaded);
+ return tileset.zoomRange;
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/tile_source_impl.hpp b/src/mbgl/style/tile_source_impl.hpp
new file mode 100644
index 0000000000..8f420a15d5
--- /dev/null
+++ b/src/mbgl/style/tile_source_impl.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/optional.hpp>
+
+namespace mbgl {
+
+class AsyncRequest;
+
+namespace style {
+
+/*
+ Shared implementation for VectorSource and RasterSource. Should eventually
+ be refactored to use composition rather than inheritance.
+*/
+class TileSourceImpl : public Source::Impl {
+public:
+ static Tileset parseTileJSON(const std::string& json, const std::string& sourceURL, SourceType, uint16_t tileSize);
+
+ TileSourceImpl(SourceType, std::string id, Source&,
+ variant<std::string, Tileset> urlOrTileset,
+ uint16_t tileSize);
+ ~TileSourceImpl() override;
+
+ void load(FileSource&) final;
+
+ uint16_t getTileSize() const final {
+ return tileSize;
+ }
+
+ const variant<std::string, Tileset>& getURLOrTileset() const {
+ return urlOrTileset;
+ }
+
+protected:
+ Range<uint8_t> getZoomRange() final;
+
+ const variant<std::string, Tileset> urlOrTileset;
+ const uint16_t tileSize;
+
+ Tileset tileset;
+ std::unique_ptr<AsyncRequest> req;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp
new file mode 100644
index 0000000000..be0b217134
--- /dev/null
+++ b/src/mbgl/style/types.cpp
@@ -0,0 +1,92 @@
+#include <mbgl/style/types.hpp>
+#include <mbgl/util/enum.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+MBGL_DEFINE_ENUM(SourceType, {
+ { SourceType::Vector, "vector" },
+ { SourceType::Raster, "raster" },
+ { SourceType::GeoJSON, "geojson" },
+ { SourceType::Video, "video" },
+ { SourceType::Annotations, "annotations" },
+});
+
+MBGL_DEFINE_ENUM(VisibilityType, {
+ { VisibilityType::Visible, "visible" },
+ { VisibilityType::None, "none" },
+});
+
+MBGL_DEFINE_ENUM(TranslateAnchorType, {
+ { TranslateAnchorType::Map, "map" },
+ { TranslateAnchorType::Viewport, "viewport" },
+});
+
+MBGL_DEFINE_ENUM(RotateAnchorType, {
+ { RotateAnchorType::Map, "map" },
+ { RotateAnchorType::Viewport, "viewport" },
+});
+
+MBGL_DEFINE_ENUM(CirclePitchScaleType, {
+ { CirclePitchScaleType::Map, "map" },
+ { CirclePitchScaleType::Viewport, "viewport" },
+});
+
+MBGL_DEFINE_ENUM(LineCapType, {
+ { LineCapType::Round, "round" },
+ { LineCapType::Butt, "butt" },
+ { LineCapType::Square, "square" },
+});
+
+MBGL_DEFINE_ENUM(LineJoinType, {
+ { LineJoinType::Miter, "miter" },
+ { LineJoinType::Bevel, "bevel" },
+ { LineJoinType::Round, "round" },
+ { LineJoinType::FakeRound, "fakeround" },
+ { LineJoinType::FlipBevel, "flipbevel" },
+});
+
+MBGL_DEFINE_ENUM(SymbolPlacementType, {
+ { SymbolPlacementType::Point, "point" },
+ { 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(TextJustifyType, {
+ { TextJustifyType::Center, "center" },
+ { TextJustifyType::Left, "left" },
+ { TextJustifyType::Right, "right" },
+});
+
+MBGL_DEFINE_ENUM(TextTransformType, {
+ { TextTransformType::None, "none" },
+ { TextTransformType::Uppercase, "uppercase" },
+ { TextTransformType::Lowercase, "lowercase" },
+});
+
+MBGL_DEFINE_ENUM(AlignmentType, {
+ { AlignmentType::Map, "map" },
+ { AlignmentType::Viewport, "viewport" },
+ { AlignmentType::Undefined, "undefined" },
+});
+
+MBGL_DEFINE_ENUM(IconTextFitType, {
+ { IconTextFitType::None, "none" },
+ { IconTextFitType::Both, "both" },
+ { IconTextFitType::Width, "width" },
+ { IconTextFitType::Height, "height" },
+});
+
+} // namespace mbgl
diff --git a/src/mbgl/style/update_parameters.hpp b/src/mbgl/style/update_parameters.hpp
index 9faaa32d6f..bdae4f42b0 100644
--- a/src/mbgl/style/update_parameters.hpp
+++ b/src/mbgl/style/update_parameters.hpp
@@ -7,7 +7,6 @@ namespace mbgl {
class TransformState;
class Worker;
class FileSource;
-namespace gl { class TexturePool; }
class AnnotationManager;
namespace style {
@@ -22,18 +21,16 @@ public:
const TransformState& transformState_,
Worker& worker_,
FileSource& fileSource_,
- gl::TexturePool& texturePool_,
bool shouldReparsePartialTiles_,
const MapMode mode_,
AnnotationManager& annotationManager_,
Style& style_)
: pixelRatio(pixelRatio_),
debugOptions(debugOptions_),
- animationTime(animationTime_),
+ animationTime(std::move(animationTime_)),
transformState(transformState_),
worker(worker_),
fileSource(fileSource_),
- texturePool(texturePool_),
shouldReparsePartialTiles(shouldReparsePartialTiles_),
mode(mode_),
annotationManager(annotationManager_),
@@ -45,7 +42,6 @@ public:
const TransformState& transformState;
Worker& worker;
FileSource& fileSource;
- gl::TexturePool& texturePool;
bool shouldReparsePartialTiles;
const MapMode mode;
AnnotationManager& annotationManager;
diff --git a/src/mbgl/text/check_max_angle.hpp b/src/mbgl/text/check_max_angle.hpp
index 5e1ff6f049..008e70b5da 100644
--- a/src/mbgl/text/check_max_angle.hpp
+++ b/src/mbgl/text/check_max_angle.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
namespace mbgl {
diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp
index 57a9da0440..74a41099ff 100644
--- a/src/mbgl/text/collision_feature.cpp
+++ b/src/mbgl/text/collision_feature.cpp
@@ -5,9 +5,9 @@ namespace mbgl {
CollisionFeature::CollisionFeature(const GeometryCoordinates &line, const Anchor &anchor,
const float top, const float bottom, const float left, const float right,
- const float boxScale, const float padding, const bool alongLine, const IndexedSubfeature& indexedFeature_,
+ const float boxScale, const float padding, const bool alongLine, IndexedSubfeature indexedFeature_,
const bool straight)
- : indexedFeature(indexedFeature_) {
+ : indexedFeature(std::move(indexedFeature_)) {
if (top == 0 && bottom == 0 && left == 0 && right == 0) return;
diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp
index 0fcc083a19..5c0095bc6d 100644
--- a/src/mbgl/text/collision_feature.hpp
+++ b/src/mbgl/text/collision_feature.hpp
@@ -2,7 +2,7 @@
#include <mbgl/geometry/anchor.hpp>
#include <mbgl/text/shaping.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <vector>
@@ -10,8 +10,8 @@
namespace mbgl {
class CollisionBox {
public:
- explicit CollisionBox(const Point<float> &_anchor, float _x1, float _y1, float _x2, float _y2, float _maxScale) :
- anchor(_anchor), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {}
+ explicit CollisionBox(Point<float> _anchor, float _x1, float _y1, float _x2, float _y2, float _maxScale) :
+ anchor(std::move(_anchor)), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {}
// the box is centered around the anchor point
Point<float> anchor;
@@ -33,7 +33,7 @@ namespace mbgl {
class CollisionFeature {
public:
// for text
- inline explicit CollisionFeature(const GeometryCoordinates &line, const Anchor &anchor,
+ explicit CollisionFeature(const GeometryCoordinates &line, const Anchor &anchor,
const Shaping &shapedText,
const float boxScale, const float padding, const bool alongLine, const IndexedSubfeature& indexedFeature_)
: CollisionFeature(line, anchor,
@@ -41,7 +41,7 @@ namespace mbgl {
boxScale, padding, alongLine, indexedFeature_, false) {}
// for icons
- inline explicit CollisionFeature(const GeometryCoordinates &line, const Anchor &anchor,
+ explicit CollisionFeature(const GeometryCoordinates &line, const Anchor &anchor,
const PositionedIcon &shapedIcon,
const float boxScale, const float padding, const bool alongLine, const IndexedSubfeature& indexedFeature_)
: CollisionFeature(line, anchor,
@@ -51,7 +51,7 @@ namespace mbgl {
explicit CollisionFeature(const GeometryCoordinates &line, const Anchor &anchor,
const float top, const float bottom, const float left, const float right,
const float boxScale, const float padding, const bool alongLine,
- const IndexedSubfeature&, const bool straight);
+ IndexedSubfeature, const bool straight);
std::vector<CollisionBox> boxes;
diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp
index e7824cde3c..982dff1688 100644
--- a/src/mbgl/text/collision_tile.cpp
+++ b/src/mbgl/text/collision_tile.cpp
@@ -9,7 +9,7 @@ namespace mbgl {
auto infinity = std::numeric_limits<float>::infinity();
-CollisionTile::CollisionTile(PlacementConfig config_) : config(config_),
+CollisionTile::CollisionTile(PlacementConfig config_) : config(std::move(config_)),
edges({{
// left
CollisionBox(Point<float>(0, 0), 0, -infinity, 0, infinity, infinity),
diff --git a/src/mbgl/text/get_anchors.cpp b/src/mbgl/text/get_anchors.cpp
index c55f238e52..ce45e05d9c 100644
--- a/src/mbgl/text/get_anchors.cpp
+++ b/src/mbgl/text/get_anchors.cpp
@@ -80,25 +80,25 @@ Anchors getAnchors(const GeometryCoordinates &line, float spacing,
const float angleWindowSize = (textLeft - textRight) != 0.0f ?
3.0f / 5.0f * glyphSize * boxScale :
0;
-
+
const float labelLength = fmax(textRight - textLeft, iconRight - iconLeft);
-
+
// Is the line continued from outside the tile boundary?
const bool continuedLine = (line[0].x == 0 || line[0].x == util::EXTENT || line[0].y == 0 || line[0].y == util::EXTENT);
-
+
// Is the label long, relative to the spacing?
// If so, adjust the spacing so there is always a minimum space of `spacing / 4` between label edges.
if (spacing - labelLength * boxScale < spacing / 4) {
spacing = labelLength * boxScale + spacing / 4;
}
-
+
// Offset the first anchor by:
// Either half the label length plus a fixed extra offset if the line is not continued
// Or half the spacing if the line is continued.
// For non-continued lines, add a bit of fixed extra offset to avoid collisions at T intersections.
const float fixedExtraOffset = glyphSize * 2;
-
+
const float offset = !continuedLine ?
std::fmod((labelLength / 2 + fixedExtraOffset) * boxScale * overscaling, spacing) :
std::fmod(spacing / 2 * overscaling, spacing);
diff --git a/src/mbgl/text/get_anchors.hpp b/src/mbgl/text/get_anchors.hpp
index 9a0a5a650c..b61f8fe0dc 100644
--- a/src/mbgl/text/get_anchors.hpp
+++ b/src/mbgl/text/get_anchors.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/geometry/anchor.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/util/math.hpp>
namespace mbgl {
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index b5891831d5..975dc4ad23 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -28,10 +28,9 @@ struct GlyphMetrics {
};
struct Glyph {
- inline explicit Glyph() : rect(0, 0, 0, 0), metrics() {}
- inline explicit Glyph(const Rect<uint16_t> &rect_,
- const GlyphMetrics &metrics_)
- : rect(rect_), metrics(metrics_) {}
+ explicit Glyph() : rect(0, 0, 0, 0), metrics() {}
+ explicit Glyph(Rect<uint16_t> rect_, GlyphMetrics metrics_)
+ : rect(std::move(rect_)), metrics(std::move(metrics_)) {}
operator bool() const {
return metrics || rect.hasArea();
@@ -45,7 +44,7 @@ typedef std::map<uint32_t, Glyph> GlyphPositions;
class PositionedGlyph {
public:
- inline explicit PositionedGlyph(uint32_t glyph_, float x_, float y_)
+ explicit PositionedGlyph(uint32_t glyph_, float x_, float y_)
: glyph(glyph_), x(x_), y(y_) {}
uint32_t glyph = 0;
@@ -55,8 +54,8 @@ public:
class Shaping {
public:
- inline explicit Shaping() : top(0), bottom(0), left(0), right(0) {}
- inline explicit Shaping(float x, float y, std::u32string text_)
+ explicit Shaping() : top(0), bottom(0), left(0), right(0) {}
+ explicit Shaping(float x, float y, std::u32string text_)
: text(std::move(text_)), top(y), bottom(y), left(x), right(x) {}
std::vector<PositionedGlyph> positionedGlyphs;
std::u32string text;
diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp
index 9046464c5c..b4dac4ce01 100644
--- a/src/mbgl/text/glyph_pbf.cpp
+++ b/src/mbgl/text/glyph_pbf.cpp
@@ -53,7 +53,7 @@ void parseGlyphPBF(mbgl::GlyphSet& glyphSet, const std::string& data) {
}
}
- glyphSet.insert(glyph.id, glyph);
+ glyphSet.insert(glyph.id, std::move(glyph));
}
}
}
diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp
index c238cbb5d7..85f4b62e6f 100644
--- a/src/mbgl/text/glyph_pbf.hpp
+++ b/src/mbgl/text/glyph_pbf.hpp
@@ -2,10 +2,10 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/text/glyph_store.hpp>
-#include <mbgl/util/atomic.hpp>
#include <mbgl/util/font_stack.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include <atomic>
#include <functional>
#include <string>
#include <memory>
@@ -29,7 +29,7 @@ public:
}
private:
- util::Atomic<bool> parsed;
+ std::atomic<bool> parsed;
std::unique_ptr<AsyncRequest> req;
GlyphStoreObserver* observer = nullptr;
};
diff --git a/src/mbgl/text/glyph_set.cpp b/src/mbgl/text/glyph_set.cpp
index 8bd7e4ae03..0875a83850 100644
--- a/src/mbgl/text/glyph_set.cpp
+++ b/src/mbgl/text/glyph_set.cpp
@@ -6,11 +6,11 @@
namespace mbgl {
-void GlyphSet::insert(uint32_t id, const SDFGlyph &glyph) {
+void GlyphSet::insert(uint32_t id, SDFGlyph&& glyph) {
auto it = sdfs.find(id);
if (it == sdfs.end()) {
// Glyph doesn't exist yet.
- sdfs.emplace(id, glyph);
+ sdfs.emplace(id, std::move(glyph));
} else if (it->second.metrics == glyph.metrics) {
if (it->second.bitmap != glyph.bitmap) {
// The actual bitmap was updated; this is unsupported.
@@ -18,7 +18,7 @@ void GlyphSet::insert(uint32_t id, const SDFGlyph &glyph) {
}
// At least try to update it in case it's currently unsused.
// If it is already used; we won't attempt to update the glyph atlas texture.
- it->second.bitmap = glyph.bitmap;
+ it->second.bitmap = std::move(glyph.bitmap);
} else {
// The metrics were updated; this is unsupported.
Log::Warning(Event::Glyph, "Modified glyph has different metrics");
diff --git a/src/mbgl/text/glyph_set.hpp b/src/mbgl/text/glyph_set.hpp
index 77e2cb1c95..37ffdb070a 100644
--- a/src/mbgl/text/glyph_set.hpp
+++ b/src/mbgl/text/glyph_set.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class GlyphSet {
public:
- void insert(uint32_t id, const SDFGlyph &glyph);
+ void insert(uint32_t id, SDFGlyph&&);
const std::map<uint32_t, SDFGlyph> &getSDFs() const;
const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight,
float horizontalAlign, float verticalAlign, float justify,
diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp
index 75345b89e8..7e61cabc24 100644
--- a/src/mbgl/text/placement_config.hpp
+++ b/src/mbgl/text/placement_config.hpp
@@ -4,15 +4,15 @@ namespace mbgl {
class PlacementConfig {
public:
- inline PlacementConfig(float angle_ = 0, float pitch_ = 0, bool debug_ = false)
+ PlacementConfig(float angle_ = 0, float pitch_ = 0, bool debug_ = false)
: angle(angle_), pitch(pitch_), debug(debug_) {
}
- inline bool operator==(const PlacementConfig& rhs) const {
+ bool operator==(const PlacementConfig& rhs) const {
return angle == rhs.angle && pitch == rhs.pitch && debug == rhs.debug;
}
- inline bool operator!=(const PlacementConfig& rhs) const {
+ bool operator!=(const PlacementConfig& rhs) const {
return !operator==(rhs);
}
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index ce320791c3..727b86f610 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -1,6 +1,6 @@
#include <mbgl/text/quads.hpp>
#include <mbgl/text/shaping.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/geometry/anchor.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/util/math.hpp>
@@ -15,7 +15,7 @@ const float globalMinScale = 0.5f; // underscale by 1 zoom level
SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon,
const GeometryCoordinates& line, const SymbolLayoutProperties& layout,
- const bool alongLine) {
+ const bool alongLine, const Shaping& shapedText) {
auto image = *(shapedIcon.image);
@@ -24,11 +24,43 @@ SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon,
auto right = left + image.pos.w / image.relativePixelRatio;
auto top = shapedIcon.top - border;
auto bottom = top + image.pos.h / image.relativePixelRatio;
- Point<float> tl{left, top};
- Point<float> tr{right, top};
- Point<float> br{right, bottom};
- Point<float> bl{left, bottom};
-
+ Point<float> tl;
+ Point<float> tr;
+ Point<float> br;
+ Point<float> bl;
+
+ if (layout.iconTextFit != IconTextFitType::None && shapedText) {
+ auto iconWidth = right - left;
+ auto iconHeight = bottom - top;
+ auto size = layout.textSize / 24.0f;
+ auto textLeft = shapedText.left * size;
+ auto textRight = shapedText.right * size;
+ auto textTop = shapedText.top * size;
+ auto textBottom = shapedText.bottom * size;
+ auto textWidth = textRight - textLeft;
+ auto textHeight = textBottom - textTop;;
+ auto padT = layout.iconTextFitPadding.value[0];
+ auto padR = layout.iconTextFitPadding.value[1];
+ auto padB = layout.iconTextFitPadding.value[2];
+ auto padL = layout.iconTextFitPadding.value[3];
+ auto offsetY = layout.iconTextFit == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0;
+ auto offsetX = layout.iconTextFit == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0;
+ auto width = layout.iconTextFit == IconTextFitType::Width || layout.iconTextFit == IconTextFitType::Both ? textWidth : iconWidth;
+ auto height = layout.iconTextFit == IconTextFitType::Height || layout.iconTextFit == IconTextFitType::Both ? textHeight : iconHeight;
+ left = textLeft + offsetX - padL;
+ top = textTop + offsetY - padT;
+ right = textLeft + offsetX + padR + width;
+ bottom = textTop + offsetY + padB + height;
+ tl = {left, top};
+ tr = {right, top};
+ br = {right, bottom};
+ bl = {left, bottom};
+ } else {
+ tl = {left, top};
+ tr = {right, top};
+ br = {right, bottom};
+ bl = {left, bottom};
+ }
float angle = layout.iconRotate * util::DEG2RAD;
if (alongLine) {
@@ -57,15 +89,15 @@ SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon,
}
SymbolQuads quads;
- quads.emplace_back(tl, tr, bl, br, image.pos, 0, anchor.point, globalMinScale, std::numeric_limits<float>::infinity());
+ quads.emplace_back(tl, tr, bl, br, image.pos, 0, 0, anchor.point, globalMinScale, std::numeric_limits<float>::infinity());
return quads;
}
struct GlyphInstance {
- explicit GlyphInstance(const Point<float> &anchorPoint_) : anchorPoint(anchorPoint_) {}
- explicit GlyphInstance(const Point<float> &anchorPoint_, float offset_, float minScale_, float maxScale_,
+ explicit GlyphInstance(Point<float> anchorPoint_) : anchorPoint(std::move(anchorPoint_)) {}
+ explicit GlyphInstance(Point<float> anchorPoint_, float offset_, float minScale_, float maxScale_,
float angle_)
- : anchorPoint(anchorPoint_), offset(offset_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {}
+ : anchorPoint(std::move(anchorPoint_)), offset(offset_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {}
const Point<float> anchorPoint;
const float offset = 0.0f;
@@ -102,8 +134,6 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor &
float angle = std::atan2(end.y - newAnchorPoint.y, end.x - newAnchorPoint.x);
if (!forward)
angle += M_PI;
- if (upsideDown)
- angle += M_PI;
glyphs = GlyphInstance{
/* anchor */ newAnchorPoint,
@@ -188,12 +218,11 @@ SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText,
Point<float> tr = otr;
Point<float> bl = obl;
Point<float> br = obr;
- const float angle = instance.angle + textRotate;
- if (angle) {
+ if (textRotate) {
// Compute the transformation matrix.
- float angle_sin = std::sin(angle);
- float angle_cos = std::cos(angle);
+ float angle_sin = std::sin(textRotate);
+ float angle_cos = std::cos(textRotate);
std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
tl = util::matrixMultiply(matrix, tl);
@@ -205,8 +234,9 @@ SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText,
// Prevent label from extending past the end of the line
const float glyphMinScale = std::max(instance.minScale, anchor.scale);
- const float glyphAngle = std::fmod((anchor.angle + textRotate + instance.offset + 2 * M_PI), (2 * M_PI));
- quads.emplace_back(tl, tr, bl, br, rect, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale);
+ const float anchorAngle = std::fmod((anchor.angle + instance.offset + 2 * M_PI), (2 * M_PI));
+ const float glyphAngle = std::fmod((instance.angle + instance.offset + 2 * M_PI), (2 * M_PI));
+ quads.emplace_back(tl, tr, bl, br, rect, anchorAngle, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale);
}
diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp
index 0627647f4c..dd64af682a 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -1,7 +1,7 @@
#pragma once
#include <mbgl/text/glyph.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <vector>
@@ -12,26 +12,26 @@ class PositionedIcon;
namespace style {
class SymbolLayoutProperties;
-}
+} // namespace style
struct SymbolQuad {
- explicit SymbolQuad(const Point<float> &tl_, const Point<float> &tr_,
- const Point<float> &bl_, const Point<float> &br_,
- const Rect<uint16_t> &tex_, float angle_, const Point<float> &anchorPoint_,
+ explicit SymbolQuad(Point<float> tl_, Point<float> tr_, Point<float> bl_, Point<float> br_,
+ Rect<uint16_t> tex_, float anchorAngle_, float glyphAngle_, Point<float> anchorPoint_,
float minScale_, float maxScale_)
- : tl(tl_),
- tr(tr_),
- bl(bl_),
- br(br_),
- tex(tex_),
- angle(angle_),
- anchorPoint(anchorPoint_),
+ : tl(std::move(tl_)),
+ tr(std::move(tr_)),
+ bl(std::move(bl_)),
+ br(std::move(br_)),
+ tex(std::move(tex_)),
+ anchorAngle(anchorAngle_),
+ glyphAngle(glyphAngle_),
+ anchorPoint(std::move(anchorPoint_)),
minScale(minScale_),
maxScale(maxScale_) {}
Point<float> tl, tr, bl, br;
Rect<uint16_t> tex;
- float angle;
+ float anchorAngle, glyphAngle;
Point<float> anchorPoint;
float minScale, maxScale;
};
@@ -40,7 +40,7 @@ typedef std::vector<SymbolQuad> SymbolQuads;
SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon,
const GeometryCoordinates& line, const style::SymbolLayoutProperties&,
- const bool alongLine);
+ const bool alongLine, const Shaping& shapedText);
SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText,
const float boxScale, const GeometryCoordinates& line, const style::SymbolLayoutProperties&,
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index f81230acec..cd5e8105fd 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -11,12 +11,12 @@ struct SpriteAtlasElement;
namespace style {
class SymbolLayoutProperties;
-}
+} // namespace style
class PositionedIcon {
public:
- inline explicit PositionedIcon() {}
- inline explicit PositionedIcon(const SpriteAtlasElement& _image,
+ explicit PositionedIcon() {}
+ explicit PositionedIcon(const SpriteAtlasElement& _image,
float _top, float _bottom, float _left, float _right) :
image(_image), top(_top), bottom(_bottom), left(_left), right(_right) {}
diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp
index 90147ffd87..ab596bd9ba 100644
--- a/src/mbgl/tile/geojson_tile.cpp
+++ b/src/mbgl/tile/geojson_tile.cpp
@@ -1,12 +1,108 @@
#include <mbgl/tile/geojson_tile.hpp>
-#include <mbgl/storage/file_source.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/style/update_parameters.hpp>
+
#include <mapbox/geojsonvt.hpp>
+#include <supercluster.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:
+ GeoJSONTileFeature(FeatureType, GeometryCollection&&, PropertyMap&&);
+ FeatureType getType() const override;
+ optional<Value> getValue(const std::string&) const override;
+ PropertyMap getProperties() const override { return properties; }
+ GeometryCollection getGeometries() const override;
+
+private:
+ const FeatureType type;
+ const GeometryCollection geometries;
+ const PropertyMap properties;
+};
+
+class GeoJSONTileLayer : public GeometryTileLayer {
+public:
+ using Features = std::vector<std::shared_ptr<const GeoJSONTileFeature>>;
+
+ GeoJSONTileLayer(Features&&);
+ std::size_t featureCount() const override;
+ util::ptr<const GeometryTileFeature> getFeature(std::size_t) const override;
+ std::string getName() const override { return ""; };
+
+private:
+ const Features features;
+};
+
+class GeoJSONTileData : public GeometryTileData {
+public:
+ GeoJSONTileData(std::shared_ptr<GeoJSONTileLayer>);
+ util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
+
+private:
+ const std::shared_ptr<GeoJSONTileLayer> layer;
+};
+
+// Converts the geojsonvt::Tile to a a GeoJSONTile. They have a differing internal structure.
+std::unique_ptr<GeoJSONTileData> convertTile(const mapbox::geometry::feature_collection<int16_t>& features) {
+ std::shared_ptr<GeoJSONTileLayer> layer;
+
+ if (!features.empty()) {
+ std::vector<std::shared_ptr<const GeoJSONTileFeature>> convertedFeatures;
+
+ ToFeatureType toFeatureType;
+ ToGeometryCollection toGeometryCollection;
+
+ for (auto& feature : features) {
+ const FeatureType featureType = apply_visitor(toFeatureType, feature.geometry);
+
+ if (featureType == FeatureType::Unknown) {
+ continue;
+ }
+
+ GeometryCollection geometry = apply_visitor(toGeometryCollection, feature.geometry);
+
+ // https://github.com/mapbox/geojson-vt-cpp/issues/44
+ if (featureType == FeatureType::Polygon) {
+ geometry = fixupPolygons(geometry);
+ }
+
+ PropertyMap properties = feature.properties;
+
+ convertedFeatures.emplace_back(std::make_shared<GeoJSONTileFeature>(
+ featureType, std::move(geometry), std::move(properties)));
+ }
+
+ layer = std::make_unique<GeoJSONTileLayer>(std::move(convertedFeatures));
+ }
+
+ return std::make_unique<GeoJSONTileData>(layer);
+}
+
+GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
+ std::string sourceID_,
+ const style::UpdateParameters& parameters,
+ mapbox::geojsonvt::GeoJSONVT& geojsonvt)
+ : GeometryTile(overscaledTileID, sourceID_, parameters.style, parameters.mode) {
+ setData(convertTile(geojsonvt.getTile(id.canonical.z, id.canonical.x, id.canonical.y).features));
+}
+
+GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
+ std::string sourceID_,
+ const style::UpdateParameters& parameters,
+ mapbox::supercluster::Supercluster& supercluster)
+ : GeometryTile(overscaledTileID, sourceID_, parameters.style, parameters.mode) {
+ setData(convertTile(supercluster.getTile(id.canonical.z, id.canonical.x, id.canonical.y)));
+}
+
+void GeoJSONTile::setNecessity(Necessity) {}
+
GeoJSONTileFeature::GeoJSONTileFeature(FeatureType type_,
GeometryCollection&& geometries_,
- Feature::property_map&& properties_)
+ PropertyMap&& properties_)
: type(type_), geometries(std::move(geometries_)), properties(std::move(properties_)) {
}
@@ -37,105 +133,12 @@ util::ptr<const GeometryTileFeature> GeoJSONTileLayer::getFeature(std::size_t i)
return features[i];
}
-GeoJSONTile::GeoJSONTile(std::shared_ptr<GeoJSONTileLayer> layer_) : layer(std::move(layer_)) {
+GeoJSONTileData::GeoJSONTileData(std::shared_ptr<GeoJSONTileLayer> layer_) : layer(std::move(layer_)) {
}
-util::ptr<GeometryTileLayer> GeoJSONTile::getLayer(const std::string&) const {
+util::ptr<GeometryTileLayer> GeoJSONTileData::getLayer(const std::string&) const {
// We're ignoring the layer name because GeoJSON tiles only have one layer.
return layer;
}
-// Converts the geojsonvt::Tile to a a GeoJSONTile. They have a differing internal structure.
-std::unique_ptr<GeoJSONTile> convertTile(const mapbox::geojsonvt::Tile& tile) {
- std::shared_ptr<GeoJSONTileLayer> layer;
-
- if (tile) {
- std::vector<std::shared_ptr<const GeoJSONTileFeature>> features;
- GeometryCoordinates line;
-
- for (auto& feature : tile.features) {
- const FeatureType featureType =
- (feature.type == mapbox::geojsonvt::TileFeatureType::Point
- ? FeatureType::Point
- : (feature.type == mapbox::geojsonvt::TileFeatureType::LineString
- ? FeatureType::LineString
- : (feature.type == mapbox::geojsonvt::TileFeatureType::Polygon
- ? FeatureType::Polygon
- : FeatureType::Unknown)));
- if (featureType == FeatureType::Unknown) {
- continue;
- }
-
- GeometryCollection geometry;
-
- // Flatten the geometry; GeoJSONVT distinguishes between a Points array and Rings array
- // (Points = GeoJSON types Point, MultiPoint, LineString)
- // (Rings = GeoJSON types MultiLineString, Polygon, MultiPolygon)
- // However, in Mapbox GL, we use one structure for both types, and just have one outer
- // element for Points.
- if (feature.tileGeometry.is<mapbox::geojsonvt::TilePoints>()) {
- line.clear();
- for (auto& point : feature.tileGeometry.get<mapbox::geojsonvt::TilePoints>()) {
- line.emplace_back(point.x, point.y);
- }
- geometry.emplace_back(std::move(line));
- } else if (feature.tileGeometry.is<mapbox::geojsonvt::TileRings>()) {
- for (auto& ring : feature.tileGeometry.get<mapbox::geojsonvt::TileRings>()) {
- line.clear();
- for (auto& point : ring) {
- line.emplace_back(point.x, point.y);
- }
- geometry.emplace_back(std::move(line));
- }
- }
-
- // https://github.com/mapbox/geojson-vt-cpp/issues/44
- if (featureType == FeatureType::Polygon) {
- geometry = fixupPolygons(geometry);
- }
-
- Feature::property_map properties { feature.tags.begin(), feature.tags.end() };
-
- features.emplace_back(std::make_shared<GeoJSONTileFeature>(
- featureType, std::move(geometry), std::move(properties)));
- }
-
- layer = std::make_unique<GeoJSONTileLayer>(std::move(features));
- }
-
- return std::make_unique<GeoJSONTile>(layer);
-}
-
-GeoJSONTileMonitor::GeoJSONTileMonitor(mapbox::geojsonvt::GeoJSONVT* geojsonvt_,
- const OverscaledTileID& id)
- : tileID(id), geojsonvt(geojsonvt_) {
-}
-
-GeoJSONTileMonitor::~GeoJSONTileMonitor() = default;
-
-// A monitor can have its GeoJSONVT object swapped out (e.g. when loading a new GeoJSON file).
-// In that case, we're sending new notifications to all observers.
-void GeoJSONTileMonitor::setGeoJSONVT(mapbox::geojsonvt::GeoJSONVT* vt) {
- // Don't duplicate notifications in case of nil changes.
- if (geojsonvt != vt) {
- geojsonvt = vt;
- update();
- }
-}
-
-void GeoJSONTileMonitor::update() {
- if (geojsonvt) {
- auto tile = convertTile(
- geojsonvt->getTile(tileID.canonical.z, tileID.canonical.x, tileID.canonical.y));
- callback(nullptr, std::move(tile), {}, {});
- }
-}
-
-std::unique_ptr<AsyncRequest>
-GeoJSONTileMonitor::monitorTile(const GeometryTileMonitor::Callback& cb) {
- callback = cb;
- update();
- return nullptr;
-}
-
} // namespace mbgl
diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp
index 04ecbc9a24..422101b767 100644
--- a/src/mbgl/tile/geojson_tile.hpp
+++ b/src/mbgl/tile/geojson_tile.hpp
@@ -1,75 +1,38 @@
#pragma once
#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/tile/tile_id.hpp>
-
-#include <unordered_map>
namespace mapbox {
+
namespace geojsonvt {
class GeoJSONVT;
} // namespace geojsonvt
-} // namespace mapbox
-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.
+namespace supercluster {
+class Supercluster;
+} // namespace supercluster
-class GeoJSONTileFeature : public GeometryTileFeature {
-public:
- GeoJSONTileFeature(FeatureType, GeometryCollection&&, Feature::property_map&&);
- FeatureType getType() const override;
- optional<Value> getValue(const std::string&) const override;
- Feature::property_map getProperties() const override { return properties; }
- GeometryCollection getGeometries() const override;
-
-private:
- const FeatureType type;
- const GeometryCollection geometries;
- const Feature::property_map properties;
-};
-
-class GeoJSONTileLayer : public GeometryTileLayer {
-public:
- using Features = std::vector<std::shared_ptr<const GeoJSONTileFeature>>;
+} // namespace mapbox
- GeoJSONTileLayer(Features&&);
- std::size_t featureCount() const override;
- util::ptr<const GeometryTileFeature> getFeature(std::size_t) const override;
- std::string getName() const override { return ""; };
+namespace mbgl {
-private:
- const Features features;
-};
+namespace style {
+class UpdateParameters;
+} // namespace style
class GeoJSONTile : public GeometryTile {
public:
- GeoJSONTile(std::shared_ptr<GeoJSONTileLayer>);
- util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
-
-private:
- const std::shared_ptr<GeoJSONTileLayer> layer;
-};
-
-class GeoJSONTileMonitor : public GeometryTileMonitor {
-public:
- GeoJSONTileMonitor(mapbox::geojsonvt::GeoJSONVT*, const OverscaledTileID&);
- virtual ~GeoJSONTileMonitor();
-
- std::unique_ptr<AsyncRequest> monitorTile(const GeometryTileMonitor::Callback&) override;
-
- void setGeoJSONVT(mapbox::geojsonvt::GeoJSONVT*);
+ GeoJSONTile(const OverscaledTileID&,
+ std::string sourceID,
+ const style::UpdateParameters&,
+ mapbox::geojsonvt::GeoJSONVT&);
-private:
- void update();
-
-public:
- const OverscaledTileID tileID;
+ GeoJSONTile(const OverscaledTileID&,
+ std::string sourceID,
+ const style::UpdateParameters&,
+ mapbox::supercluster::Supercluster&);
-private:
- mapbox::geojsonvt::GeoJSONVT* geojsonvt = nullptr;
- GeometryTileMonitor::Callback callback;
+ void setNecessity(Necessity) final;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index 1a74880377..daa358420b 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -1,218 +1,224 @@
#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/tile/tile_id.hpp>
-
-#include <clipper/clipper.hpp>
+#include <mbgl/tile/tile_observer.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/util/worker.hpp>
+#include <mbgl/util/work_request.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/text/collision_tile.hpp>
+#include <mbgl/map/transform_state.hpp>
namespace mbgl {
-static double signedArea(const GeometryCoordinates& ring) {
- double sum = 0;
-
- for (std::size_t i = 0, len = ring.size(), j = len - 1; i < len; j = i++) {
- const GeometryCoordinate& p1 = ring[i];
- const GeometryCoordinate& p2 = ring[j];
- sum += (p2.x - p1.x) * (p1.y + p2.y);
- }
+using namespace style;
+
+GeometryTile::GeometryTile(const OverscaledTileID& id_,
+ std::string sourceID_,
+ Style& style_,
+ const MapMode mode_)
+ : Tile(id_),
+ sourceID(std::move(sourceID_)),
+ style(style_),
+ worker(style_.workers),
+ tileWorker(id_,
+ *style_.spriteStore,
+ *style_.glyphAtlas,
+ *style_.glyphStore,
+ obsolete,
+ mode_) {
+}
- return sum;
+GeometryTile::~GeometryTile() {
+ cancel();
}
-static ClipperLib::Path toClipperPath(const GeometryCoordinates& ring) {
- ClipperLib::Path result;
- result.reserve(ring.size());
- for (const auto& p : ring) {
- result.emplace_back(p.x, p.y);
- }
- return result;
+void GeometryTile::setError(std::exception_ptr err) {
+ observer->onTileError(*this, err);
}
-static GeometryCoordinates fromClipperPath(const ClipperLib::Path& path) {
- GeometryCoordinates result;
- result.reserve(path.size() + 1);
-
- result.reserve(path.size());
- for (const auto& p : path) {
- using Coordinate = GeometryCoordinates::coordinate_type;
- assert(p.x >= std::numeric_limits<Coordinate>::min());
- assert(p.x <= std::numeric_limits<Coordinate>::max());
- assert(p.y >= std::numeric_limits<Coordinate>::min());
- assert(p.y <= std::numeric_limits<Coordinate>::max());
- result.emplace_back(Coordinate(p.x), Coordinate(p.y));
- }
-
- // Clipper does not repeat initial point, but our geometry model requires it.
- if (!result.empty()) {
- result.push_back(result.front());
+std::vector<std::unique_ptr<Layer>> GeometryTile::cloneStyleLayers() const {
+ std::vector<std::unique_ptr<Layer>> result;
+
+ for (const Layer* layer : style.getLayers()) {
+ // Avoid cloning and including irrelevant layers.
+ if (layer->is<BackgroundLayer>() ||
+ layer->is<CustomLayer>() ||
+ layer->baseImpl->source != sourceID ||
+ id.overscaledZ < std::floor(layer->baseImpl->minZoom) ||
+ id.overscaledZ >= std::ceil(layer->baseImpl->maxZoom) ||
+ layer->baseImpl->visibility == VisibilityType::None) {
+ continue;
+ }
+
+ result.push_back(layer->baseImpl->clone());
}
-
+
return result;
}
-static void processPolynodeBranch(ClipperLib::PolyNode* polynode, GeometryCollection& rings) {
- // Exterior ring.
- rings.push_back(fromClipperPath(polynode->Contour));
- assert(signedArea(rings.back()) > 0);
-
- // Interior rings.
- for (auto * ring : polynode->Childs) {
- rings.push_back(fromClipperPath(ring->Contour));
- assert(signedArea(rings.back()) < 0);
+void GeometryTile::setData(std::unique_ptr<GeometryTileData> data_) {
+ if (!data_) {
+ // This is a 404 response. We're treating these as empty tiles.
+ workRequest.reset();
+ availableData = DataAvailability::All;
+ buckets.clear();
+ redoPlacement();
+ observer->onTileLoaded(*this, true);
+ return;
}
- // PolyNodes may be nested in the case of a polygon inside a hole.
- for (auto * ring : polynode->Childs) {
- for (auto * subRing : ring->Childs) {
- processPolynodeBranch(subRing, rings);
- }
+ // Mark the tile as pending again if it was complete before to prevent signaling a complete
+ // state despite pending parse operations.
+ if (availableData == DataAvailability::All) {
+ availableData = DataAvailability::Some;
}
-}
-GeometryCollection fixupPolygons(const GeometryCollection& rings) {
- ClipperLib::Clipper clipper;
- clipper.StrictlySimple(true);
+ // Kick off a fresh parse of this tile. This happens when the tile is new, or
+ // when tile data changed. Replacing the workdRequest will cancel a pending work
+ // request in case there is one.
+ workRequest.reset();
+ workRequest = worker.parseGeometryTile(tileWorker, cloneStyleLayers(), std::move(data_), targetConfig, [this, config = targetConfig] (TileParseResult result) {
+ workRequest.reset();
- for (const auto& ring : rings) {
- clipper.AddPath(toClipperPath(ring), ClipperLib::ptSubject, true);
- }
+ if (result.is<TileParseResultData>()) {
+ auto& resultBuckets = result.get<TileParseResultData>();
+ availableData = resultBuckets.complete ? DataAvailability::All : DataAvailability::Some;
- ClipperLib::PolyTree polygons;
- clipper.Execute(ClipperLib::ctUnion, polygons, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd);
- clipper.Clear();
+ // Persist the configuration we just placed so that we can later check whether we need to
+ // place again in case the configuration has changed.
+ placedConfig = config;
- GeometryCollection result;
- for (auto * polynode : polygons.Childs) {
- processPolynodeBranch(polynode, result);
- }
- return result;
-}
+ // Move over all buckets we received in this parse request, potentially overwriting
+ // existing buckets in case we got a refresh parse.
+ buckets = std::move(resultBuckets.buckets);
-std::vector<GeometryCollection> classifyRings(const GeometryCollection& rings) {
- std::vector<GeometryCollection> polygons;
+ if (isComplete()) {
+ featureIndex = std::move(resultBuckets.featureIndex);
+ data = std::move(resultBuckets.tileData);
+ }
- std::size_t len = rings.size();
+ redoPlacement();
+ observer->onTileLoaded(*this, true);
+ } else {
+ availableData = DataAvailability::All;
+ observer->onTileError(*this, result.get<std::exception_ptr>());
+ }
+ });
+}
- if (len <= 1) {
- polygons.push_back(rings);
- return polygons;
+bool GeometryTile::parsePending() {
+ if (workRequest) {
+ // There's already parsing or placement going on.
+ return false;
}
- GeometryCollection polygon;
- int8_t ccw = 0;
+ workRequest.reset();
+ workRequest = worker.parsePendingGeometryTileLayers(tileWorker, targetConfig, [this, config = targetConfig] (TileParseResult result) {
+ workRequest.reset();
- for (std::size_t i = 0; i < len; i++) {
- double area = signedArea(rings[i]);
+ if (result.is<TileParseResultData>()) {
+ auto& resultBuckets = result.get<TileParseResultData>();
+ availableData = resultBuckets.complete ? DataAvailability::All : DataAvailability::Some;
- if (area == 0)
- continue;
+ // Move over all buckets we received in this parse request, potentially overwriting
+ // existing buckets in case we got a refresh parse.
+ for (auto& bucket : resultBuckets.buckets) {
+ buckets[bucket.first] = std::move(bucket.second);
+ }
- if (ccw == 0)
- ccw = (area < 0 ? -1 : 1);
+ // Persist the configuration we just placed so that we can later check whether we need to
+ // place again in case the configuration has changed.
+ placedConfig = config;
+
+ if (isComplete()) {
+ featureIndex = std::move(resultBuckets.featureIndex);
+ data = std::move(resultBuckets.tileData);
+ }
- if (ccw == (area < 0 ? -1 : 1) && !polygon.empty()) {
- polygons.push_back(polygon);
- polygon.clear();
+ redoPlacement();
+ observer->onTileLoaded(*this, false);
+ } else {
+ availableData = DataAvailability::All;
+ observer->onTileError(*this, result.get<std::exception_ptr>());
}
+ });
- polygon.push_back(rings[i]);
+ return true;
+}
+
+Bucket* GeometryTile::getBucket(const Layer& layer) {
+ const auto it = buckets.find(layer.baseImpl->bucketName());
+ if (it == buckets.end()) {
+ return nullptr;
}
- if (!polygon.empty())
- polygons.push_back(polygon);
+ assert(it->second);
+ return it->second.get();
+}
- return polygons;
+void GeometryTile::redoPlacement(const PlacementConfig newConfig) {
+ targetConfig = newConfig;
+ redoPlacement();
}
-void limitHoles(GeometryCollection& polygon, uint32_t maxHoles) {
- if (polygon.size() > 1 + maxHoles) {
- std::nth_element(polygon.begin() + 1,
- polygon.begin() + 1 + maxHoles,
- polygon.end(),
- [] (const auto& a, const auto& b) {
- return signedArea(a) > signedArea(b);
- });
- polygon.resize(1 + maxHoles);
+void GeometryTile::redoPlacement() {
+ // Don't start a new placement request when the current one hasn't completed yet, or when
+ // we are parsing buckets.
+ if (workRequest || targetConfig == placedConfig) {
+ return;
}
-}
-static Feature::geometry_type convertGeometry(const GeometryTileFeature& geometryTileFeature, const CanonicalTileID& tileID) {
- const double size = util::EXTENT * std::pow(2, tileID.z);
- const double x0 = util::EXTENT * tileID.x;
- const double y0 = util::EXTENT * tileID.y;
-
- auto tileCoordinatesToLatLng = [&] (const Point<int16_t>& p) {
- double y2 = 180 - (p.y + y0) * 360 / size;
- return Point<double>(
- (p.x + x0) * 360 / size - 180,
- 360.0 / M_PI * std::atan(std::exp(y2 * M_PI / 180)) - 90.0
- );
- };
-
- GeometryCollection geometries = geometryTileFeature.getGeometries();
-
- switch (geometryTileFeature.getType()) {
- case FeatureType::Unknown: {
- assert(false);
- return Point<double>(NAN, NAN);
- }
+ workRequest = worker.redoPlacement(tileWorker, buckets, targetConfig, [this, config = targetConfig](std::unique_ptr<CollisionTile> collisionTile) {
+ workRequest.reset();
- case FeatureType::Point: {
- MultiPoint<double> multiPoint;
- for (const auto& p : geometries.at(0)) {
- multiPoint.push_back(tileCoordinatesToLatLng(p));
- }
- if (multiPoint.size() == 1) {
- return multiPoint[0];
- } else {
- return multiPoint;
- }
+ // Persist the configuration we just placed so that we can later check whether we need to
+ // place again in case the configuration has changed.
+ placedConfig = config;
+
+ for (auto& bucket : buckets) {
+ bucket.second->swapRenderData();
}
- case FeatureType::LineString: {
- MultiLineString<double> multiLineString;
- for (const auto& g : geometries) {
- LineString<double> lineString;
- for (const auto& p : g) {
- lineString.push_back(tileCoordinatesToLatLng(p));
- }
- multiLineString.push_back(std::move(lineString));
- }
- if (multiLineString.size() == 1) {
- return multiLineString[0];
- } else {
- return multiLineString;
- }
+ if (featureIndex) {
+ featureIndex->setCollisionTile(std::move(collisionTile));
}
- case FeatureType::Polygon: {
- MultiPolygon<double> multiPolygon;
- for (const auto& pg : classifyRings(geometries)) {
- Polygon<double> polygon;
- for (const auto& r : pg) {
- LinearRing<double> linearRing;
- for (const auto& p : r) {
- linearRing.push_back(tileCoordinatesToLatLng(p));
- }
- polygon.push_back(std::move(linearRing));
- }
- multiPolygon.push_back(std::move(polygon));
- }
- if (multiPolygon.size() == 1) {
- return multiPolygon[0];
- } else {
- return multiPolygon;
- }
+ // The target configuration could have changed since we started placement. In this case,
+ // we're starting another placement run.
+ if (placedConfig != targetConfig) {
+ redoPlacement();
+ } else {
+ observer->onNeedsRepaint();
}
- }
+ });
+}
- // Unreachable, but placate GCC.
- return Point<double>();
+void GeometryTile::queryRenderedFeatures(
+ std::unordered_map<std::string, std::vector<Feature>>& result,
+ const GeometryCoordinates& queryGeometry,
+ const TransformState& transformState,
+ const optional<std::vector<std::string>>& layerIDs) {
+
+ if (!featureIndex || !data) return;
+
+ featureIndex->query(result,
+ { queryGeometry },
+ transformState.getAngle(),
+ util::tileSize * id.overscaleFactor(),
+ std::pow(2, transformState.getZoom() - id.overscaledZ),
+ layerIDs,
+ *data,
+ id.canonical,
+ style);
}
-Feature convertFeature(const GeometryTileFeature& geometryTileFeature, const CanonicalTileID& tileID) {
- Feature feature { convertGeometry(geometryTileFeature, tileID) };
- feature.properties = geometryTileFeature.getProperties();
- feature.id = geometryTileFeature.getID();
- return feature;
+void GeometryTile::cancel() {
+ obsolete = true;
+ workRequest.reset();
}
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp
index 6904c922ba..891aba0432 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -1,104 +1,79 @@
#pragma once
-#include <mbgl/util/geometry.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/tile/tile_worker.hpp>
+#include <mbgl/text/placement_config.hpp>
#include <mbgl/util/feature.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/ptr.hpp>
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/variant.hpp>
-#include <mbgl/util/constants.hpp>
-
-#include <cstdint>
-#include <string>
-#include <vector>
+
+#include <atomic>
+#include <memory>
#include <unordered_map>
-#include <functional>
+#include <vector>
namespace mbgl {
-enum class FeatureType : uint8_t {
- Unknown = 0,
- Point = 1,
- LineString = 2,
- Polygon = 3
-};
-
-class CanonicalTileID;
+class AsyncRequest;
+class GeometryTileData;
+class FeatureIndex;
-// Normalized vector tile coordinates.
-// Each geometry coordinate represents a point in a bidimensional space,
-// varying from -V...0...+V, where V is the maximum extent applicable.
-using GeometryCoordinate = Point<int16_t>;
+namespace style {
+class Style;
+class Layer;
+} // namespace style
-class GeometryCoordinates : public std::vector<GeometryCoordinate> {
+class GeometryTile : public Tile {
public:
- using coordinate_type = int16_t;
- using std::vector<GeometryCoordinate>::vector;
-};
+ GeometryTile(const OverscaledTileID&,
+ std::string sourceID,
+ style::Style&,
+ const MapMode);
-class GeometryCollection : public std::vector<GeometryCoordinates> {
-public:
- using coordinate_type = int16_t;
- using std::vector<GeometryCoordinates>::vector;
-};
+ ~GeometryTile() override;
-class GeometryTileFeature : private util::noncopyable {
-public:
- virtual ~GeometryTileFeature() = default;
- virtual FeatureType getType() const = 0;
- virtual optional<Value> getValue(const std::string& key) const = 0;
- virtual Feature::property_map getProperties() const { return Feature::property_map(); }
- virtual optional<uint64_t> getID() const { return {}; }
- virtual GeometryCollection getGeometries() const = 0;
-};
+ void setError(std::exception_ptr err);
+ void setData(std::unique_ptr<GeometryTileData>);
-class GeometryTileLayer : private util::noncopyable {
-public:
- virtual ~GeometryTileLayer() = default;
- virtual std::size_t featureCount() const = 0;
- virtual util::ptr<const GeometryTileFeature> getFeature(std::size_t) const = 0;
- virtual std::string getName() const = 0;
-};
+ Bucket* getBucket(const style::Layer&) override;
-class GeometryTile : private util::noncopyable {
-public:
- virtual ~GeometryTile() = default;
- virtual util::ptr<GeometryTileLayer> getLayer(const std::string&) const = 0;
-};
+ bool parsePending() override;
-class AsyncRequest;
+ void redoPlacement(PlacementConfig) override;
-class GeometryTileMonitor : private util::noncopyable {
-public:
- virtual ~GeometryTileMonitor() = default;
-
- using Callback = std::function<void (std::exception_ptr,
- std::unique_ptr<GeometryTile>,
- optional<Timestamp> modified,
- optional<Timestamp> expires)>;
- /*
- * Monitor the tile held by this object for changes. When the tile is loaded for the first time,
- * or updates, the callback is executed. If an error occurs, the first parameter will be set.
- * Otherwise it will be null. If there is no data for the requested tile, the second parameter
- * will be null.
- *
- * To cease monitoring, release the returned Request.
- */
- virtual std::unique_ptr<AsyncRequest> monitorTile(const Callback&) = 0;
-};
+ void queryRenderedFeatures(
+ std::unordered_map<std::string, std::vector<Feature>>& result,
+ const GeometryCoordinates& queryGeometry,
+ const TransformState&,
+ const optional<std::vector<std::string>>& layerIDs) override;
-// classifies an array of rings into polygons with outer rings and holes
-std::vector<GeometryCollection> classifyRings(const GeometryCollection&);
+ void cancel() override;
-// Truncate polygon to the largest `maxHoles` inner rings by area.
-void limitHoles(GeometryCollection&, uint32_t maxHoles);
+private:
+ std::vector<std::unique_ptr<style::Layer>> cloneStyleLayers() const;
+ void redoPlacement();
-// convert from GeometryTileFeature to Feature (eventually we should eliminate GeometryTileFeature)
-Feature convertFeature(const GeometryTileFeature&, const CanonicalTileID&);
+ const std::string sourceID;
+ style::Style& style;
+ Worker& worker;
+ TileWorker tileWorker;
-// Fix up possibly-non-V2-compliant polygon geometry using angus clipper.
-// The result is guaranteed to have correctly wound, strictly simple rings.
-GeometryCollection fixupPolygons(const GeometryCollection&);
+ std::unique_ptr<AsyncRequest> workRequest;
+
+ // Contains all the Bucket objects for the tile. Buckets are render
+ // objects and they get added by tile parsing operations.
+ std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
+
+ std::unique_ptr<FeatureIndex> featureIndex;
+ std::unique_ptr<const GeometryTileData> data;
+
+ // Stores the placement configuration of the text that is currently placed on the screen.
+ PlacementConfig placedConfig;
+
+ // Stores the placement configuration of how the text should be placed. This isn't necessarily
+ // the one that is being displayed.
+ PlacementConfig targetConfig;
+
+ // Used to signal the worker that it should abandon parsing this tile as soon as possible.
+ std::atomic<bool> obsolete { false };
+};
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile_data.cpp b/src/mbgl/tile/geometry_tile_data.cpp
new file mode 100644
index 0000000000..2e465a6f65
--- /dev/null
+++ b/src/mbgl/tile/geometry_tile_data.cpp
@@ -0,0 +1,217 @@
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/tile/tile_id.hpp>
+
+#include <clipper/clipper.hpp>
+
+namespace mbgl {
+
+static double signedArea(const GeometryCoordinates& ring) {
+ double sum = 0;
+
+ for (std::size_t i = 0, len = ring.size(), j = len - 1; i < len; j = i++) {
+ const GeometryCoordinate& p1 = ring[i];
+ const GeometryCoordinate& p2 = ring[j];
+ sum += (p2.x - p1.x) * (p1.y + p2.y);
+ }
+
+ return sum;
+}
+
+static ClipperLib::Path toClipperPath(const GeometryCoordinates& ring) {
+ ClipperLib::Path result;
+ result.reserve(ring.size());
+ for (const auto& p : ring) {
+ result.emplace_back(p.x, p.y);
+ }
+ return result;
+}
+
+static GeometryCoordinates fromClipperPath(const ClipperLib::Path& path) {
+ GeometryCoordinates result;
+ result.reserve(path.size() + 1);
+
+ for (const auto& p : path) {
+ using Coordinate = GeometryCoordinates::coordinate_type;
+ assert(p.x >= std::numeric_limits<Coordinate>::min());
+ assert(p.x <= std::numeric_limits<Coordinate>::max());
+ assert(p.y >= std::numeric_limits<Coordinate>::min());
+ assert(p.y <= std::numeric_limits<Coordinate>::max());
+ result.emplace_back(Coordinate(p.x), Coordinate(p.y));
+ }
+
+ // Clipper does not repeat initial point, but our geometry model requires it.
+ if (!result.empty()) {
+ result.push_back(result.front());
+ }
+
+ return result;
+}
+
+static void processPolynodeBranch(ClipperLib::PolyNode* polynode, GeometryCollection& rings) {
+ // Exterior ring.
+ rings.push_back(fromClipperPath(polynode->Contour));
+ assert(signedArea(rings.back()) > 0);
+
+ // Interior rings.
+ for (auto * ring : polynode->Childs) {
+ rings.push_back(fromClipperPath(ring->Contour));
+ assert(signedArea(rings.back()) < 0);
+ }
+
+ // PolyNodes may be nested in the case of a polygon inside a hole.
+ for (auto * ring : polynode->Childs) {
+ for (auto * subRing : ring->Childs) {
+ processPolynodeBranch(subRing, rings);
+ }
+ }
+}
+
+GeometryCollection fixupPolygons(const GeometryCollection& rings) {
+ ClipperLib::Clipper clipper;
+ clipper.StrictlySimple(true);
+
+ for (const auto& ring : rings) {
+ clipper.AddPath(toClipperPath(ring), ClipperLib::ptSubject, true);
+ }
+
+ ClipperLib::PolyTree polygons;
+ clipper.Execute(ClipperLib::ctUnion, polygons, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd);
+ clipper.Clear();
+
+ GeometryCollection result;
+ for (auto * polynode : polygons.Childs) {
+ processPolynodeBranch(polynode, result);
+ }
+ return result;
+}
+
+std::vector<GeometryCollection> classifyRings(const GeometryCollection& rings) {
+ std::vector<GeometryCollection> polygons;
+
+ std::size_t len = rings.size();
+
+ if (len <= 1) {
+ polygons.push_back(rings);
+ return polygons;
+ }
+
+ GeometryCollection polygon;
+ int8_t ccw = 0;
+
+ for (std::size_t i = 0; i < len; i++) {
+ double area = signedArea(rings[i]);
+
+ if (area == 0)
+ continue;
+
+ if (ccw == 0)
+ ccw = (area < 0 ? -1 : 1);
+
+ if (ccw == (area < 0 ? -1 : 1) && !polygon.empty()) {
+ polygons.push_back(polygon);
+ polygon.clear();
+ }
+
+ polygon.push_back(rings[i]);
+ }
+
+ if (!polygon.empty())
+ polygons.push_back(polygon);
+
+ return polygons;
+}
+
+void limitHoles(GeometryCollection& polygon, uint32_t maxHoles) {
+ if (polygon.size() > 1 + maxHoles) {
+ std::nth_element(polygon.begin() + 1,
+ polygon.begin() + 1 + maxHoles,
+ polygon.end(),
+ [] (const auto& a, const auto& b) {
+ return signedArea(a) > signedArea(b);
+ });
+ polygon.resize(1 + maxHoles);
+ }
+}
+
+static Feature::geometry_type convertGeometry(const GeometryTileFeature& geometryTileFeature, const CanonicalTileID& tileID) {
+ const double size = util::EXTENT * std::pow(2, tileID.z);
+ const double x0 = util::EXTENT * tileID.x;
+ const double y0 = util::EXTENT * tileID.y;
+
+ auto tileCoordinatesToLatLng = [&] (const Point<int16_t>& p) {
+ double y2 = 180 - (p.y + y0) * 360 / size;
+ return Point<double>(
+ (p.x + x0) * 360 / size - 180,
+ 360.0 / M_PI * std::atan(std::exp(y2 * M_PI / 180)) - 90.0
+ );
+ };
+
+ GeometryCollection geometries = geometryTileFeature.getGeometries();
+
+ switch (geometryTileFeature.getType()) {
+ case FeatureType::Unknown: {
+ assert(false);
+ return Point<double>(NAN, NAN);
+ }
+
+ case FeatureType::Point: {
+ MultiPoint<double> multiPoint;
+ for (const auto& p : geometries.at(0)) {
+ multiPoint.push_back(tileCoordinatesToLatLng(p));
+ }
+ if (multiPoint.size() == 1) {
+ return multiPoint[0];
+ } else {
+ return multiPoint;
+ }
+ }
+
+ case FeatureType::LineString: {
+ MultiLineString<double> multiLineString;
+ for (const auto& g : geometries) {
+ LineString<double> lineString;
+ for (const auto& p : g) {
+ lineString.push_back(tileCoordinatesToLatLng(p));
+ }
+ multiLineString.push_back(std::move(lineString));
+ }
+ if (multiLineString.size() == 1) {
+ return multiLineString[0];
+ } else {
+ return multiLineString;
+ }
+ }
+
+ case FeatureType::Polygon: {
+ MultiPolygon<double> multiPolygon;
+ for (const auto& pg : classifyRings(geometries)) {
+ Polygon<double> polygon;
+ for (const auto& r : pg) {
+ LinearRing<double> linearRing;
+ for (const auto& p : r) {
+ linearRing.push_back(tileCoordinatesToLatLng(p));
+ }
+ polygon.push_back(std::move(linearRing));
+ }
+ multiPolygon.push_back(std::move(polygon));
+ }
+ if (multiPolygon.size() == 1) {
+ return multiPolygon[0];
+ } else {
+ return multiPolygon;
+ }
+ }
+ }
+
+ // Unreachable, but placate GCC.
+ return Point<double>();
+}
+
+Feature convertFeature(const GeometryTileFeature& geometryTileFeature, const CanonicalTileID& tileID) {
+ Feature feature { convertGeometry(geometryTileFeature, tileID) };
+ feature.properties = geometryTileFeature.getProperties();
+ feature.id = geometryTileFeature.getID();
+ return feature;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile_data.hpp b/src/mbgl/tile/geometry_tile_data.hpp
new file mode 100644
index 0000000000..4055a80ecf
--- /dev/null
+++ b/src/mbgl/tile/geometry_tile_data.hpp
@@ -0,0 +1,143 @@
+#pragma once
+
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/constants.hpp>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <functional>
+#include <iostream>
+
+namespace mbgl {
+
+class CanonicalTileID;
+
+// Normalized vector tile coordinates.
+// Each geometry coordinate represents a point in a bidimensional space,
+// varying from -V...0...+V, where V is the maximum extent applicable.
+using GeometryCoordinate = Point<int16_t>;
+
+class GeometryCoordinates : public std::vector<GeometryCoordinate> {
+public:
+ using coordinate_type = int16_t;
+ using std::vector<GeometryCoordinate>::vector;
+};
+
+class GeometryCollection : public std::vector<GeometryCoordinates> {
+public:
+ using coordinate_type = int16_t;
+ using std::vector<GeometryCoordinates>::vector;
+};
+
+class GeometryTileFeature : private util::noncopyable {
+public:
+ virtual ~GeometryTileFeature() = default;
+ virtual FeatureType getType() const = 0;
+ virtual optional<Value> getValue(const std::string& key) const = 0;
+ virtual PropertyMap getProperties() const { return PropertyMap(); }
+ virtual optional<FeatureIdentifier> getID() const { return {}; }
+ virtual GeometryCollection getGeometries() const = 0;
+};
+
+class GeometryTileLayer : private util::noncopyable {
+public:
+ virtual ~GeometryTileLayer() = default;
+ virtual std::size_t featureCount() const = 0;
+ virtual util::ptr<const GeometryTileFeature> getFeature(std::size_t) const = 0;
+ virtual std::string getName() const = 0;
+};
+
+class GeometryTileData : private util::noncopyable {
+public:
+ virtual ~GeometryTileData() = default;
+ virtual util::ptr<GeometryTileLayer> getLayer(const std::string&) const = 0;
+};
+
+// classifies an array of rings into polygons with outer rings and holes
+std::vector<GeometryCollection> classifyRings(const GeometryCollection&);
+
+// Truncate polygon to the largest `maxHoles` inner rings by area.
+void limitHoles(GeometryCollection&, uint32_t maxHoles);
+
+// convert from GeometryTileFeature to Feature (eventually we should eliminate GeometryTileFeature)
+Feature convertFeature(const GeometryTileFeature&, const CanonicalTileID&);
+
+// Fix up possibly-non-V2-compliant polygon geometry using angus clipper.
+// The result is guaranteed to have correctly wound, strictly simple rings.
+GeometryCollection fixupPolygons(const GeometryCollection&);
+
+struct ToGeometryCollection {
+ GeometryCollection operator()(const mapbox::geometry::point<int16_t>& geom) const {
+ return { { geom } };
+ }
+ GeometryCollection operator()(const mapbox::geometry::multi_point<int16_t>& geom) const {
+ GeometryCoordinates coordinates;
+ coordinates.reserve(geom.size());
+ for (const auto& point : geom) {
+ coordinates.emplace_back(point);
+ }
+ return { coordinates };
+ }
+ GeometryCollection operator()(const mapbox::geometry::line_string<int16_t>& geom) const {
+ GeometryCoordinates coordinates;
+ coordinates.reserve(geom.size());
+ for (const auto& point : geom) {
+ coordinates.emplace_back(point);
+ }
+ return { coordinates };
+ }
+ GeometryCollection operator()(const mapbox::geometry::multi_line_string<int16_t>& geom) const {
+ GeometryCollection collection;
+ collection.reserve(geom.size());
+ for (const auto& ring : geom) {
+ GeometryCoordinates coordinates;
+ coordinates.reserve(ring.size());
+ for (const auto& point : ring) {
+ coordinates.emplace_back(point);
+ }
+ collection.push_back(std::move(coordinates));
+ }
+ return collection;
+ }
+ GeometryCollection operator()(const mapbox::geometry::polygon<int16_t>& geom) const {
+ GeometryCollection collection;
+ collection.reserve(geom.size());
+ for (const auto& ring : geom) {
+ GeometryCoordinates coordinates;
+ coordinates.reserve(ring.size());
+ for (const auto& point : ring) {
+ coordinates.emplace_back(point);
+ }
+ collection.push_back(std::move(coordinates));
+ }
+ return collection;
+ }
+ GeometryCollection operator()(const mapbox::geometry::multi_polygon<int16_t>& geom) const {
+ GeometryCollection collection;
+ for (auto& polygon : geom) {
+ for (auto& ring : polygon) {
+ GeometryCoordinates coordinates;
+ coordinates.reserve(ring.size());
+ for (auto& point : ring) {
+ coordinates.emplace_back(point);
+ }
+ collection.push_back(std::move(coordinates));
+ }
+ }
+ return collection;
+ }
+ GeometryCollection operator()(const mapbox::geometry::geometry_collection<int16_t>&) const {
+ GeometryCollection collection;
+ return collection;
+ }
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp
new file mode 100644
index 0000000000..2460aac8f9
--- /dev/null
+++ b/src/mbgl/tile/raster_tile.cpp
@@ -0,0 +1,71 @@
+#include <mbgl/tile/raster_tile.hpp>
+#include <mbgl/tile/tile_observer.hpp>
+#include <mbgl/tile/tile_loader_impl.hpp>
+#include <mbgl/style/source.hpp>
+#include <mbgl/style/update_parameters.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/util/worker.hpp>
+#include <mbgl/util/work_request.hpp>
+
+namespace mbgl {
+
+RasterTile::RasterTile(const OverscaledTileID& id_,
+ const style::UpdateParameters& parameters,
+ const Tileset& tileset)
+ : Tile(id_),
+ worker(parameters.worker),
+ loader(*this, id_, parameters, tileset) {
+}
+
+RasterTile::~RasterTile() = default;
+
+void RasterTile::setError(std::exception_ptr err) {
+ observer->onTileError(*this, err);
+}
+
+void RasterTile::setData(std::shared_ptr<const std::string> data,
+ optional<Timestamp> modified_,
+ optional<Timestamp> expires_) {
+ modified = modified_;
+ expires = expires_;
+
+ if (!data) {
+ // This is a 404 response. We're treating these as empty tiles.
+ workRequest.reset();
+ availableData = DataAvailability::All;
+ bucket.reset();
+ observer->onTileLoaded(*this, true);
+ return;
+ }
+
+ workRequest.reset();
+ workRequest = worker.parseRasterTile(std::make_unique<RasterBucket>(), data, [this] (RasterTileParseResult result) {
+ workRequest.reset();
+
+ availableData = DataAvailability::All;
+
+ if (result.is<std::unique_ptr<Bucket>>()) {
+ bucket = std::move(result.get<std::unique_ptr<Bucket>>());
+ observer->onTileLoaded(*this, true);
+ } else {
+ bucket.reset();
+ observer->onTileError(*this, result.get<std::exception_ptr>());
+ }
+ });
+}
+
+Bucket* RasterTile::getBucket(const style::Layer&) {
+ return bucket.get();
+}
+
+void RasterTile::setNecessity(Necessity necessity) {
+ loader.setNecessity(necessity);
+}
+
+void RasterTile::cancel() {
+ workRequest.reset();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp
new file mode 100644
index 0000000000..496edda6b3
--- /dev/null
+++ b/src/mbgl/tile/raster_tile.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/tile/tile_loader.hpp>
+#include <mbgl/renderer/raster_bucket.hpp>
+
+namespace mbgl {
+
+class AsyncRequest;
+class Tileset;
+
+namespace style {
+class Layer;
+class UpdateParameters;
+} // namespace style
+
+class RasterTile : public Tile {
+public:
+ RasterTile(const OverscaledTileID&,
+ const style::UpdateParameters&,
+ const Tileset&);
+ ~RasterTile() final;
+
+ void setNecessity(Necessity) final;
+
+ void setError(std::exception_ptr err);
+
+ void setData(std::shared_ptr<const std::string> data,
+ optional<Timestamp> modified_,
+ optional<Timestamp> expires_);
+
+ void cancel() override;
+ Bucket* getBucket(const style::Layer&) override;
+
+private:
+ Worker& worker;
+
+ TileLoader<RasterTile> loader;
+ std::unique_ptr<AsyncRequest> workRequest;
+
+ // Contains the Bucket object for the tile. Buckets are render
+ // objects and they get added by tile parsing operations.
+ std::unique_ptr<Bucket> bucket;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile_data.cpp b/src/mbgl/tile/raster_tile_data.cpp
deleted file mode 100644
index 420cb7055e..0000000000
--- a/src/mbgl/tile/raster_tile_data.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#include <mbgl/tile/raster_tile_data.hpp>
-#include <mbgl/style/source.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/util/worker.hpp>
-#include <mbgl/util/work_request.hpp>
-
-using namespace mbgl;
-
-RasterTileData::RasterTileData(const OverscaledTileID& id_,
- float pixelRatio,
- const std::string& urlTemplate,
- gl::TexturePool &texturePool_,
- Worker& worker_,
- FileSource& fileSource,
- const std::function<void(std::exception_ptr)>& callback)
- : TileData(id_),
- texturePool(texturePool_),
- worker(worker_) {
- const Resource resource =
- Resource::tile(urlTemplate, pixelRatio, id.canonical.x, id.canonical.y, id.canonical.z);
- req = fileSource.request(resource, [callback, this](Response res) {
- if (res.error) {
- callback(std::make_exception_ptr(std::runtime_error(res.error->message)));
- } else if (res.notModified) {
- modified = res.modified;
- expires = res.expires;
- } else if (res.noContent) {
- availableData = DataAvailability::All;
- modified = res.modified;
- expires = res.expires;
- workRequest.reset();
- bucket.reset();
- callback(nullptr);
- } else {
- modified = res.modified;
- expires = res.expires;
-
- workRequest.reset();
- workRequest = worker.parseRasterTile(std::make_unique<RasterBucket>(texturePool), res.data, [this, callback] (RasterTileParseResult result) {
- workRequest.reset();
-
- std::exception_ptr error;
- if (result.is<std::unique_ptr<Bucket>>()) {
- bucket = std::move(result.get<std::unique_ptr<Bucket>>());
- } else {
- error = result.get<std::exception_ptr>();
- bucket.reset();
- }
-
- availableData = DataAvailability::All;
-
- callback(error);
- });
- }
- });
-}
-
-RasterTileData::~RasterTileData() {
- cancel();
-}
-
-Bucket* RasterTileData::getBucket(const style::Layer&) {
- return bucket.get();
-}
-
-void RasterTileData::cancel() {
- req = nullptr;
- workRequest.reset();
-}
diff --git a/src/mbgl/tile/raster_tile_data.hpp b/src/mbgl/tile/raster_tile_data.hpp
deleted file mode 100644
index 62d21d28c4..0000000000
--- a/src/mbgl/tile/raster_tile_data.hpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include <mbgl/tile/tile_data.hpp>
-#include <mbgl/renderer/raster_bucket.hpp>
-
-namespace mbgl {
-
-class FileSource;
-class AsyncRequest;
-
-namespace gl { class TexturePool; }
-
-namespace style {
-class Layer;
-}
-
-class RasterTileData : public TileData {
-public:
- RasterTileData(const OverscaledTileID&,
- float pixelRatio,
- const std::string& urlTemplate,
- gl::TexturePool&,
- Worker&,
- FileSource&,
- const std::function<void(std::exception_ptr)>& callback);
- ~RasterTileData();
-
- void cancel() override;
- Bucket* getBucket(const style::Layer&) override;
-
-private:
- gl::TexturePool& texturePool;
- Worker& worker;
- std::unique_ptr<AsyncRequest> req;
- std::unique_ptr<Bucket> bucket;
- std::unique_ptr<AsyncRequest> workRequest;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp
index a1d36421a4..6595c314ee 100644
--- a/src/mbgl/tile/tile.cpp
+++ b/src/mbgl/tile/tile.cpp
@@ -1,3 +1,36 @@
#include <mbgl/tile/tile.hpp>
+#include <mbgl/tile/tile_observer.hpp>
+#include <mbgl/renderer/debug_bucket.hpp>
+#include <mbgl/util/string.hpp>
-using namespace mbgl;
+namespace mbgl {
+
+static TileObserver nullObserver;
+
+Tile::Tile(OverscaledTileID id_) : id(std::move(id_)), observer(&nullObserver) {
+}
+
+Tile::~Tile() = default;
+
+void Tile::setObserver(TileObserver* observer_) {
+ observer = observer_;
+}
+
+void Tile::setTriedOptional() {
+ triedOptional = true;
+ observer->onNeedsRepaint();
+}
+
+void Tile::dumpDebugLogs() const {
+ Log::Info(Event::General, "Tile::id: %s", util::toString(id).c_str());
+ Log::Info(Event::General, "Tile::renderable: %s", isRenderable() ? "yes" : "no");
+ Log::Info(Event::General, "Tile::complete: %s", isComplete() ? "yes" : "no");
+}
+
+void Tile::queryRenderedFeatures(
+ std::unordered_map<std::string, std::vector<Feature>>&,
+ const GeometryCoordinates&,
+ const TransformState&,
+ const optional<std::vector<std::string>>&) {}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index 1e35e78735..65f3aaa245 100644
--- a/src/mbgl/tile/tile.hpp
+++ b/src/mbgl/tile/tile.hpp
@@ -1,28 +1,111 @@
#pragma once
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/feature.hpp>
#include <mbgl/tile/tile_id.hpp>
-#include <mbgl/util/mat4.hpp>
-#include <mbgl/util/ptr.hpp>
-#include <mbgl/util/clip_id.hpp>
+#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/text/placement_config.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/storage/resource.hpp>
+
+#include <string>
+#include <memory>
+#include <functional>
+#include <unordered_map>
namespace mbgl {
-class TileData;
+class Worker;
+class DebugBucket;
+class TransformState;
+class TileObserver;
+
+namespace style {
+class Layer;
+} // namespace style
-class Tile {
+class Tile : private util::noncopyable {
public:
- Tile(const UnwrappedTileID& id_, TileData& data_) : id(id_), data(data_) {
+ Tile(OverscaledTileID);
+ virtual ~Tile();
+
+ void setObserver(TileObserver* observer);
+
+ // Tiles can have two states: optional or required.
+ // - optional means that only low-cost actions should be taken to obtain the data
+ // (e.g. load from cache, but accept stale data)
+ // - required means that every effort should be taken to obtain the data (e.g. load
+ // from internet and keep the data fresh if it expires)
+ using Necessity = Resource::Necessity;
+
+ virtual void setNecessity(Necessity) = 0;
+
+ // Mark this tile as no longer needed and cancel any pending work.
+ virtual void cancel() = 0;
+
+ virtual Bucket* getBucket(const style::Layer&) = 0;
+
+ virtual bool parsePending() { return true; }
+ virtual void redoPlacement(PlacementConfig) {}
+
+ virtual void queryRenderedFeatures(
+ std::unordered_map<std::string, std::vector<Feature>>& result,
+ const GeometryCoordinates& queryGeometry,
+ const TransformState&,
+ const optional<std::vector<std::string>>& layerIDs);
+
+ void setTriedOptional();
+
+ // Returns true when the tile source has received a first response, regardless of whether a load
+ // error occurred or actual data was loaded.
+ bool hasTriedOptional() const {
+ return triedOptional;
}
- Tile(const Tile&) = delete;
- Tile(Tile&&) = default;
- Tile& operator=(const Tile&) = delete;
- Tile& operator=(Tile&&) = default;
+ // Tile data considered "Renderable" can be used for rendering. Data in
+ // partial state is still waiting for network resources but can also
+ // be rendered, although layers will be missing.
+ bool isRenderable() const {
+ return availableData != DataAvailability::None;
+ }
+
+ bool isComplete() const {
+ return availableData == DataAvailability::All;
+ }
+ bool isIncomplete() const {
+ return availableData == DataAvailability::Some;
+ }
+
+ void dumpDebugLogs() const;
+
+ const OverscaledTileID id;
+ optional<Timestamp> modified;
+ optional<Timestamp> expires;
+
+ // Contains the tile ID string for painting debug information.
+ std::unique_ptr<DebugBucket> debugBucket;
+
+protected:
+ bool triedOptional = false;
+
+ enum class DataAvailability : uint8_t {
+ // Still waiting for data to load or parse.
+ None,
+
+ // Tile is partially parsed, some buckets are still waiting for dependencies
+ // to arrive, but it is good for rendering. Partial tiles can also be re-parsed,
+ // but might remain in the same state if dependencies are still missing.
+ Some,
+
+ // Tile is fully parsed, and all buckets are available if they exist.
+ All,
+ };
+
+ DataAvailability availableData = DataAvailability::None;
- const UnwrappedTileID id;
- TileData& data;
- ClipID clip;
- mat4 matrix;
+ TileObserver* observer = nullptr;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/tile_cache.cpp b/src/mbgl/tile/tile_cache.cpp
index e012ef0b7e..3fafb1259c 100644
--- a/src/mbgl/tile/tile_cache.cpp
+++ b/src/mbgl/tile/tile_cache.cpp
@@ -1,5 +1,5 @@
#include <mbgl/tile/tile_cache.hpp>
-#include <mbgl/tile/tile_data.hpp>
+#include <mbgl/tile/tile.hpp>
#include <cassert>
@@ -17,42 +17,42 @@ void TileCache::setSize(size_t size_) {
assert(orderedKeys.size() <= size);
}
-void TileCache::add(const OverscaledTileID& key, std::unique_ptr<TileData> data) {
- if (!data->isRenderable() || !size) {
+void TileCache::add(const OverscaledTileID& key, std::unique_ptr<Tile> tile) {
+ if (!tile->isRenderable() || !size) {
return;
}
- // insert new or query existing data
- if (tiles.emplace(key, std::move(data)).second) {
- // remove existing data key
+ // insert new or query existing tile
+ if (tiles.emplace(key, std::move(tile)).second) {
+ // remove existing tile key
orderedKeys.remove(key);
}
- // (re-)insert data key as newest
+ // (re-)insert tile key as newest
orderedKeys.push_back(key);
- // purge oldest key/data if necessary
+ // purge oldest key/tile if necessary
if (orderedKeys.size() > size) {
get(orderedKeys.front());
}
assert(orderedKeys.size() <= size);
-};
+}
-std::unique_ptr<TileData> TileCache::get(const OverscaledTileID& key) {
+std::unique_ptr<Tile> TileCache::get(const OverscaledTileID& key) {
- std::unique_ptr<TileData> data;
+ std::unique_ptr<Tile> tile;
auto it = tiles.find(key);
if (it != tiles.end()) {
- data = std::move(it->second);
+ tile = std::move(it->second);
tiles.erase(it);
orderedKeys.remove(key);
- assert(data->isRenderable());
+ assert(tile->isRenderable());
}
- return data;
-};
+ return tile;
+}
bool TileCache::has(const OverscaledTileID& key) {
return tiles.find(key) != tiles.end();
diff --git a/src/mbgl/tile/tile_cache.hpp b/src/mbgl/tile/tile_cache.hpp
index edaa6bd680..80fe98a20c 100644
--- a/src/mbgl/tile/tile_cache.hpp
+++ b/src/mbgl/tile/tile_cache.hpp
@@ -8,7 +8,7 @@
namespace mbgl {
-class TileData;
+class Tile;
class TileCache {
public:
@@ -16,13 +16,13 @@ public:
void setSize(size_t);
size_t getSize() const { return size; };
- void add(const OverscaledTileID& key, std::unique_ptr<TileData> data);
- std::unique_ptr<TileData> get(const OverscaledTileID& key);
+ void add(const OverscaledTileID& key, std::unique_ptr<Tile> data);
+ std::unique_ptr<Tile> get(const OverscaledTileID& key);
bool has(const OverscaledTileID& key);
void clear();
private:
- std::map<OverscaledTileID, std::unique_ptr<TileData>> tiles;
+ std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles;
std::list<OverscaledTileID> orderedKeys;
size_t size;
diff --git a/src/mbgl/tile/tile_data.cpp b/src/mbgl/tile/tile_data.cpp
deleted file mode 100644
index e23ae2a727..0000000000
--- a/src/mbgl/tile/tile_data.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include <mbgl/tile/tile_data.hpp>
-#include <mbgl/renderer/debug_bucket.hpp>
-#include <mbgl/util/string.hpp>
-
-namespace mbgl {
-
-TileData::TileData(const OverscaledTileID& id_)
- : id(id_) {
-}
-
-TileData::~TileData() = default;
-
-void TileData::dumpDebugLogs() const {
- Log::Info(Event::General, "TileData::id: %s", util::toString(id).c_str());
- Log::Info(Event::General, "TileData::renderable: %s", isRenderable() ? "yes" : "no");
- Log::Info(Event::General, "TileData::complete: %s", isComplete() ? "yes" : "no");
-}
-
-void TileData::queryRenderedFeatures(
- std::unordered_map<std::string, std::vector<Feature>>&,
- const GeometryCoordinates&,
- const TransformState&,
- const optional<std::vector<std::string>>&) {}
-
-} // namespace mbgl
diff --git a/src/mbgl/tile/tile_data.hpp b/src/mbgl/tile/tile_data.hpp
deleted file mode 100644
index 5b6583faf8..0000000000
--- a/src/mbgl/tile/tile_data.hpp
+++ /dev/null
@@ -1,87 +0,0 @@
-#pragma once
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/feature.hpp>
-#include <mbgl/tile/tile_id.hpp>
-#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/text/placement_config.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
-
-#include <string>
-#include <memory>
-#include <functional>
-#include <unordered_map>
-
-namespace mbgl {
-
-class Worker;
-class DebugBucket;
-class TransformState;
-
-namespace style {
-class Layer;
-}
-
-class TileData : private util::noncopyable {
-public:
- TileData(const OverscaledTileID&);
- virtual ~TileData();
-
- // Mark this tile as no longer needed and cancel any pending work.
- virtual void cancel() = 0;
-
- virtual Bucket* getBucket(const style::Layer&) = 0;
-
- virtual bool parsePending(std::function<void (std::exception_ptr)>) { return true; }
- virtual void redoPlacement(PlacementConfig, const std::function<void()>&) {}
- virtual void redoPlacement(const std::function<void()>&) {}
-
- virtual void queryRenderedFeatures(
- std::unordered_map<std::string, std::vector<Feature>>& result,
- const GeometryCoordinates& queryGeometry,
- const TransformState&,
- const optional<std::vector<std::string>>& layerIDs);
-
- // Tile data considered "Renderable" can be used for rendering. Data in
- // partial state is still waiting for network resources but can also
- // be rendered, although layers will be missing.
- bool isRenderable() const {
- return availableData != DataAvailability::None;
- }
-
- bool isComplete() const {
- return availableData == DataAvailability::All;
- }
- bool isIncomplete() const {
- return availableData == DataAvailability::Some;
- }
-
- void dumpDebugLogs() const;
-
- const OverscaledTileID id;
- optional<Timestamp> modified;
- optional<Timestamp> expires;
-
- // Contains the tile ID string for painting debug information.
- std::unique_ptr<DebugBucket> debugBucket;
-
-protected:
- enum class DataAvailability : uint8_t {
- // Still waiting for data to load or parse.
- None,
-
- // TileData is partially parsed, some buckets are still waiting for dependencies
- // to arrive, but it is good for rendering. Partial tiles can also be re-parsed,
- // but might remain in the same state if dependencies are still missing.
- Some,
-
- // TileData is fully parsed, and all buckets are available if they exist.
- All,
- };
-
- DataAvailability availableData = DataAvailability::None;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/tile/tile_id.hpp b/src/mbgl/tile/tile_id.hpp
index f9afdfa529..f561db9a4d 100644
--- a/src/mbgl/tile/tile_id.hpp
+++ b/src/mbgl/tile/tile_id.hpp
@@ -45,8 +45,7 @@ std::string toString(const CanonicalTileID&);
// z/x/y describe the
class OverscaledTileID {
public:
- OverscaledTileID(uint8_t overscaledZ, const CanonicalTileID&);
- OverscaledTileID(uint8_t overscaledZ, CanonicalTileID&&);
+ OverscaledTileID(uint8_t overscaledZ, CanonicalTileID);
OverscaledTileID(uint8_t overscaledZ, uint8_t z, uint32_t x, uint32_t y);
OverscaledTileID(uint8_t z, uint32_t x, uint32_t y);
explicit OverscaledTileID(const CanonicalTileID&);
@@ -75,8 +74,7 @@ std::string toString(const OverscaledTileID&);
class UnwrappedTileID {
public:
UnwrappedTileID(uint8_t z, int64_t x, int64_t y);
- UnwrappedTileID(int16_t wrap, const CanonicalTileID&);
- UnwrappedTileID(int16_t wrap, CanonicalTileID&&);
+ UnwrappedTileID(int16_t wrap, CanonicalTileID);
bool operator==(const UnwrappedTileID&) const;
bool operator!=(const UnwrappedTileID&) const;
bool operator<(const UnwrappedTileID&) const;
@@ -143,13 +141,8 @@ inline std::array<CanonicalTileID, 4> CanonicalTileID::children() const {
} };
}
-inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, const CanonicalTileID& canonical_)
- : overscaledZ(overscaledZ_), canonical(canonical_) {
- assert(overscaledZ >= canonical.z);
-}
-
-inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, CanonicalTileID&& canonical_)
- : overscaledZ(overscaledZ_), canonical(std::forward<CanonicalTileID>(canonical_)) {
+inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, CanonicalTileID canonical_)
+ : overscaledZ(overscaledZ_), canonical(std::move(canonical_)) {
assert(overscaledZ >= canonical.z);
}
@@ -216,12 +209,8 @@ inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_)
y_ < 0 ? 0 : std::min(static_cast<uint32_t>(y_), static_cast<uint32_t>(1ull << z_) - 1)) {
}
-inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, const CanonicalTileID& canonical_)
- : wrap(wrap_), canonical(canonical_) {
-}
-
-inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID&& canonical_)
- : wrap(wrap_), canonical(std::forward<CanonicalTileID>(canonical_)) {
+inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID canonical_)
+ : wrap(wrap_), canonical(std::move(canonical_)) {
}
inline bool UnwrappedTileID::operator==(const UnwrappedTileID& rhs) const {
diff --git a/src/mbgl/tile/tile_loader.hpp b/src/mbgl/tile/tile_loader.hpp
new file mode 100644
index 0000000000..0d64f647d7
--- /dev/null
+++ b/src/mbgl/tile/tile_loader.hpp
@@ -0,0 +1,62 @@
+#pragma once
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/tile/tile.hpp>
+
+namespace mbgl {
+
+class FileSource;
+class AsyncRequest;
+class Response;
+class Tileset;
+
+namespace style {
+class UpdateParameters;
+} // namespace style
+
+template <typename T>
+class TileLoader : private util::noncopyable {
+public:
+ TileLoader(T&,
+ const OverscaledTileID&,
+ const style::UpdateParameters&,
+ const Tileset&);
+ ~TileLoader();
+
+ using Necessity = Resource::Necessity;
+
+ void setNecessity(Necessity newNecessity) {
+ if (newNecessity != necessity) {
+ necessity = newNecessity;
+ if (necessity == Necessity::Required) {
+ makeRequired();
+ } else {
+ makeOptional();
+ }
+ }
+ }
+
+private:
+ // called when the tile is one of the ideal tiles that we want to show definitely. the tile source
+ // should try to make every effort (e.g. fetch from internet, or revalidate existing resources).
+ void makeRequired();
+
+ // called when the zoom level no longer corresponds to the displayed one, but
+ // we're still interested in retaining the tile, e.g. for backfill.
+ // subclassed TileSources should cancel actions they are taking to provide
+ // an up-to-date version or load new data
+ void makeOptional();
+
+ void loadOptional();
+ void loadedData(const Response&);
+ void loadRequired();
+
+ T& tile;
+ Necessity necessity;
+ Resource resource;
+ FileSource& fileSource;
+ std::unique_ptr<AsyncRequest> request;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/tile_loader_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp
new file mode 100644
index 0000000000..b987d6ec65
--- /dev/null
+++ b/src/mbgl/tile/tile_loader_impl.hpp
@@ -0,0 +1,113 @@
+#pragma once
+
+#include <mbgl/tile/tile_loader.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/style/update_parameters.hpp>
+#include <mbgl/util/tileset.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+
+template <typename T>
+TileLoader<T>::TileLoader(T& tile_,
+ const OverscaledTileID& id,
+ const style::UpdateParameters& parameters,
+ const Tileset& tileset)
+ : tile(tile_),
+ necessity(Necessity::Optional),
+ resource(Resource::tile(
+ tileset.tiles.at(0),
+ parameters.pixelRatio,
+ id.canonical.x,
+ id.canonical.y,
+ id.canonical.z)),
+ fileSource(parameters.fileSource) {
+ assert(!request);
+ if (fileSource.supportsOptionalRequests()) {
+ // When supported, the first request is always optional, even if the TileLoader
+ // is marked as required. That way, we can let the first optional request continue
+ // to load when the TileLoader is later changed from required to optional. If we
+ // started out with a required request, we'd have to cancel everything, including the
+ // initial optional part of the request.
+ loadOptional();
+ } else {
+ // When the FileSource doesn't support optional requests, we do nothing until the
+ // data is definitely required.
+ if (necessity == Necessity::Required) {
+ loadRequired();
+ } else {
+ // We're using this field to check whether the pending request is optional or required.
+ resource.necessity = Resource::Optional;
+ }
+ }
+}
+
+template <typename T>
+TileLoader<T>::~TileLoader() = default;
+
+template <typename T>
+void TileLoader<T>::loadOptional() {
+ assert(!request);
+
+ resource.necessity = Resource::Optional;
+ request = fileSource.request(resource, [this](Response res) {
+ request.reset();
+
+ tile.setTriedOptional();
+
+ if (res.error && res.error->reason == Response::Error::Reason::NotFound) {
+ // 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.priorExpires = Timestamp{ Seconds::zero() };
+ } else {
+ loadedData(res);
+ }
+
+ if (necessity == Necessity::Required) {
+ loadRequired();
+ }
+ });
+}
+
+template <typename T>
+void TileLoader<T>::makeRequired() {
+ if (!request) {
+ loadRequired();
+ }
+}
+
+template <typename T>
+void TileLoader<T>::makeOptional() {
+ if (resource.necessity == Resource::Required && request) {
+ // Abort a potential HTTP request.
+ request.reset();
+ }
+}
+
+template <typename T>
+void TileLoader<T>::loadedData(const Response& res) {
+ if (res.error && res.error->reason != Response::Error::Reason::NotFound) {
+ tile.setError(std::make_exception_ptr(std::runtime_error(res.error->message)));
+ } else if (res.notModified) {
+ resource.priorExpires = res.expires;
+ // Do not notify the tile; when we get this message, it already has the current
+ // version of the data.
+ } else {
+ resource.priorModified = res.modified;
+ resource.priorExpires = res.expires;
+ resource.priorEtag = res.etag;
+ tile.setData(res.noContent ? nullptr : res.data, res.modified, res.expires);
+ }
+}
+
+template <typename T>
+void TileLoader<T>::loadRequired() {
+ assert(!request);
+
+ resource.necessity = Resource::Required;
+ request = fileSource.request(resource, [this](Response res) { loadedData(res); });
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/tile_observer.hpp b/src/mbgl/tile/tile_observer.hpp
new file mode 100644
index 0000000000..96ded11fbb
--- /dev/null
+++ b/src/mbgl/tile/tile_observer.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <exception>
+
+namespace mbgl {
+
+class Tile;
+
+class TileObserver {
+public:
+ virtual ~TileObserver() = default;
+
+ virtual void onTileLoaded(Tile&, bool /*isNewTile*/) {}
+ virtual void onTileError(Tile&, std::exception_ptr) {}
+ virtual void onNeedsRepaint() {}
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/tile_worker.cpp b/src/mbgl/tile/tile_worker.cpp
index 1890aa7c47..5761ca83b9 100644
--- a/src/mbgl/tile/tile_worker.cpp
+++ b/src/mbgl/tile/tile_worker.cpp
@@ -1,9 +1,7 @@
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/tile/tile_worker.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/style/bucket_parameters.hpp>
-#include <mbgl/style/layers/background_layer.hpp>
-#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
@@ -13,21 +11,20 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/exception.hpp>
+
#include <utility>
namespace mbgl {
-using namespace mbgl::style;
+using namespace style;
-TileWorker::TileWorker(const OverscaledTileID& id_,
- std::string sourceID_,
+TileWorker::TileWorker(OverscaledTileID id_,
SpriteStore& spriteStore_,
GlyphAtlas& glyphAtlas_,
GlyphStore& glyphStore_,
- const util::Atomic<bool>& obsolete_,
+ const std::atomic<bool>& obsolete_,
const MapMode mode_)
- : id(id_),
- sourceID(std::move(sourceID_)),
+ : id(std::move(id_)),
spriteStore(spriteStore_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
@@ -40,14 +37,14 @@ TileWorker::~TileWorker() {
}
TileParseResult TileWorker::parseAllLayers(std::vector<std::unique_ptr<Layer>> layers_,
- std::unique_ptr<const GeometryTile> geometryTile_,
+ std::unique_ptr<const GeometryTileData> tileData_,
PlacementConfig config) {
// We're doing a fresh parse of the tile, because the underlying data has changed.
pending.clear();
placementPending.clear();
partialParse = false;
featureIndex = std::make_unique<FeatureIndex>();
- geometryTile = std::move(geometryTile_);
+ tileData = std::move(tileData_);
// Store the layers for use in redoPlacement.
layers = std::move(layers_);
@@ -98,7 +95,7 @@ TileParseResult TileWorker::prepareResult(const PlacementConfig& config) {
if (result.complete) {
featureIndex->setCollisionTile(placeLayers(config));
result.featureIndex = std::move(featureIndex);
- result.geometryTile = std::move(geometryTile);
+ result.tileData = std::move(tileData);
}
return std::move(result);
@@ -135,19 +132,7 @@ void TileWorker::parseLayer(const Layer* layer) {
if (obsolete)
return;
- // Background and custom layers are special cases.
- if (layer->is<BackgroundLayer>() || layer->is<CustomLayer>())
- return;
-
- // Skip this bucket if we are to not render this
- if ((layer->baseImpl->source != sourceID) ||
- (id.overscaledZ < std::floor(layer->baseImpl->minZoom)) ||
- (id.overscaledZ >= std::ceil(layer->baseImpl->maxZoom)) ||
- (layer->baseImpl->visibility == VisibilityType::None)) {
- return;
- }
-
- auto geometryLayer = geometryTile->getLayer(layer->baseImpl->sourceLayer);
+ auto geometryLayer = tileData->getLayer(layer->baseImpl->sourceLayer);
if (!geometryLayer) {
// The layer specified in the bucket does not exist. Do nothing.
if (debug::tileParseWarnings) {
@@ -188,4 +173,4 @@ void TileWorker::insertBucket(const std::string& name, std::unique_ptr<Bucket> b
}
}
-}
+} // namespace mbgl
diff --git a/src/mbgl/tile/tile_worker.hpp b/src/mbgl/tile/tile_worker.hpp
index 631a8c929f..872c058b43 100644
--- a/src/mbgl/tile/tile_worker.hpp
+++ b/src/mbgl/tile/tile_worker.hpp
@@ -2,13 +2,13 @@
#include <mbgl/map/mode.hpp>
#include <mbgl/tile/tile_id.hpp>
-#include <mbgl/util/atomic.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/variant.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/text/placement_config.hpp>
#include <mbgl/geometry/feature_index.hpp>
+#include <atomic>
#include <string>
#include <memory>
#include <mutex>
@@ -18,7 +18,7 @@
namespace mbgl {
class CollisionTile;
-class GeometryTile;
+class GeometryTileData;
class SpriteStore;
class GlyphAtlas;
class GlyphStore;
@@ -27,7 +27,7 @@ class Bucket;
namespace style {
class Layer;
class SymbolLayer;
-}
+} // namespace style
// We're using this class to shuttle the resulting buckets from the worker thread to the MapContext
// thread. This class is movable-only because the vector contains movable-only value elements.
@@ -36,7 +36,7 @@ public:
bool complete = false;
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
std::unique_ptr<FeatureIndex> featureIndex;
- std::unique_ptr<const GeometryTile> geometryTile;
+ std::unique_ptr<const GeometryTileData> tileData;
};
using TileParseResult = variant<
@@ -45,17 +45,16 @@ using TileParseResult = variant<
class TileWorker : public util::noncopyable {
public:
- TileWorker(const OverscaledTileID&,
- std::string sourceID,
+ TileWorker(OverscaledTileID,
SpriteStore&,
GlyphAtlas&,
GlyphStore&,
- const util::Atomic<bool>&,
+ const std::atomic<bool>&,
const MapMode);
~TileWorker();
TileParseResult parseAllLayers(std::vector<std::unique_ptr<style::Layer>>,
- std::unique_ptr<const GeometryTile> geometryTile,
+ std::unique_ptr<const GeometryTileData> tileData,
PlacementConfig);
TileParseResult parsePendingLayers(PlacementConfig);
@@ -70,12 +69,11 @@ private:
std::unique_ptr<CollisionTile> placeLayers(PlacementConfig);
const OverscaledTileID id;
- const std::string sourceID;
SpriteStore& spriteStore;
GlyphAtlas& glyphAtlas;
GlyphStore& glyphStore;
- const util::Atomic<bool>& obsolete;
+ const std::atomic<bool>& obsolete;
const MapMode mode;
bool partialParse = false;
@@ -83,7 +81,7 @@ private:
std::vector<std::unique_ptr<style::Layer>> layers;
std::unique_ptr<FeatureIndex> featureIndex;
- std::unique_ptr<const GeometryTile> geometryTile;
+ std::unique_ptr<const GeometryTileData> tileData;
// Contains buckets that we couldn't parse so far due to missing resources.
// They will be attempted on subsequent parses.
diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp
index 5126d0ce83..6a3c51c05d 100644
--- a/src/mbgl/tile/vector_tile.cpp
+++ b/src/mbgl/tile/vector_tile.cpp
@@ -1,14 +1,94 @@
#include <mbgl/tile/vector_tile.hpp>
-#include <mbgl/style/source.hpp>
-#include <mbgl/storage/resource.hpp>
-#include <mbgl/storage/response.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/util/url.hpp>
+#include <mbgl/tile/tile_loader_impl.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/style/update_parameters.hpp>
+#include <protozero/pbf_reader.hpp>
+
+#include <map>
+#include <unordered_map>
+#include <functional>
#include <utility>
namespace mbgl {
+class VectorTileLayer;
+
+using pbf_iter_type = protozero::pbf_reader::const_uint32_iterator;
+using packed_iter_type = std::pair<pbf_iter_type,pbf_iter_type>;
+
+class VectorTileFeature : public GeometryTileFeature {
+public:
+ VectorTileFeature(protozero::pbf_reader, const VectorTileLayer&);
+
+ FeatureType getType() const override { return type; }
+ optional<Value> getValue(const std::string&) const override;
+ std::unordered_map<std::string,Value> getProperties() const override;
+ optional<FeatureIdentifier> getID() const override;
+ GeometryCollection getGeometries() const override;
+
+private:
+ const VectorTileLayer& layer;
+ optional<FeatureIdentifier> id;
+ FeatureType type = FeatureType::Unknown;
+ packed_iter_type tags_iter;
+ packed_iter_type geometry_iter;
+};
+
+class VectorTileLayer : public GeometryTileLayer {
+public:
+ VectorTileLayer(protozero::pbf_reader);
+
+ std::size_t featureCount() const override { return features.size(); }
+ util::ptr<const GeometryTileFeature> getFeature(std::size_t) const override;
+ std::string getName() const override;
+
+private:
+ friend class VectorTileData;
+ friend class VectorTileFeature;
+
+ std::string name;
+ uint32_t version = 1;
+ uint32_t extent = 4096;
+ std::map<std::string, uint32_t> keysMap;
+ std::vector<std::reference_wrapper<const std::string>> keys;
+ std::vector<Value> values;
+ std::vector<protozero::pbf_reader> features;
+};
+
+class VectorTileData : public GeometryTileData {
+public:
+ VectorTileData(std::shared_ptr<const std::string> data);
+
+ util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
+
+private:
+ std::shared_ptr<const std::string> data;
+ mutable bool parsed = false;
+ mutable std::map<std::string, util::ptr<GeometryTileLayer>> layers;
+};
+
+VectorTile::VectorTile(const OverscaledTileID& id_,
+ std::string sourceID_,
+ const style::UpdateParameters& parameters,
+ const Tileset& tileset)
+ : GeometryTile(id_, sourceID_, parameters.style, parameters.mode),
+ loader(*this, id_, parameters, tileset) {
+}
+
+void VectorTile::setNecessity(Necessity necessity) {
+ loader.setNecessity(necessity);
+}
+
+void VectorTile::setData(std::shared_ptr<const std::string> data_,
+ optional<Timestamp> modified_,
+ optional<Timestamp> expires_) {
+ modified = modified_;
+ expires = expires_;
+
+ GeometryTile::setData(data_ ? std::make_unique<VectorTileData>(data_) : nullptr);
+}
+
Value parseValue(protozero::pbf_reader data) {
while (data.next())
{
@@ -40,7 +120,7 @@ VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, const Ve
while (feature_pbf.next()) {
switch (feature_pbf.tag()) {
case 1: // id
- id = feature_pbf.get_uint64();
+ id = { feature_pbf.get_uint64() };
break;
case 2: // tags
tags_iter = feature_pbf.get_packed_uint32();
@@ -105,7 +185,7 @@ std::unordered_map<std::string,Value> VectorTileFeature::getProperties() const {
return properties;
}
-optional<uint64_t> VectorTileFeature::getID() const {
+optional<FeatureIdentifier> VectorTileFeature::getID() const {
return id;
}
@@ -159,11 +239,11 @@ GeometryCollection VectorTileFeature::getGeometries() const {
return fixupPolygons(lines);
}
-VectorTile::VectorTile(std::shared_ptr<const std::string> data_)
+VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_)
: data(std::move(data_)) {
}
-util::ptr<GeometryTileLayer> VectorTile::getLayer(const std::string& name) const {
+util::ptr<GeometryTileLayer> VectorTileData::getLayer(const std::string& name) const {
if (!parsed) {
parsed = true;
protozero::pbf_reader tile_pbf(*data);
@@ -220,28 +300,4 @@ std::string VectorTileLayer::getName() const {
return name;
}
-VectorTileMonitor::VectorTileMonitor(const OverscaledTileID& tileID_, float pixelRatio_,
- const std::string& urlTemplate_, FileSource& fileSource_)
- : tileID(tileID_),
- pixelRatio(pixelRatio_),
- urlTemplate(urlTemplate_),
- fileSource(fileSource_) {
-}
-
-std::unique_ptr<AsyncRequest> VectorTileMonitor::monitorTile(const GeometryTileMonitor::Callback& callback) {
- const Resource resource = Resource::tile(urlTemplate, pixelRatio, tileID.canonical.x,
- tileID.canonical.y, tileID.canonical.z);
- return fileSource.request(resource, [callback, this](Response res) {
- if (res.error) {
- callback(std::make_exception_ptr(std::runtime_error(res.error->message)), nullptr, res.modified, res.expires);
- } else if (res.notModified) {
- return;
- } else if (res.noContent) {
- callback(nullptr, nullptr, res.modified, res.expires);
- } else {
- callback(nullptr, std::make_unique<VectorTile>(res.data), res.modified, res.expires);
- }
- });
-}
-
} // namespace mbgl
diff --git a/src/mbgl/tile/vector_tile.hpp b/src/mbgl/tile/vector_tile.hpp
index 9e81f0ec8c..2568a277e6 100644
--- a/src/mbgl/tile/vector_tile.hpp
+++ b/src/mbgl/tile/vector_tile.hpp
@@ -1,85 +1,30 @@
#pragma once
#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/tile/tile_id.hpp>
-#include <protozero/pbf_reader.hpp>
-
-#include <map>
-#include <unordered_map>
-#include <functional>
+#include <mbgl/tile/tile_loader.hpp>
namespace mbgl {
-class VectorTileLayer;
-
-using pbf_iter_type = protozero::pbf_reader::const_uint32_iterator;
-using packed_iter_type = std::pair<pbf_iter_type,pbf_iter_type>;
-
-class VectorTileFeature : public GeometryTileFeature {
-public:
- VectorTileFeature(protozero::pbf_reader, const VectorTileLayer&);
+class Tileset;
- FeatureType getType() const override { return type; }
- optional<Value> getValue(const std::string&) const override;
- std::unordered_map<std::string,Value> getProperties() const override;
- optional<uint64_t> getID() const override;
- GeometryCollection getGeometries() const override;
-
-private:
- const VectorTileLayer& layer;
- optional<uint64_t> id;
- FeatureType type = FeatureType::Unknown;
- packed_iter_type tags_iter;
- packed_iter_type geometry_iter;
-};
-
-class VectorTileLayer : public GeometryTileLayer {
-public:
- VectorTileLayer(protozero::pbf_reader);
-
- std::size_t featureCount() const override { return features.size(); }
- util::ptr<const GeometryTileFeature> getFeature(std::size_t) const override;
- std::string getName() const override;
-
-private:
- friend class VectorTile;
- friend class VectorTileFeature;
-
- std::string name;
- uint32_t version = 1;
- uint32_t extent = 4096;
- std::map<std::string, uint32_t> keysMap;
- std::vector<std::reference_wrapper<const std::string>> keys;
- std::vector<Value> values;
- std::vector<protozero::pbf_reader> features;
-};
+namespace style {
+class UpdateParameters;
+} // namespace style
class VectorTile : public GeometryTile {
public:
- VectorTile(std::shared_ptr<const std::string> data);
-
- util::ptr<GeometryTileLayer> getLayer(const std::string&) const override;
-
-private:
- std::shared_ptr<const std::string> data;
- mutable bool parsed = false;
- mutable std::map<std::string, util::ptr<GeometryTileLayer>> layers;
-};
-
-class TileID;
-class FileSource;
-
-class VectorTileMonitor : public GeometryTileMonitor {
-public:
- VectorTileMonitor(const OverscaledTileID&, float pixelRatio, const std::string& urlTemplate, FileSource&);
+ VectorTile(const OverscaledTileID&,
+ std::string sourceID,
+ const style::UpdateParameters&,
+ const Tileset&);
- std::unique_ptr<AsyncRequest> monitorTile(const GeometryTileMonitor::Callback&) override;
+ void setNecessity(Necessity) final;
+ void setData(std::shared_ptr<const std::string> data,
+ optional<Timestamp> modified,
+ optional<Timestamp> expires);
private:
- OverscaledTileID tileID;
- float pixelRatio;
- std::string urlTemplate;
- FileSource& fileSource;
+ TileLoader<VectorTile> loader;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/vector_tile_data.cpp b/src/mbgl/tile/vector_tile_data.cpp
deleted file mode 100644
index 1ab2566a25..0000000000
--- a/src/mbgl/tile/vector_tile_data.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-#include <mbgl/tile/vector_tile_data.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/style/layer_impl.hpp>
-#include <mbgl/util/worker.hpp>
-#include <mbgl/util/work_request.hpp>
-#include <mbgl/style/style.hpp>
-#include <mbgl/storage/file_source.hpp>
-#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/text/collision_tile.hpp>
-#include <mbgl/map/transform_state.hpp>
-
-namespace mbgl {
-
-VectorTileData::VectorTileData(const OverscaledTileID& id_,
- std::unique_ptr<GeometryTileMonitor> monitor_,
- std::string sourceID,
- style::Style& style_,
- const MapMode mode_,
- const std::function<void(std::exception_ptr)>& callback)
- : TileData(id_),
- style(style_),
- worker(style_.workers),
- tileWorker(id_,
- sourceID,
- *style_.spriteStore,
- *style_.glyphAtlas,
- *style_.glyphStore,
- obsolete,
- mode_),
- monitor(std::move(monitor_))
-{
- tileRequest = monitor->monitorTile([callback, this](std::exception_ptr err,
- std::unique_ptr<GeometryTile> tile,
- optional<Timestamp> modified_,
- optional<Timestamp> expires_) {
- if (err) {
- callback(err);
- return;
- }
-
- modified = modified_;
- expires = expires_;
-
- if (!tile) {
- // This is a 404 response. We're treating these as empty tiles.
- workRequest.reset();
- availableData = DataAvailability::All;
- buckets.clear();
- callback(err);
- return;
- }
-
- // Mark the tile as pending again if it was complete before to prevent signaling a complete
- // state despite pending parse operations.
- if (availableData == DataAvailability::All) {
- availableData = DataAvailability::Some;
- }
-
- // Kick off a fresh parse of this tile. This happens when the tile is new, or
- // when tile data changed. Replacing the workdRequest will cancel a pending work
- // request in case there is one.
- workRequest.reset();
- workRequest = worker.parseGeometryTile(tileWorker, style.getLayers(), std::move(tile), targetConfig, [callback, this, config = targetConfig] (TileParseResult result) {
- workRequest.reset();
-
- std::exception_ptr error;
- if (result.is<TileParseResultData>()) {
- auto& resultBuckets = result.get<TileParseResultData>();
- availableData =
- resultBuckets.complete ? DataAvailability::All : DataAvailability::Some;
-
- // Persist the configuration we just placed so that we can later check whether we need to
- // place again in case the configuration has changed.
- placedConfig = config;
-
- // Move over all buckets we received in this parse request, potentially overwriting
- // existing buckets in case we got a refresh parse.
- buckets = std::move(resultBuckets.buckets);
-
- if (isComplete()) {
- featureIndex = std::move(resultBuckets.featureIndex);
- geometryTile = std::move(resultBuckets.geometryTile);
- }
-
- } else {
- // This is triggered when parsing fails (e.g. due to an invalid vector tile)
- error = result.get<std::exception_ptr>();
- availableData = DataAvailability::All;
- }
-
- callback(error);
- });
- });
-}
-
-VectorTileData::~VectorTileData() {
- cancel();
-}
-
-bool VectorTileData::parsePending(std::function<void(std::exception_ptr)> callback) {
- if (workRequest) {
- // There's already parsing or placement going on.
- return false;
- }
-
- workRequest.reset();
- workRequest = worker.parsePendingGeometryTileLayers(tileWorker, targetConfig, [this, callback, config = targetConfig] (TileParseResult result) {
- workRequest.reset();
-
- std::exception_ptr error;
- if (result.is<TileParseResultData>()) {
- auto& resultBuckets = result.get<TileParseResultData>();
- availableData =
- resultBuckets.complete ? DataAvailability::All : DataAvailability::Some;
-
- // Move over all buckets we received in this parse request, potentially overwriting
- // existing buckets in case we got a refresh parse.
- for (auto& bucket : resultBuckets.buckets) {
- buckets[bucket.first] = std::move(bucket.second);
- }
-
- // Persist the configuration we just placed so that we can later check whether we need to
- // place again in case the configuration has changed.
- placedConfig = config;
-
- if (isComplete()) {
- featureIndex = std::move(resultBuckets.featureIndex);
- geometryTile = std::move(resultBuckets.geometryTile);
- }
-
- } else {
- error = result.get<std::exception_ptr>();
- availableData = DataAvailability::All;
- }
-
- callback(error);
- });
-
- return true;
-}
-
-Bucket* VectorTileData::getBucket(const style::Layer& layer) {
- const auto it = buckets.find(layer.baseImpl->bucketName());
- if (it == buckets.end()) {
- return nullptr;
- }
-
- assert(it->second);
- return it->second.get();
-}
-
-void VectorTileData::redoPlacement(const PlacementConfig newConfig, const std::function<void()>& callback) {
- if (newConfig != placedConfig) {
- targetConfig = newConfig;
-
- redoPlacement(callback);
- }
-}
-
-void VectorTileData::redoPlacement(const std::function<void()>& callback) {
- // Don't start a new placement request when the current one hasn't completed yet, or when
- // we are parsing buckets.
- if (workRequest) return;
-
- workRequest = worker.redoPlacement(tileWorker, buckets, targetConfig, [this, callback, config = targetConfig](std::unique_ptr<CollisionTile> collisionTile) {
- workRequest.reset();
-
- // Persist the configuration we just placed so that we can later check whether we need to
- // place again in case the configuration has changed.
- placedConfig = config;
-
- for (auto& bucket : buckets) {
- bucket.second->swapRenderData();
- }
-
- if (featureIndex) {
- featureIndex->setCollisionTile(std::move(collisionTile));
- }
-
- // The target configuration could have changed since we started placement. In this case,
- // we're starting another placement run.
- if (placedConfig != targetConfig) {
- redoPlacement(callback);
- } else {
- callback();
- }
- });
-}
-
-void VectorTileData::queryRenderedFeatures(
- std::unordered_map<std::string, std::vector<Feature>>& result,
- const GeometryCoordinates& queryGeometry,
- const TransformState& transformState,
- const optional<std::vector<std::string>>& layerIDs) {
-
- if (!featureIndex || !geometryTile) return;
-
- featureIndex->query(result,
- { queryGeometry },
- transformState.getAngle(),
- util::tileSize * id.overscaleFactor(),
- std::pow(2, transformState.getZoom() - id.overscaledZ),
- layerIDs,
- *geometryTile,
- id.canonical,
- style);
-}
-
-void VectorTileData::cancel() {
- obsolete = true;
- tileRequest.reset();
- workRequest.reset();
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/tile/vector_tile_data.hpp b/src/mbgl/tile/vector_tile_data.hpp
deleted file mode 100644
index df2a23fda6..0000000000
--- a/src/mbgl/tile/vector_tile_data.hpp
+++ /dev/null
@@ -1,75 +0,0 @@
-#pragma once
-
-#include <mbgl/tile/tile_data.hpp>
-#include <mbgl/tile/tile_worker.hpp>
-#include <mbgl/text/placement_config.hpp>
-#include <mbgl/util/atomic.hpp>
-#include <mbgl/util/feature.hpp>
-
-#include <memory>
-#include <unordered_map>
-
-namespace mbgl {
-
-class AsyncRequest;
-class GeometryTileMonitor;
-class FeatureIndex;
-
-namespace style {
-class Style;
-}
-
-class VectorTileData : public TileData {
-public:
- VectorTileData(const OverscaledTileID&,
- std::unique_ptr<GeometryTileMonitor> monitor,
- std::string sourceID,
- style::Style&,
- const MapMode,
- const std::function<void(std::exception_ptr)>& callback);
-
- ~VectorTileData();
-
- Bucket* getBucket(const style::Layer&) override;
-
- bool parsePending(std::function<void(std::exception_ptr)> callback) override;
-
- void redoPlacement(PlacementConfig config, const std::function<void()>&) override;
- void redoPlacement(const std::function<void()>&) override;
-
- void queryRenderedFeatures(
- std::unordered_map<std::string, std::vector<Feature>>& result,
- const GeometryCoordinates& queryGeometry,
- const TransformState&,
- const optional<std::vector<std::string>>& layerIDs) override;
-
- void cancel() override;
-
-private:
- style::Style& style;
- Worker& worker;
- TileWorker tileWorker;
-
- std::unique_ptr<GeometryTileMonitor> monitor;
- std::unique_ptr<AsyncRequest> tileRequest;
- std::unique_ptr<AsyncRequest> workRequest;
-
- // Contains all the Bucket objects for the tile. Buckets are render
- // objects and they get added by tile parsing operations.
- std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
-
- std::unique_ptr<FeatureIndex> featureIndex;
- std::unique_ptr<const GeometryTile> geometryTile;
-
- // Stores the placement configuration of the text that is currently placed on the screen.
- PlacementConfig placedConfig;
-
- // Stores the placement configuration of how the text should be placed. This isn't necessarily
- // the one that is being displayed.
- PlacementConfig targetConfig;
-
- // Used to signal the worker that it should abandon parsing this tile as soon as possible.
- util::Atomic<bool> obsolete { false };
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/util/atomic.hpp b/src/mbgl/util/atomic.hpp
deleted file mode 100644
index 3f68bf46a5..0000000000
--- a/src/mbgl/util/atomic.hpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#pragma once
-
-#include <atomic>
-#include <mutex>
-
-namespace mbgl {
-namespace util {
-
-// std::atomic<bool> is implemented lock free which
-// is not supported by ARMv5 but the code generated
-// seems to be using that and not working at all,
-// thus, this naive implementation using mutexes.
-#if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__)
-
-template <typename T>
-class Atomic {
-public:
- Atomic() = default;
- explicit Atomic(const T& data_) : data(data_) {}
-
- void operator=(const T& other) {
- std::lock_guard<std::mutex> lock(mtx);
- data = other;
- }
-
- operator bool() const {
- std::lock_guard<std::mutex> lock(mtx);
-
- return data;
- }
-
-private:
- T data;
- mutable std::mutex mtx;
-};
-
-#else
-
-template <typename T>
-using Atomic = std::atomic<T>;
-
-#endif
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/clip_id.hpp b/src/mbgl/util/clip_id.hpp
index 064a7acf94..de2dc51919 100644
--- a/src/mbgl/util/clip_id.hpp
+++ b/src/mbgl/util/clip_id.hpp
@@ -11,20 +11,18 @@
namespace mbgl {
-class Tile;
-
struct ClipID {
- inline ClipID() {}
- inline ClipID(const std::string &mask_, const std::string &reference_) : mask(mask_), reference(reference_) {}
+ ClipID() {}
+ ClipID(const std::string &mask_, const std::string &reference_) : mask(mask_), reference(reference_) {}
std::bitset<8> mask;
std::bitset<8> reference;
- inline bool operator==(const ClipID &other) const {
+ bool operator==(const ClipID &other) const {
return mask == other.mask && reference == other.reference;
}
- inline ClipID& operator|=(const ClipID &other) {
+ ClipID& operator|=(const ClipID &other) {
mask |= other.mask;
reference |= other.reference;
return *this;
diff --git a/src/mbgl/util/color.cpp b/src/mbgl/util/color.cpp
new file mode 100644
index 0000000000..eea897d3ab
--- /dev/null
+++ b/src/mbgl/util/color.cpp
@@ -0,0 +1,20 @@
+#include <mbgl/util/color.hpp>
+
+#include <csscolorparser/csscolorparser.hpp>
+
+namespace mbgl {
+
+optional<Color> Color::parse(const std::string& s) {
+ CSSColorParser::Color css_color = CSSColorParser::parse(s);
+
+ // Premultiply the color.
+ const float factor = css_color.a / 255;
+ return {{
+ css_color.r * factor,
+ css_color.g * factor,
+ css_color.b * factor,
+ css_color.a
+ }};
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/util/convert.cpp b/src/mbgl/util/convert.cpp
new file mode 100644
index 0000000000..97bfe9108d
--- /dev/null
+++ b/src/mbgl/util/convert.cpp
@@ -0,0 +1,9 @@
+#include <mbgl/util/convert.hpp>
+
+namespace mbgl {
+namespace util {
+
+template std::array<float, 2> convert(const std::array<int32_t, 2>&);
+
+} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/dtoa.cpp b/src/mbgl/util/dtoa.cpp
index 7ccee345f6..dd4fba0f89 100644
--- a/src/mbgl/util/dtoa.cpp
+++ b/src/mbgl/util/dtoa.cpp
@@ -9,7 +9,7 @@ namespace {
// From https://github.com/miloyip/rapidjson/blob/master/include/rapidjson/internal/dtoa.h
-inline char* Prettify(char* buffer, int length, int k) {
+char* Prettify(char* buffer, int length, int k) {
constexpr int maxDecimalPlaces = 324;
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
@@ -93,5 +93,13 @@ char* dtoa(double value, char* buffer) {
}
}
+std::string dtoa(double value) {
+ std::string data;
+ data.resize(25);
+ auto end = dtoa(value, const_cast<char*>(data.data()));
+ data.resize(end - data.data());
+ return data;
+}
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/dtoa.hpp b/src/mbgl/util/dtoa.hpp
index 17614732db..db7d309452 100644
--- a/src/mbgl/util/dtoa.hpp
+++ b/src/mbgl/util/dtoa.hpp
@@ -6,14 +6,7 @@ namespace mbgl {
namespace util {
char* dtoa(double value, char* buffer);
-
-inline std::string dtoa(double value) {
- std::string data;
- data.resize(25);
- auto end = dtoa(value, const_cast<char*>(data.data()));
- data.resize(end - data.data());
- return data;
-}
+std::string dtoa(double value);
} // end namespace util
} // end namespace mbgl
diff --git a/src/mbgl/util/exclusive.hpp b/src/mbgl/util/exclusive.hpp
index 946604af69..844588dc90 100644
--- a/src/mbgl/util/exclusive.hpp
+++ b/src/mbgl/util/exclusive.hpp
@@ -10,12 +10,12 @@ namespace util {
template <class T>
class exclusive {
public:
- inline exclusive(T* val, std::unique_ptr<std::lock_guard<std::mutex>> mtx) : ptr(val), lock(std::move(mtx)) {}
+ exclusive(T* val, std::unique_ptr<std::lock_guard<std::mutex>> mtx) : ptr(val), lock(std::move(mtx)) {}
- inline T* operator->() { return ptr; }
- inline const T* operator->() const { return ptr; }
- inline T* operator*() { return ptr; }
- inline const T* operator*() const { return ptr; }
+ T* operator->() { return ptr; }
+ const T* operator->() const { return ptr; }
+ T* operator*() { return ptr; }
+ const T* operator*() const { return ptr; }
private:
T *ptr;
diff --git a/src/mbgl/util/geo.cpp b/src/mbgl/util/geo.cpp
index dd40af882b..fe24334e82 100644
--- a/src/mbgl/util/geo.cpp
+++ b/src/mbgl/util/geo.cpp
@@ -8,12 +8,12 @@ namespace mbgl {
namespace {
-inline double lat(const uint8_t z, const int64_t y) {
+double lat(const uint8_t z, const int64_t y) {
const double n = M_PI - 2.0 * M_PI * y / std::pow(2.0, z);
return util::RAD2DEG * std::atan(0.5 * (std::exp(n) - std::exp(-n)));
}
-inline double lon(const uint8_t z, const int64_t x) {
+double lon(const uint8_t z, const int64_t x) {
return x / std::pow(2.0, z) * util::DEGREES_MAX - util::LONGITUDE_MAX;
}
diff --git a/src/mbgl/util/grid_index.cpp b/src/mbgl/util/grid_index.cpp
index 4877a90e72..b3afd3fdc8 100644
--- a/src/mbgl/util/grid_index.cpp
+++ b/src/mbgl/util/grid_index.cpp
@@ -1,5 +1,6 @@
#include <mbgl/util/grid_index.hpp>
#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/math/minmax.hpp>
#include <unordered_set>
@@ -17,7 +18,7 @@ GridIndex<T>::GridIndex(int32_t extent_, int32_t n_, int32_t padding_) :
max(extent + double(padding) / n * extent)
{
cells.resize(d * d);
- };
+ }
template <class T>
void GridIndex<T>::insert(T&& t, const BBox& bbox) {
@@ -28,9 +29,10 @@ void GridIndex<T>::insert(T&& t, const BBox& bbox) {
auto cx2 = convertToCellCoord(bbox.max.x);
auto cy2 = convertToCellCoord(bbox.max.y);
- for (int32_t x = cx1; x <= cx2; x++) {
- for (int32_t y = cy1; y <= cy2; y++) {
- auto cellIndex = d * y + x;
+ int32_t x, y, cellIndex;
+ for (x = cx1; x <= cx2; ++x) {
+ for (y = cy1; y <= cy2; ++y) {
+ cellIndex = d * y + x;
cells[cellIndex].push_back(uid);
}
}
@@ -48,9 +50,10 @@ std::vector<T> GridIndex<T>::query(const BBox& queryBBox) const {
auto cx2 = convertToCellCoord(queryBBox.max.x);
auto cy2 = convertToCellCoord(queryBBox.max.y);
- for (int32_t x = cx1; x <= cx2; x++) {
- for (int32_t y = cy1; y <= cy2; y++) {
- auto cellIndex = d * y + x;
+ int32_t x, y, cellIndex;
+ for (x = cx1; x <= cx2; ++x) {
+ for (y = cy1; y <= cy2; ++y) {
+ cellIndex = d * y + x;
for (auto uid : cells[cellIndex]) {
if (seenUids.count(uid) == 0) {
seenUids.insert(uid);
@@ -75,8 +78,8 @@ std::vector<T> GridIndex<T>::query(const BBox& queryBBox) const {
template <class T>
int32_t GridIndex<T>::convertToCellCoord(int32_t x) const {
- return std::max(0.0, std::min(d - 1.0, std::floor(x * scale) + padding));
+ return util::max(0.0, util::min(d - 1.0, std::floor(x * scale) + padding));
}
template class GridIndex<IndexedSubfeature>;
-}
+} // namespace mbgl
diff --git a/src/mbgl/util/grid_index.hpp b/src/mbgl/util/grid_index.hpp
index 656a4bdbd8..8ef8fb35b7 100644
--- a/src/mbgl/util/grid_index.hpp
+++ b/src/mbgl/util/grid_index.hpp
@@ -35,4 +35,4 @@ private:
};
-}
+} // namespace mbgl
diff --git a/src/mbgl/util/interpolate.hpp b/src/mbgl/util/interpolate.hpp
index 18a9e6bbc5..8b9929fa4a 100644
--- a/src/mbgl/util/interpolate.hpp
+++ b/src/mbgl/util/interpolate.hpp
@@ -5,6 +5,7 @@
#include <string>
#include <type_traits>
#include <utility>
+#include <mbgl/util/color.hpp>
namespace mbgl {
namespace util {
@@ -41,6 +42,19 @@ public:
}
};
+template <>
+struct Interpolator<Color> {
+public:
+ Color operator()(const Color& a, const Color& b, const double t) {
+ return {
+ interpolate(a.r, b.r, t),
+ interpolate(a.g, b.g, t),
+ interpolate(a.b, b.b, t),
+ interpolate(a.a, b.a, t)
+ };
+ }
+};
+
struct Uninterpolated {
template <class T>
T operator()(const T&, const T& b, const double) const {
diff --git a/src/mbgl/util/intersection_tests.cpp b/src/mbgl/util/intersection_tests.cpp
index 3e4ae27cd8..74b3bec388 100644
--- a/src/mbgl/util/intersection_tests.cpp
+++ b/src/mbgl/util/intersection_tests.cpp
@@ -143,5 +143,5 @@ bool multiPolygonIntersectsMultiPolygon(const GeometryCollection& multiPolygonA,
return false;
}
-}
-}
+} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/intersection_tests.hpp b/src/mbgl/util/intersection_tests.hpp
index a3ffe65cbf..be89d14ed6 100644
--- a/src/mbgl/util/intersection_tests.hpp
+++ b/src/mbgl/util/intersection_tests.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
namespace mbgl {
namespace util {
@@ -9,5 +9,5 @@ bool multiPolygonIntersectsBufferedMultiPoint(const GeometryCollection&, const G
bool multiPolygonIntersectsBufferedMultiLine(const GeometryCollection&, const GeometryCollection&, float radius);
bool multiPolygonIntersectsMultiPolygon(const GeometryCollection&, const GeometryCollection&);
-}
+} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/io.hpp b/src/mbgl/util/io.hpp
index 2679eb5360..795a465328 100644
--- a/src/mbgl/util/io.hpp
+++ b/src/mbgl/util/io.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
namespace util {
struct IOException : std::runtime_error {
- inline IOException(int err, const char* msg) : std::runtime_error(msg), code(err) {
+ IOException(int err, const char* msg) : std::runtime_error(msg), code(err) {
}
const int code = 0;
};
diff --git a/src/mbgl/util/mat2.cpp b/src/mbgl/util/mat2.cpp
index 6910244541..4fb5abafb8 100644
--- a/src/mbgl/util/mat2.cpp
+++ b/src/mbgl/util/mat2.cpp
@@ -24,7 +24,7 @@
#include <cmath>
-using namespace mbgl;
+namespace mbgl {
void matrix::identity(mat2& out) {
out[0] = 1.0f;
@@ -41,7 +41,7 @@ void matrix::rotate(mat2& out, const mat2& a, double rad) {
out[1] = a1 * c + a3 * s;
out[2] = a0 * -s + a2 * c;
out[3] = a1 * -s + a3 * c;
-};
+}
void matrix::scale(mat2& out, const mat2& a, double v0, double v1) {
double a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
@@ -50,3 +50,5 @@ void matrix::scale(mat2& out, const mat2& a, double v0, double v1) {
out[2] = a2 * v1;
out[3] = a3 * v1;
}
+
+} // namespace mbgl
diff --git a/src/mbgl/util/mat3.cpp b/src/mbgl/util/mat3.cpp
index f4ae8841d5..e2200867ce 100644
--- a/src/mbgl/util/mat3.cpp
+++ b/src/mbgl/util/mat3.cpp
@@ -24,7 +24,7 @@
#include <cmath>
-using namespace mbgl;
+namespace mbgl {
void matrix::identity(mat3& out) {
out[0] = 1.0f;
@@ -80,7 +80,7 @@ void matrix::rotate(mat3& out, const mat3& a, double rad) {
out[6] = a20;
out[7] = a21;
out[8] = a22;
-};
+}
void matrix::scale(mat3& out, const mat3& a, double x, double y) {
out[0] = x * a[0];
@@ -93,3 +93,5 @@ void matrix::scale(mat3& out, const mat3& a, double x, double y) {
out[7] = a[7];
out[8] = a[8];
}
+
+} // namespace mbgl
diff --git a/src/mbgl/util/math.cpp b/src/mbgl/util/math.cpp
index 0e86849ad3..7b1516c041 100644
--- a/src/mbgl/util/math.cpp
+++ b/src/mbgl/util/math.cpp
@@ -11,8 +11,8 @@ uint32_t ceil_log2(uint64_t x) {
uint32_t y = (((x & (x - 1)) == 0) ? 0 : 1);
uint32_t j = 32;
- for (int32_t i = 0; i < 6; i++) {
- const uint32_t k = (((x & t[i]) == 0) ? 0 : j);
+ for (const auto& i : t) {
+ const uint32_t k = (((x & i) == 0) ? 0 : j);
y += k;
x >>= k;
j >>= 1;
@@ -24,7 +24,7 @@ uint32_t ceil_log2(uint64_t x) {
double log2(double x) {
// log2() is producing wrong results on ARMv5 binaries
// running on ARMv7+ CPUs.
-#if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__)
+#if defined(__ANDROID__)
return std::log(x) / 0.6931471805599453; // log(x) / log(2)
#else
return ::log2(x);
diff --git a/src/mbgl/util/math.hpp b/src/mbgl/util/math.hpp
index bfedc2a421..5d4220d0a2 100644
--- a/src/mbgl/util/math.hpp
+++ b/src/mbgl/util/math.hpp
@@ -14,18 +14,18 @@ namespace util {
// Find the angle of the two vectors, solving the formula for the cross product
// a x b = |a||b|sin(θ) for θ.
template <typename T = double, typename S>
-inline T angle_between(const Point<S>& a, const Point<S>& b) {
+T angle_between(const Point<S>& a, const Point<S>& b) {
return std::atan2((a.x * b.y - a.y * b.x), a.x * b.x + a.y * b.y);
}
template <typename T = double, typename S>
-inline T angle_to(const Point<S>& a, const Point<S>& b) {
+T angle_to(const Point<S>& a, const Point<S>& b) {
return std::atan2(a.y - b.y, a.x - b.x);
}
// Reflect an angle around 0 degrees
template <typename T>
-inline std::array<T, 2> flip(const std::array<T, 2>& c) {
+std::array<T, 2> flip(const std::array<T, 2>& c) {
return {{
static_cast<T>(2 * M_PI - c[0]),
static_cast<T>(2 * M_PI - c[1])
@@ -33,7 +33,7 @@ inline std::array<T, 2> flip(const std::array<T, 2>& c) {
}
template <typename T, typename S1, typename S2>
-inline Point<T> normal(const S1& a, const S2& b) {
+Point<T> normal(const S1& a, const S2& b) {
T dx = b.x - a.x;
T dy = b.y - a.y;
T c = std::sqrt(dx * dx + dy * dy);
@@ -41,12 +41,12 @@ inline Point<T> normal(const S1& a, const S2& b) {
}
template <typename T>
-inline T perp(const T& a) {
+T perp(const T& a) {
return T(-a.y, a.x);
}
template <typename T, typename S1, typename S2>
-inline T dist(const S1& a, const S2& b) {
+T dist(const S1& a, const S2& b) {
T dx = b.x - a.x;
T dy = b.y - a.y;
T c = std::sqrt(dx * dx + dy * dy);
@@ -54,7 +54,7 @@ inline T dist(const S1& a, const S2& b) {
}
template <typename T, typename S1, typename S2>
-inline T distSqr(const S1& a, const S2& b) {
+T distSqr(const S1& a, const S2& b) {
T dx = b.x - a.x;
T dy = b.y - a.y;
T c = dx * dx + dy * dy;
@@ -62,23 +62,23 @@ inline T distSqr(const S1& a, const S2& b) {
}
template <typename T>
-inline T round(const T& a) {
+T round(const T& a) {
return T(::round(a.x), ::round(a.y));
}
template <typename T>
-inline T length(T a, T b) {
+T length(T a, T b) {
return std::sqrt(a * a + b * b);
}
// Take the magnitude of vector a.
template <typename T = double, typename S>
-inline T mag(const S& a) {
+T mag(const S& a) {
return std::sqrt(a.x * a.x + a.y * a.y);
}
template <typename S>
-inline S unit(const S& a) {
+S unit(const S& a) {
auto magnitude = mag(a);
if (magnitude == 0) {
return a;
@@ -87,7 +87,7 @@ inline S unit(const S& a) {
}
template <typename T, typename S = double>
-inline T rotate(const T& a, S angle) {
+T rotate(const T& a, S angle) {
S cos = std::cos(angle);
S sin = std::sin(angle);
S x = cos * a.x - sin * a.y;
@@ -96,7 +96,7 @@ inline T rotate(const T& a, S angle) {
}
template <typename T>
-inline Point<T> matrixMultiply(const std::array<T, 4>& m, const Point<T>& p) {
+Point<T> matrixMultiply(const std::array<T, 4>& m, const Point<T>& p) {
return Point<T>(m[0] * p.x + m[1] * p.y, m[2] * p.x + m[3] * p.y);
}
diff --git a/src/mbgl/util/ptr.hpp b/src/mbgl/util/ptr.hpp
index 2dc8118181..87c4e9f7cf 100644
--- a/src/mbgl/util/ptr.hpp
+++ b/src/mbgl/util/ptr.hpp
@@ -10,14 +10,14 @@ template <typename T>
class ptr : public ::std::shared_ptr<T> {
public:
template <typename... Args>
- inline ptr(Args &&... args)
+ ptr(Args &&... args)
: ::std::shared_ptr<T>(::std::forward<Args>(args)...) {}
- inline auto operator->() const -> decltype(this->::std::shared_ptr<T>::operator->()) {
+ auto operator->() const -> decltype(this->::std::shared_ptr<T>::operator->()) {
assert(*this);
return ::std::shared_ptr<T>::operator->();
}
- inline auto operator*() const -> decltype(this->::std::shared_ptr<T>::operator*()) {
+ auto operator*() const -> decltype(this->::std::shared_ptr<T>::operator*()) {
assert(*this);
return ::std::shared_ptr<T>::operator*();
}
diff --git a/src/mbgl/util/raster.cpp b/src/mbgl/util/raster.cpp
index 3146a00513..3f9cb467b9 100644
--- a/src/mbgl/util/raster.cpp
+++ b/src/mbgl/util/raster.cpp
@@ -1,5 +1,6 @@
#include <mbgl/platform/platform.hpp>
#include <mbgl/gl/gl.hpp>
+#include <mbgl/gl/gl_config.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/raster.hpp>
@@ -7,59 +8,80 @@
#include <cassert>
#include <cstring>
-using namespace mbgl;
-
-Raster::Raster(gl::TexturePool& texturePool_)
- : texturePool(texturePool_)
-{}
+namespace mbgl {
bool Raster::isLoaded() const {
- std::lock_guard<std::mutex> lock(mtx);
return loaded;
}
-void Raster::load(PremultipliedImage image) {
+void Raster::load(PremultipliedImage image, uint32_t mipmapLevel) {
assert(image.data.get());
- img = std::move(image);
- width = GLsizei(img.width);
- height = GLsizei(img.height);
+ if (images.size() <= mipmapLevel) {
+ images.resize(mipmapLevel + 1);
+ }
+ images.at(mipmapLevel) = std::move(image);
- std::lock_guard<std::mutex> lock(mtx);
loaded = true;
}
+void Raster::bind(gl::ObjectStore& store,
+ gl::Config& config,
+ uint32_t unit,
+ Scaling newFilter,
+ MipMap newMipMap) {
+ bool updateFilter = false;
-void Raster::bind(bool linear, gl::ObjectStore& store) {
- if (!width || !height) {
- Log::Error(Event::OpenGL, "trying to bind texture without dimension");
- return;
+ if (!texture) {
+ if (images.empty()) {
+ Log::Error(Event::OpenGL, "trying to bind texture without images");
+ return;
+ } else {
+ upload(store, config, unit);
+ updateFilter = true;
+ }
+ } else {
+ if (config.texture[unit] != *texture) {
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
+ }
+ updateFilter = (filter != newFilter || mipmap != newMipMap);
}
- if (img.data && !texture) {
- upload(store);
- } else if (texture) {
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
- }
-
- GLint new_filter = linear ? GL_LINEAR : GL_NEAREST;
- if (new_filter != this->filter) {
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, new_filter));
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, new_filter));
- filter = new_filter;
+ if (updateFilter) {
+ filter = newFilter;
+ mipmap = newMipMap;
+ config.activeTexture = unit;
+ MBGL_CHECK_ERROR(glTexParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ filter == Scaling::Linear
+ ? (mipmap == MipMap::Yes ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR)
+ : (mipmap == MipMap::Yes ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST)));
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ filter == Scaling::Linear ? GL_LINEAR : GL_NEAREST));
}
}
-void Raster::upload(gl::ObjectStore& store) {
- if (img.data && !texture) {
- texture = texturePool.acquireTexture(store);
- MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, *texture));
+void Raster::upload(gl::ObjectStore& store, gl::Config& config, uint32_t unit) {
+ if (!images.empty() && !texture) {
+ texture = store.createTexture();
+ config.activeTexture = unit;
+ config.texture[unit] = *texture;
#ifndef GL_ES_VERSION_2_0
- MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
+ MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, images.size()));
#endif
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.data.get()));
- img.data.reset();
+ GLint level = 0;
+ for (auto& img : images) {
+ assert(img.data.get());
+ MBGL_CHECK_ERROR(glTexImage2D(
+ GL_TEXTURE_2D, level++, GL_RGBA, static_cast<GLsizei>(img.width),
+ static_cast<GLsizei>(img.height), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.data.get()));
+ }
+ images.clear();
+ images.shrink_to_fit();
}
}
+
+} // namespace mbgl
diff --git a/src/mbgl/util/raster.hpp b/src/mbgl/util/raster.hpp
index b539032d81..34e7c67678 100644
--- a/src/mbgl/util/raster.hpp
+++ b/src/mbgl/util/raster.hpp
@@ -1,58 +1,51 @@
#pragma once
-#include <mbgl/gl/gl.hpp>
-#include <mbgl/gl/texture_pool.hpp>
+#include <mbgl/gl/object_store.hpp>
#include <mbgl/util/image.hpp>
-#include <mbgl/util/ptr.hpp>
-#include <mbgl/util/chrono.hpp>
#include <mbgl/util/optional.hpp>
-#include <mutex>
+#include <atomic>
namespace mbgl {
-class Raster : public std::enable_shared_from_this<Raster> {
+namespace gl {
+class Config;
+} // namespace gl
+class Raster {
public:
- Raster(gl::TexturePool&);
+ enum class MipMap : bool { No = false, Yes = true };
+ enum class Scaling : bool { Nearest = false, Linear = true };
// load image data
- void load(PremultipliedImage);
+ void load(PremultipliedImage, uint32_t mipmapLevel = 0);
// bind current texture
- void bind(bool linear, gl::ObjectStore&);
+ void bind(gl::ObjectStore&,
+ gl::Config&,
+ uint32_t unit,
+ Scaling = Scaling::Nearest,
+ MipMap = MipMap::No);
// uploads the texture if it hasn't been uploaded yet.
- void upload(gl::ObjectStore&);
+ void upload(gl::ObjectStore&, gl::Config&, uint32_t unit);
// loaded status
bool isLoaded() const;
-public:
- // loaded image dimensions
- GLsizei width = 0;
- GLsizei height = 0;
-
- // GL buffer object handle.
- mbgl::optional<gl::PooledTexture> texture;
-
- // texture opacity
- double opacity = 0;
-
private:
- mutable std::mutex mtx;
-
// raw pixels have been loaded
- bool loaded = false;
-
- // shared texture pool
- gl::TexturePool& texturePool;
+ std::atomic<bool> loaded { false };
- // min/mag filter
- GLint filter = 0;
+ // filters
+ Scaling filter = Scaling::Nearest;
+ MipMap mipmap = MipMap::No;
// the raw pixels
- PremultipliedImage img;
+ std::vector<PremultipliedImage> images;
+
+ // GL buffer object handle.
+ mbgl::optional<gl::UniqueTexture> texture;
};
} // namespace mbgl
diff --git a/src/mbgl/util/rect.hpp b/src/mbgl/util/rect.hpp
index 2b877db40d..f5937f5c94 100644
--- a/src/mbgl/util/rect.hpp
+++ b/src/mbgl/util/rect.hpp
@@ -4,21 +4,21 @@ namespace mbgl {
template <typename T>
struct Rect {
- inline Rect() = default;
- inline Rect(T x_, T y_, T w_, T h_) : x(x_), y(y_), w(w_), h(h_) {}
+ Rect() = default;
+ Rect(T x_, T y_, T w_, T h_) : x(x_), y(y_), w(w_), h(h_) {}
T x = 0, y = 0;
T w = 0, h = 0;
template <typename Number>
- inline Rect operator *(Number value) const {
+ Rect operator *(Number value) const {
return Rect(x * value, y * value, w * value, h * value);
}
template <typename R>
- inline bool operator==(const R& r) const {
+ bool operator==(const R& r) const {
return x == r.x && y == r.y && w == r.w && h == r.h;
}
- inline bool hasArea() const { return w != 0 && h != 0; }
+ bool hasArea() const { return w != 0 && h != 0; }
};
} // namespace mbgl
diff --git a/src/mbgl/util/stopwatch.cpp b/src/mbgl/util/stopwatch.cpp
index bbc6bfba0c..d588b79254 100644
--- a/src/mbgl/util/stopwatch.cpp
+++ b/src/mbgl/util/stopwatch.cpp
@@ -7,7 +7,8 @@
#include <iostream>
#include <atomic>
-using namespace mbgl::util;
+namespace mbgl {
+namespace util {
stopwatch::stopwatch(Event event_)
: event(event_), start(Clock::now()) {}
@@ -15,11 +16,11 @@ stopwatch::stopwatch(Event event_)
stopwatch::stopwatch(EventSeverity severity_, Event event_)
: severity(severity_), event(event_), start(Clock::now()) {}
-stopwatch::stopwatch(const std::string &name_, Event event_)
- : name(name_), event(event_), start(Clock::now()) {}
+stopwatch::stopwatch(std::string name_, Event event_)
+ : name(std::move(name_)), event(event_), start(Clock::now()) {}
-stopwatch::stopwatch(const std::string &name_, EventSeverity severity_, Event event_)
- : name(name_), severity(severity_), event(event_), start(Clock::now()) {}
+stopwatch::stopwatch(std::string name_, EventSeverity severity_, Event event_)
+ : name(std::move(name_)), severity(severity_), event(event_), start(Clock::now()) {}
void stopwatch::report(const std::string &name_) {
Duration duration = Clock::now() - start;
@@ -33,4 +34,8 @@ stopwatch::~stopwatch() {
report(name);
}
}
+
+} // namespace util
+} // namespace mbgl
+
#endif
diff --git a/src/mbgl/util/stopwatch.hpp b/src/mbgl/util/stopwatch.hpp
index 23086b1747..74cc2c412d 100644
--- a/src/mbgl/util/stopwatch.hpp
+++ b/src/mbgl/util/stopwatch.hpp
@@ -13,8 +13,8 @@ class stopwatch {
public:
stopwatch(Event event = Event::General);
stopwatch(EventSeverity severity, Event event = Event::General);
- stopwatch(const std::string &name, Event event = Event::General);
- stopwatch(const std::string &name, EventSeverity severity, Event event = Event::General);
+ stopwatch(std::string name, Event event = Event::General);
+ stopwatch(std::string name, EventSeverity severity, Event event = Event::General);
void report(const std::string &name);
~stopwatch();
@@ -26,12 +26,12 @@ private:
};
#else
class stopwatch {
- inline stopwatch(Event event = Event::General);
- inline stopwatch(EventSeverity severity, Event event = Event::General);
- inline stopwatch(const std::string &name, Event event = Event::General);
- inline stopwatch(const std::string &name, EventSeverity severity, Event event = Event::General);
- inline void report(const std::string &name) {}
- inline ~stopwatch() {}
+ stopwatch(Event event = Event::General);
+ stopwatch(EventSeverity severity, Event event = Event::General);
+ stopwatch(const std::string &name, Event event = Event::General);
+ stopwatch(const std::string &name, EventSeverity severity, Event event = Event::General);
+ void report(const std::string &name) {}
+ ~stopwatch() {}
};
#endif
} // namespace util
diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp
index c5f9d7338a..cd90e08049 100644
--- a/src/mbgl/util/thread.hpp
+++ b/src/mbgl/util/thread.hpp
@@ -6,7 +6,6 @@
#include <utility>
#include <functional>
-#include <mbgl/util/atomic.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/thread_context.hpp>
#include <mbgl/platform/platform.hpp>
@@ -95,13 +94,7 @@ Thread<Object>::Thread(const ThreadContext& context, Args&&... args) {
std::tuple<Args...> params = std::forward_as_tuple(::std::forward<Args>(args)...);
thread = std::thread([&] {
-#if defined(__APPLE__)
- pthread_setname_np(context.name.c_str());
-#elif defined(__GLIBC__) && defined(__GLIBC_PREREQ)
-#if __GLIBC_PREREQ(2, 12)
- pthread_setname_np(pthread_self(), context.name.c_str());
-#endif
-#endif
+ platform::setCurrentThreadName(context.name);
if (context.priority == ThreadPriority::Low) {
platform::makeThreadLowPriority();
diff --git a/src/mbgl/util/thread_context.cpp b/src/mbgl/util/thread_context.cpp
index f728e32df7..fe64c2a686 100644
--- a/src/mbgl/util/thread_context.cpp
+++ b/src/mbgl/util/thread_context.cpp
@@ -1,10 +1,11 @@
#include <mbgl/util/thread_context.hpp>
+#include <utility>
namespace mbgl {
namespace util {
-ThreadContext::ThreadContext(const std::string& name_, ThreadPriority priority_)
- : name(name_),
+ThreadContext::ThreadContext(std::string name_, ThreadPriority priority_)
+ : name(std::move(name_)),
priority(priority_) {
}
diff --git a/src/mbgl/util/thread_context.hpp b/src/mbgl/util/thread_context.hpp
index 60b32b3b2a..a51dede404 100644
--- a/src/mbgl/util/thread_context.hpp
+++ b/src/mbgl/util/thread_context.hpp
@@ -12,7 +12,7 @@ enum class ThreadPriority : bool {
struct ThreadContext {
public:
- ThreadContext(const std::string& name, ThreadPriority priority = ThreadPriority::Regular);
+ ThreadContext(std::string name, ThreadPriority priority = ThreadPriority::Regular);
std::string name;
ThreadPriority priority;
diff --git a/src/mbgl/util/thread_local.hpp b/src/mbgl/util/thread_local.hpp
index c3364c5d42..67e3842ec6 100644
--- a/src/mbgl/util/thread_local.hpp
+++ b/src/mbgl/util/thread_local.hpp
@@ -12,12 +12,12 @@ namespace util {
template <class T>
class ThreadLocal : public noncopyable {
public:
- inline ThreadLocal(T* val) {
+ ThreadLocal(T* val) {
ThreadLocal();
set(val);
}
- inline ThreadLocal() {
+ ThreadLocal() {
int ret = pthread_key_create(&key, [](void *ptr) {
delete reinterpret_cast<T *>(ptr);
});
@@ -27,13 +27,13 @@ public:
}
}
- inline ~ThreadLocal() {
+ ~ThreadLocal() {
if (pthread_key_delete(key)) {
throw std::runtime_error("Failed to delete local storage key.");
}
}
- inline T* get() {
+ T* get() {
T* ret = reinterpret_cast<T*>(pthread_getspecific(key));
if (!ret) {
return nullptr;
@@ -42,7 +42,7 @@ public:
return ret;
}
- inline void set(T* ptr) {
+ void set(T* ptr) {
if (pthread_setspecific(key, ptr)) {
throw std::runtime_error("Failed to set local storage.");
}
diff --git a/src/mbgl/util/tile_coordinate.cpp b/src/mbgl/util/tile_coordinate.cpp
deleted file mode 100644
index d9a0ea352c..0000000000
--- a/src/mbgl/util/tile_coordinate.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include <mbgl/util/tile_coordinate.hpp>
diff --git a/src/mbgl/util/tileset.hpp b/src/mbgl/util/tileset.hpp
deleted file mode 100644
index 972fc51f8c..0000000000
--- a/src/mbgl/util/tileset.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/geo.hpp>
-
-#include <array>
-#include <vector>
-#include <string>
-#include <cstdint>
-
-namespace mbgl {
-
-class Tileset {
-public:
- std::vector<std::string> tiles;
- uint8_t minZoom = 0;
- uint8_t maxZoom = 22;
- std::string attribution;
- LatLng center;
- double zoom = 0;
- LatLngBounds bounds = LatLngBounds::world();
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/util/utf.hpp b/src/mbgl/util/utf.hpp
index 02aabfc4c2..560ca3ba7f 100644
--- a/src/mbgl/util/utf.hpp
+++ b/src/mbgl/util/utf.hpp
@@ -17,5 +17,5 @@ class utf8_to_utf32 {
}
};
-} // namespace mbgl
} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/version_info.cpp b/src/mbgl/util/version_info.cpp
index c545ddcc40..f0fb139bca 100644
--- a/src/mbgl/util/version_info.cpp
+++ b/src/mbgl/util/version_info.cpp
@@ -11,4 +11,4 @@ const char *string = MBGL_VERSION_STRING;
const unsigned int number = MBGL_VERSION;
} // namespace version
-} // namespace mbgl \ No newline at end of file
+} // namespace mbgl
diff --git a/src/mbgl/util/work_task.cpp b/src/mbgl/util/work_task.cpp
deleted file mode 100644
index e69de29bb2..0000000000
--- a/src/mbgl/util/work_task.cpp
+++ /dev/null
diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp
index 2bc545c704..50ecb93c02 100644
--- a/src/mbgl/util/worker.cpp
+++ b/src/mbgl/util/worker.cpp
@@ -3,7 +3,7 @@
#include <mbgl/util/work_request.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/renderer/raster_bucket.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/style/layer.hpp>
#include <mbgl/text/collision_tile.hpp>
@@ -31,11 +31,11 @@ public:
void parseGeometryTile(TileWorker* worker,
std::vector<std::unique_ptr<style::Layer>> layers,
- std::unique_ptr<GeometryTile> tile,
+ std::unique_ptr<GeometryTileData> tileData,
PlacementConfig config,
std::function<void(TileParseResult)> callback) {
try {
- callback(worker->parseAllLayers(std::move(layers), std::move(tile), config));
+ callback(worker->parseAllLayers(std::move(layers), std::move(tileData), config));
} catch (...) {
callback(std::current_exception());
}
@@ -80,12 +80,12 @@ Worker::parseRasterTile(std::unique_ptr<RasterBucket> bucket,
std::unique_ptr<AsyncRequest>
Worker::parseGeometryTile(TileWorker& worker,
std::vector<std::unique_ptr<style::Layer>> layers,
- std::unique_ptr<GeometryTile> tile,
+ std::unique_ptr<GeometryTileData> tileData,
PlacementConfig config,
std::function<void(TileParseResult)> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::parseGeometryTile, callback, &worker,
- std::move(layers), std::move(tile), config);
+ std::move(layers), std::move(tileData), config);
}
std::unique_ptr<AsyncRequest>
diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp
index 678dfaedb3..5ab86d1b9e 100644
--- a/src/mbgl/util/worker.hpp
+++ b/src/mbgl/util/worker.hpp
@@ -41,7 +41,7 @@ public:
Request parseGeometryTile(TileWorker&,
std::vector<std::unique_ptr<style::Layer>>,
- std::unique_ptr<GeometryTile>,
+ std::unique_ptr<GeometryTileData>,
PlacementConfig,
std::function<void(TileParseResult)> callback);
diff --git a/test/algorithm/mock.hpp b/test/algorithm/mock.hpp
index 28c78854be..efa76f238e 100644
--- a/test/algorithm/mock.hpp
+++ b/test/algorithm/mock.hpp
@@ -7,16 +7,12 @@
#include <map>
#include <mbgl/tile/tile_id.hpp>
-
-struct MockSourceInfo {
- uint8_t maxZoom = 16;
- uint8_t minZoom = 0;
-};
+#include <mbgl/util/range.hpp>
struct MockTileData;
struct MockSource {
- MockSourceInfo info;
+ mbgl::Range<uint8_t> zoomRange { 0, 16 };
std::map<mbgl::OverscaledTileID, std::unique_ptr<MockTileData>> dataTiles;
std::set<mbgl::UnwrappedTileID> idealTiles;
@@ -29,11 +25,17 @@ struct MockBucket {};
struct MockTileData {
MockTileData(const mbgl::OverscaledTileID& tileID_) : tileID(tileID_) {}
- bool isRenderable() {
+
+ bool hasTriedOptional() const {
+ return triedOptional;
+ }
+
+ bool isRenderable() const {
return renderable;
}
bool renderable = false;
+ bool triedOptional = false;
const mbgl::OverscaledTileID tileID;
};
diff --git a/test/algorithm/update_renderables.cpp b/test/algorithm/update_renderables.cpp
index 67c7d79a0a..dcc43a83f3 100644
--- a/test/algorithm/update_renderables.cpp
+++ b/test/algorithm/update_renderables.cpp
@@ -8,9 +8,14 @@
using namespace mbgl;
+enum LookupResult : bool {
+ NotFound = false,
+ Found = true,
+};
+
struct GetTileDataAction {
const OverscaledTileID tileID;
- const bool found;
+ const LookupResult found;
bool operator==(const GetTileDataAction& rhs) const {
return tileID == rhs.tileID && found == rhs.found;
@@ -20,7 +25,8 @@ struct GetTileDataAction {
std::ostream& operator<<(std::ostream& os, const GetTileDataAction& action) {
return os << "GetTileDataAction{ { " << int(action.tileID.overscaledZ) << ", { "
<< int(action.tileID.canonical.z) << ", " << action.tileID.canonical.x << ", "
- << action.tileID.canonical.y << " } }, " << (action.found ? "true" : "false") << " }";
+ << action.tileID.canonical.y << " } }, "
+ << (action.found == Found ? "Found" : "NotFound") << " }";
}
struct CreateTileDataAction {
@@ -37,18 +43,25 @@ std::ostream& operator<<(std::ostream& os, const CreateTileDataAction& action) {
<< action.tileID.canonical.y << " } } }";
}
+enum Necessity : bool {
+ Optional = false,
+ Required = true,
+};
+
struct RetainTileDataAction {
const OverscaledTileID tileID;
+ const Necessity necessity;
bool operator==(const RetainTileDataAction& rhs) const {
- return tileID == rhs.tileID;
+ return tileID == rhs.tileID && necessity == rhs.necessity;
}
};
std::ostream& operator<<(std::ostream& os, const RetainTileDataAction& action) {
return os << "RetainTileDataAction{ { " << int(action.tileID.overscaledZ) << ", { "
<< int(action.tileID.canonical.z) << ", " << action.tileID.canonical.x << ", "
- << action.tileID.canonical.y << " } } }";
+ << action.tileID.canonical.y << " } }, "
+ << (action.necessity == Required ? "Required" : "Optional") << " }";
}
struct RenderTileAction {
@@ -79,7 +92,7 @@ template <typename T>
auto getTileDataFn(ActionLog& log, const T& dataTiles) {
return [&](const auto& id) {
auto it = dataTiles.find(id);
- log.emplace_back(GetTileDataAction{ id, it != dataTiles.end() });
+ log.emplace_back(GetTileDataAction{ id, it != dataTiles.end() ? Found : NotFound });
return (it != dataTiles.end()) ? it->second.get() : nullptr;
};
}
@@ -93,7 +106,9 @@ auto createTileDataFn(ActionLog& log, T& dataTiles) {
}
auto retainTileDataFn(ActionLog& log) {
- return [&](auto& tileData) { log.emplace_back(RetainTileDataAction{ tileData.tileID }); };
+ return [&](auto& tileData, bool required) {
+ log.emplace_back(RetainTileDataAction{ tileData.tileID, required ? Required : Optional });
+ };
}
auto renderTileFn(ActionLog& log) {
@@ -117,22 +132,22 @@ TEST(UpdateRenderables, SingleTile) {
tile_1_1_1_1->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 1, 1 } }, true }, // found ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } } }, //
- RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile
+ GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile
+ RetainTileDataAction{ { 1, { 1, 1, 1 } }, Required }, //
+ RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile
}),
log);
// Check a repeated render with the same data.
log.clear();
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 1, 1 } }, true }, // found ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } } }, //
- RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile
+ GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile
+ RetainTileDataAction{ { 1, { 1, 1, 1 } }, Required }, //
+ RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile
}),
log);
@@ -140,20 +155,42 @@ TEST(UpdateRenderables, SingleTile) {
log.clear();
source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 1 });
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, false }, // missing ideal tile
- CreateTileDataAction{ { 1, { 1, 0, 1 } } }, // create ideal tile
- RetainTileDataAction{ { 1, { 1, 0, 1 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 2 } }, false }, // four child tiles
- GetTileDataAction{ { 2, { 2, 0, 3 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 2 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 3 } }, false }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, false }, // parent tile
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 1, { 1, 0, 1 } } }, // create ideal tile
+ RetainTileDataAction{ { 1, { 1, 0, 1 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // four child tiles
+ GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile
+
+ GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile
+ RetainTileDataAction{ { 1, { 1, 1, 1 } }, Required }, //
+ RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile
+ }),
+ log);
- GetTileDataAction{ { 1, { 1, 1, 1 } }, true }, // found ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } } }, //
- RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile
+ // Mark the created tile as having the optional request tried.
+ log.clear();
+ source.dataTiles[{ 1, { 1, 0, 1 } }]->triedOptional = true;
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 1);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // missing ideal tile
+ RetainTileDataAction{ { 1, { 1, 0, 1 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // four child tiles
+ GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile
+ CreateTileDataAction{ { 0, { 0, 0, 0 } } }, // load parent tile
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+
+ GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile
+ RetainTileDataAction{ { 1, { 1, 1, 1 } }, Required }, //
+ RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile
}),
log);
@@ -162,15 +199,15 @@ TEST(UpdateRenderables, SingleTile) {
auto tile_1_1_0_1 = source.createTileData(OverscaledTileID{ 1, 0, 1 });
tile_1_1_0_1->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, true }, // newly added tile
- RetainTileDataAction{ { 1, { 1, 0, 1 } } }, //
- RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // newly added tile
+ RetainTileDataAction{ { 1, { 1, 0, 1 } }, Required }, //
+ RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile
- GetTileDataAction{ { 1, { 1, 1, 1 } }, true }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } } }, //
- RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile
+ GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, { 1, 1, 1 } }, Required }, //
+ RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile
}),
log);
@@ -181,23 +218,25 @@ TEST(UpdateRenderables, SingleTile) {
auto tile_1_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, 0 });
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, // found tile, not ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, // four child tiles
- GetTileDataAction{ { 2, { 2, 0, 1 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 0 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 1 } }, false }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, false }, // parent tile
-
- GetTileDataAction{ { 1, { 1, 0, 1 } }, true }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 0, 1 } } }, //
- RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile
-
- GetTileDataAction{ { 1, { 1, 1, 1 } }, true }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } } }, //
- RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // found tile, not ready
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // four child tiles
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ // optional parent tile was already created before, but is not renderable
+
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, { 1, 0, 1 } }, Required }, //
+ RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile
+
+ GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, { 1, 1, 1 } }, Required }, //
+ RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile
}),
log);
@@ -205,19 +244,19 @@ TEST(UpdateRenderables, SingleTile) {
log.clear();
tile_1_1_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, // found tile, now ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // found tile, now ready
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, //
- GetTileDataAction{ { 1, { 1, 0, 1 } }, true }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 0, 1 } } }, //
- RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, //
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, { 1, 0, 1 } }, Required }, //
+ RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, //
- GetTileDataAction{ { 1, { 1, 1, 1 } }, true }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } } }, //
- RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, //
+ GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, { 1, 1, 1 } }, Required }, //
+ RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, //
}),
log);
}
@@ -238,32 +277,32 @@ TEST(UpdateRenderables, UseParentTile) {
auto tile_0_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, 0 });
tile_0_0_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
- EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, false }, // missing ideal tile
- CreateTileDataAction{ { 1, { 1, 0, 1 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 1 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 2 } }, false }, // child tile
- GetTileDataAction{ { 2, { 2, 0, 3 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 2 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 3 } }, false }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, // parent found!
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // render parent
- GetTileDataAction{ { 1, { 1, 1, 0 } }, false }, // missing ideal tile
- CreateTileDataAction{ { 1, { 1, 1, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 1, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 2, 0 } }, false }, // child tile
- GetTileDataAction{ { 2, { 2, 2, 1 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 3, 0 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 3, 1 } }, false }, // ...
- GetTileDataAction{ { 1, { 1, 1, 1 } }, false }, // missing tile
- CreateTileDataAction{ { 1, { 1, 1, 1 } } }, //
- RetainTileDataAction{ { 1, { 1, 1, 1 } } }, //
- GetTileDataAction{ { 2, { 2, 2, 2 } }, false }, // child tile
- GetTileDataAction{ { 2, { 2, 2, 3 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 3, 2 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 3, 3 } }, false }, // ...
+ source.idealTiles, source.zoomRange, 1);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 1, { 1, 0, 1 } } }, //
+ RetainTileDataAction{ { 1, { 1, 0, 1 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent found!
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // render parent
+ GetTileDataAction{ { 1, { 1, 1, 0 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 1, { 1, 1, 0 } } }, //
+ RetainTileDataAction{ { 1, { 1, 1, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, { 2, 2, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 3, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 3, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 1, 1 } }, NotFound }, // missing tile
+ CreateTileDataAction{ { 1, { 1, 1, 1 } } }, //
+ RetainTileDataAction{ { 1, { 1, 1, 1 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 2, 2 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, { 2, 2, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 3, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 3, 3 } }, NotFound }, // ...
}),
log);
}
@@ -281,17 +320,36 @@ TEST(UpdateRenderables, DontUseWrongParentTile) {
auto tile_1_1_1_0 = source.createTileData(OverscaledTileID{ 1, 1, 0 });
tile_1_1_1_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 2);
+ source.idealTiles, source.zoomRange, 2);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // parent tile, missing
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing
+ }),
+ log);
+
+ // Now mark the created tile as having the optional request tried.
+ log.clear();
+ source.dataTiles[{ 2, { 2, 0, 0 } }]->triedOptional = true;
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, // missing ideal tile
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, false }, // child tile
- GetTileDataAction{ { 3, { 3, 0, 1 } }, false }, // ...
- GetTileDataAction{ { 3, { 3, 1, 0 } }, false }, // ...
- GetTileDataAction{ { 3, { 3, 1, 1 } }, false }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, false }, // parent tile, missing
- GetTileDataAction{ { 0, { 0, 0, 0 } }, false }, // parent tile, missing
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // non-ready ideal tile
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // parent tile, missing
+ CreateTileDataAction{ { 1, { 1, 0, 0 } } }, // find optional parent
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing
}),
log);
@@ -299,28 +357,29 @@ TEST(UpdateRenderables, DontUseWrongParentTile) {
log.clear();
source.idealTiles.emplace(UnwrappedTileID{ 2, 2, 0 });
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 2);
+ source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, // non-ready ideal tile
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // non-ready ideal tile
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Required }, //
// this tile was added by the previous invocation of updateRenderables
- GetTileDataAction{ { 3, { 3, 0, 0 } }, false }, // child tile
- GetTileDataAction{ { 3, { 3, 0, 1 } }, false }, // ...
- GetTileDataAction{ { 3, { 3, 1, 0 } }, false }, // ...
- GetTileDataAction{ { 3, { 3, 1, 1 } }, false }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, false }, // missing parent tile
- GetTileDataAction{ { 0, { 0, 0, 0 } }, false }, // missing parent tile
-
- GetTileDataAction{ { 2, { 2, 2, 0 } }, false }, // missing ideal tile
- CreateTileDataAction{ { 2, { 2, 2, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 2, 0 } } }, //
- GetTileDataAction{ { 3, { 3, 4, 0 } }, false }, // child tile
- GetTileDataAction{ { 3, { 3, 4, 1 } }, false }, // ...
- GetTileDataAction{ { 3, { 3, 5, 0 } }, false }, // ...
- GetTileDataAction{ { 3, { 3, 5, 1 } }, false }, // ...
- GetTileDataAction{ { 1, { 1, 1, 0 } }, true }, // found parent tile
- RetainTileDataAction{ { 1, { 1, 1, 0 } } }, //
- RenderTileAction{ { 1, 1, 0 }, *tile_1_1_1_0 }, // render parent tile
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // parent tile not ready
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // missing parent tile
+
+ GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 2, { 2, 2, 0 } } }, //
+ RetainTileDataAction{ { 2, { 2, 2, 0 } }, Required }, //
+ GetTileDataAction{ { 3, { 3, 4, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 3, { 3, 4, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 5, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 5, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 1, 0 } }, Found }, // found parent tile
+ RetainTileDataAction{ { 1, { 1, 1, 0 } }, Optional }, //
+ RenderTileAction{ { 1, 1, 0 }, *tile_1_1_1_0 }, // render parent tile
}),
log);
}
@@ -343,17 +402,17 @@ TEST(UpdateRenderables, UseParentTileWhenChildNotReady) {
// Make sure that it renders the parent tile.
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, true }, // found, but not ready
- RetainTileDataAction{ { 1, { 1, 0, 1 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 2 } }, false }, // child tile
- GetTileDataAction{ { 2, { 2, 0, 3 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 2 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 3 } }, false }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, // parent tile, ready
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // render parent tile
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // found, but not ready
+ RetainTileDataAction{ { 1, { 1, 0, 1 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, ready
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // render parent tile
}),
log);
@@ -361,11 +420,11 @@ TEST(UpdateRenderables, UseParentTileWhenChildNotReady) {
log.clear();
tile_1_1_0_1->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, true }, // found and ready
- RetainTileDataAction{ { 1, { 1, 0, 1 } } }, //
- RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // found and ready
+ RetainTileDataAction{ { 1, { 1, 0, 1 } }, Required }, //
+ RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile
}),
log);
}
@@ -388,22 +447,22 @@ TEST(UpdateRenderables, UseOverlappingParentTile) {
tile_1_1_0_1->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, false }, // ideal tile not found
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, // child tile
- GetTileDataAction{ { 2, { 2, 0, 1 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 0 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 1 } }, false }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, // parent tile found
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
-
- GetTileDataAction{ { 1, { 1, 0, 1 } }, true }, // ideal tile found
- RetainTileDataAction{ { 1, { 1, 0, 1 } } }, //
- RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile not found
+ CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile found
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
+
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // ideal tile found
+ RetainTileDataAction{ { 1, { 1, 0, 1 } }, Required }, //
+ RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, //
}),
log);
}
@@ -424,19 +483,19 @@ TEST(UpdateRenderables, UseChildTiles) {
tile_1_1_1_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 0);
+ source.idealTiles, source.zoomRange, 0);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 0, { 0, 0, 0 } }, false }, // ideal tile, missing
- CreateTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, // child tile found
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, // render child tile
- GetTileDataAction{ { 1, { 1, 0, 1 } }, false }, // child tile not found
- GetTileDataAction{ { 1, { 1, 1, 0 } }, true }, // child tile found
- RetainTileDataAction{ { 1, { 1, 1, 0 } } }, //
- RenderTileAction{ { 1, 1, 0 }, *tile_1_1_1_0 }, // render child tile
- GetTileDataAction{ { 1, { 1, 1, 1 } }, false }, // child tile not found
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 0, { 0, 0, 0 } } }, //
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // child tile found
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, // render child tile
+ GetTileDataAction{ { 1, { 1, 0, 1 } }, NotFound }, // child tile not found
+ GetTileDataAction{ { 1, { 1, 1, 0 } }, Found }, // child tile found
+ RetainTileDataAction{ { 1, { 1, 1, 0 } }, Optional }, //
+ RenderTileAction{ { 1, 1, 0 }, *tile_1_1_1_0 }, // render child tile
+ GetTileDataAction{ { 1, { 1, 1, 1 } }, NotFound }, // child tile not found
// no parent tile of 0 to consider
}),
log);
@@ -458,20 +517,20 @@ TEST(UpdateRenderables, PreferChildTiles) {
tile_2_2_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, false }, // ideal tile, not found
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, // child tile, found
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, false }, // child tile, not found
- GetTileDataAction{ { 2, { 2, 1, 0 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 1, 1 } }, false }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, // parent tile, found
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile, not found
+ CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // child tile, not found
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, found
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
@@ -481,22 +540,22 @@ TEST(UpdateRenderables, PreferChildTiles) {
auto tile_2_2_0_1 = source.createTileData(OverscaledTileID{ 2, 0, 1 });
tile_2_2_0_1->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, // ideal tile, not ready
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready
// ideal tile was added in previous invocation, but is not yet ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, // child tile, found
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, true }, // ...
- RetainTileDataAction{ { 2, { 2, 0, 1 } } }, // ...
- RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, false }, // child tile, not found
- GetTileDataAction{ { 2, { 2, 1, 1 } }, false }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, // parent tile, found
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, Found }, // ...
+ RetainTileDataAction{ { 2, { 2, 0, 1 } }, Optional }, // ...
+ RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, //
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // child tile, not found
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, found
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
@@ -504,24 +563,24 @@ TEST(UpdateRenderables, PreferChildTiles) {
auto tile_2_2_1_0 = source.createTileData(OverscaledTileID{ 2, 1, 0 });
tile_2_2_1_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, // ideal tile, not ready
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready
// ideal tile was added in first invocation, but is not yet ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, // child tile, found
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, true }, // ...
- RetainTileDataAction{ { 2, { 2, 0, 1 } } }, //
- RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, true }, // ...
- RetainTileDataAction{ { 2, { 2, 1, 0 } } }, //
- RenderTileAction{ { 2, 1, 0 }, *tile_2_2_1_0 }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, false }, // child tile, not found
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, // parent tile, found
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, Found }, // ...
+ RetainTileDataAction{ { 2, { 2, 0, 1 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, //
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 2, { 2, 1, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 1, 0 }, *tile_2_2_1_0 }, //
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // child tile, not found
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, found
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
@@ -530,23 +589,23 @@ TEST(UpdateRenderables, PreferChildTiles) {
auto tile_2_2_1_1 = source.createTileData(OverscaledTileID{ 2, 1, 1 });
tile_2_2_1_1->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, // ideal tile, not ready
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready
// ideal tile was added in first invocation, but is not yet ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, // child tile, found
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, true }, // ...
- RetainTileDataAction{ { 2, { 2, 0, 1 } } }, //
- RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, true }, // ...
- RetainTileDataAction{ { 2, { 2, 1, 0 } } }, //
- RenderTileAction{ { 2, 1, 0 }, *tile_2_2_1_0 }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, true }, // ...
- RetainTileDataAction{ { 2, { 2, 1, 1 } } }, //
- RenderTileAction{ { 2, 1, 1 }, *tile_2_2_1_1 }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, Found }, // ...
+ RetainTileDataAction{ { 2, { 2, 0, 1 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, //
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 2, { 2, 1, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 1, 0 }, *tile_2_2_1_0 }, //
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, Found }, // ...
+ RetainTileDataAction{ { 2, { 2, 1, 1 } }, Optional }, //
+ RenderTileAction{ { 2, 1, 1 }, *tile_2_2_1_1 }, //
}),
log);
}
@@ -568,20 +627,20 @@ TEST(UpdateRenderables, UseParentAndChildTiles) {
// Check that it uses the child tile and the parent tile to cover the rest.
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, false }, // ideal tile, missing
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, false }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, false }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
@@ -589,17 +648,17 @@ TEST(UpdateRenderables, UseParentAndChildTiles) {
log.clear();
source.dataTiles.erase(OverscaledTileID{ 2, 0, 0 });
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
+ source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, // ideal tile, not ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, false }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, false }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
}
@@ -612,22 +671,22 @@ TEST(UpdateRenderables, DontUseTilesLowerThanMinzoom) {
auto retainTileData = retainTileDataFn(log);
auto renderTile = renderTileFn(log);
- source.info.minZoom = 2;
+ source.zoomRange.min = 2;
source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 });
auto tile_1_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, 0 });
tile_1_1_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 2);
+ source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, // ideal tile, missing
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, false }, //
- GetTileDataAction{ { 3, { 3, 0, 1 } }, false }, //
- GetTileDataAction{ { 3, { 3, 1, 0 } }, false }, //
- GetTileDataAction{ { 3, { 3, 1, 1 } }, false }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, //
// no requests for zoom 1 tiles
}),
log);
@@ -641,38 +700,56 @@ TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) {
auto retainTileData = retainTileDataFn(log);
auto renderTile = renderTileFn(log);
- source.info.maxZoom = 2;
+ source.zoomRange.max = 2;
source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 });
// Add a child tile (that should never occur in practice) and make sure it's not selected.
auto tile_3_3_0_0 = source.createTileData(OverscaledTileID{ 3, 0, 0 });
tile_3_3_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 2);
- EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, // ideal tile, missing
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- GetTileDataAction{ { 3, { 2, 0, 0 } }, false }, // overzoomed tile, not children!
- GetTileDataAction{ { 1, { 1, 0, 0 } }, false }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, false }, //
- }),
- log);
+ source.idealTiles, source.zoomRange, 2);
+ EXPECT_EQ(
+ ActionLog({
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children!
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, //
+ }),
+ log);
+
+ // Mark the created tile as having tried the optional request.
+ log.clear();
+ source.dataTiles[{ 2, { 2, 0, 0 } }]->triedOptional = true;
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 2);
+ EXPECT_EQ(
+ ActionLog({
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // ideal tile, missing
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children!
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, //
+ CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, //
+ }),
+ log);
// Only add a non-overzoomed ("parent") tile at first.
log.clear();
auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } });
tile_2_2_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 3);
+ source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, false }, // ideal tile, missing
- CreateTileDataAction{ { 3, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 3, { 2, 0, 0 } } }, //
- GetTileDataAction{ { 4, { 2, 0, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
+ GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 3, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 3, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
}),
log);
@@ -681,22 +758,22 @@ TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) {
auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } });
tile_3_2_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 3);
+ source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, true }, //
- RetainTileDataAction{ { 3, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, //
+ GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 3, { 2, 0, 0 } }, Required }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, //
}),
log);
// Check that it's switching back to the tile that has the matching overzoom value.
log.clear();
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 2);
+ source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Required }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
}),
log);
@@ -707,14 +784,14 @@ TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) {
// Use the overzoomed tile even though it doesn't match the zoom level.
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 2);
+ source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, //
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- GetTileDataAction{ { 3, { 2, 0, 0 } }, true }, // use overzoomed tile!
- RetainTileDataAction{ { 3, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
+ CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // use overzoomed tile!
+ RetainTileDataAction{ { 3, { 2, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, //
}),
log);
}
@@ -727,18 +804,18 @@ TEST(UpdateRenderables, AscendToNonOverzoomedTiles) {
auto retainTileData = retainTileDataFn(log);
auto renderTile = renderTileFn(log);
- source.info.maxZoom = 2;
+ source.zoomRange.max = 2;
source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 });
// Add a matching overzoomed tile and make sure it gets selected.
auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } });
tile_3_2_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 3);
+ source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, true }, //
- RetainTileDataAction{ { 3, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, //
+ GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 3, { 2, 0, 0 } }, Required }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, //
}),
log);
@@ -749,15 +826,15 @@ TEST(UpdateRenderables, AscendToNonOverzoomedTiles) {
auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } });
tile_2_2_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 3);
+ source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, false }, //
- CreateTileDataAction{ { 3, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 3, { 2, 0, 0 } } }, //
- GetTileDataAction{ { 4, { 2, 0, 0 } }, false }, // prefer using a child first
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
+ GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, //
+ CreateTileDataAction{ { 3, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 3, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, // prefer using a child first
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
}),
log);
@@ -768,15 +845,33 @@ TEST(UpdateRenderables, AscendToNonOverzoomedTiles) {
auto tile_1_1_0_0 = source.createTileData(OverscaledTileID{ 1, { 1, 0, 0 } });
tile_1_1_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 3);
+ source.idealTiles, source.zoomRange, 3);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 3, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, //
+ }),
+ log);
+
+ // Now, mark the created tile as found.
+ log.clear();
+ source.dataTiles[{ 3, { 2, 0, 0 } }]->triedOptional = true;
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, true }, // ideal tile found, but not ready
- RetainTileDataAction{ { 3, { 2, 0, 0 } } }, //
- GetTileDataAction{ { 4, { 2, 0, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, //
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, //
+ GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 3, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
+ CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, //
}),
log);
}
@@ -793,31 +888,31 @@ TEST(UpdateRenderables, DoNotAscendMultipleTimesIfNotFound) {
source.idealTiles.emplace(UnwrappedTileID{ 8, 1, 0 });
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 8);
- EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 8, { 8, 0, 0 } }, false }, // ideal tile
- CreateTileDataAction{ { 8, { 8, 0, 0 } } }, //
- RetainTileDataAction{ { 8, { 8, 0, 0 } } }, //
- GetTileDataAction{ { 9, { 9, 0, 0 } }, false }, // child tile
- GetTileDataAction{ { 9, { 9, 0, 1 } }, false }, // ...
- GetTileDataAction{ { 9, { 9, 1, 0 } }, false }, // ...
- GetTileDataAction{ { 9, { 9, 1, 1 } }, false }, // ...
- GetTileDataAction{ { 7, { 7, 0, 0 } }, false }, // ascent
- GetTileDataAction{ { 6, { 6, 0, 0 } }, false }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, false }, // ...
- GetTileDataAction{ { 4, { 4, 0, 0 } }, false }, // ...
- GetTileDataAction{ { 3, { 3, 0, 0 } }, false }, // ...
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, false }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, false }, // ...
-
- GetTileDataAction{ { 8, { 8, 1, 0 } }, false }, // ideal tile
- CreateTileDataAction{ { 8, { 8, 1, 0 } } }, //
- RetainTileDataAction{ { 8, { 8, 1, 0 } } }, //
- GetTileDataAction{ { 9, { 9, 2, 0 } }, false }, // child tile
- GetTileDataAction{ { 9, { 9, 2, 1 } }, false }, // ...
- GetTileDataAction{ { 9, { 9, 3, 0 } }, false }, // ...
- GetTileDataAction{ { 9, { 9, 3, 1 } }, false }, // ...
+ source.idealTiles, source.zoomRange, 8);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 8, { 8, 0, 0 } }, NotFound }, // ideal tile
+ CreateTileDataAction{ { 8, { 8, 0, 0 } } }, //
+ RetainTileDataAction{ { 8, { 8, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 9, { 9, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 9, { 9, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, { 9, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, { 9, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // ascent
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+
+ GetTileDataAction{ { 8, { 8, 1, 0 } }, NotFound }, // ideal tile
+ CreateTileDataAction{ { 8, { 8, 1, 0 } } }, //
+ RetainTileDataAction{ { 8, { 8, 1, 0 } }, Required }, //
+ GetTileDataAction{ { 9, { 9, 2, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 9, { 9, 2, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, { 9, 3, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, { 9, 3, 1 } }, NotFound }, // ...
// no second ascent to 0
}),
log);
@@ -828,27 +923,27 @@ TEST(UpdateRenderables, DoNotAscendMultipleTimesIfNotFound) {
tile_4_0_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 8);
- EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 8, { 8, 0, 0 } }, true }, // ideal tile found, but not ready
- RetainTileDataAction{ { 8, { 8, 0, 0 } } }, //
- GetTileDataAction{ { 9, { 9, 0, 0 } }, false }, // child tile
- GetTileDataAction{ { 9, { 9, 0, 1 } }, false }, // ...
- GetTileDataAction{ { 9, { 9, 1, 0 } }, false }, // ...
- GetTileDataAction{ { 9, { 9, 1, 1 } }, false }, // ...
- GetTileDataAction{ { 7, { 7, 0, 0 } }, false }, // ascent
- GetTileDataAction{ { 6, { 6, 0, 0 } }, false }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, false }, // ...
- GetTileDataAction{ { 4, { 4, 0, 0 } }, true }, // stops ascent
- RetainTileDataAction{ { 4, { 4, 0, 0 } } }, //
- RenderTileAction{ { 4, 0, 0 }, *tile_4_0_0_0 },
-
- GetTileDataAction{ { 8, { 8, 1, 0 } }, true }, // ideal tile found, but not ready
- RetainTileDataAction{ { 8, { 8, 1, 0 } } }, //
- GetTileDataAction{ { 9, { 9, 2, 0 } }, false }, // child tile
- GetTileDataAction{ { 9, { 9, 2, 1 } }, false }, // ...
- GetTileDataAction{ { 9, { 9, 3, 0 } }, false }, // ...
- GetTileDataAction{ { 9, { 9, 3, 1 } }, false }, // ...
+ source.idealTiles, source.zoomRange, 8);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 8, { 8, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 8, { 8, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 9, { 9, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 9, { 9, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, { 9, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, { 9, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // ascent
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // stops ascent
+ RetainTileDataAction{ { 4, { 4, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 4, 0, 0 }, *tile_4_0_0_0 }, //
+
+ GetTileDataAction{ { 8, { 8, 1, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 8, { 8, 1, 0 } }, Required }, //
+ GetTileDataAction{ { 9, { 9, 2, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 9, { 9, 2, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, { 9, 3, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, { 9, 3, 1 } }, NotFound }, // ...
// no second ascent to 0
}),
log);
@@ -868,17 +963,17 @@ TEST(UpdateRenderables, DontRetainUnusedNonIdealTiles) {
source.createTileData(OverscaledTileID{ 2, 0, 0 });
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 2);
+ source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, true }, // ideal tile, not ready
- RetainTileDataAction{ { 2, { 2, 0, 0 } } }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, false }, //
- GetTileDataAction{ { 3, { 3, 0, 1 } }, false }, //
- GetTileDataAction{ { 3, { 3, 1, 0 } }, false }, //
- GetTileDataAction{ { 3, { 3, 1, 1 } }, false }, //
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, // parent tile, not ready
- // don't retain the parent tile
- GetTileDataAction{ { 0, { 0, 0, 0 } }, false }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // parent tile, not ready
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, //
}),
log);
}
@@ -900,47 +995,239 @@ TEST(UpdateRenderables, WrappedTiles) {
tile_0_0_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
- source.idealTiles, source.info, 1);
- EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 1, 0 } }, false }, // ideal tile 1/-1/0
- CreateTileDataAction{ { 1, { 1, 1, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 1, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 2, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 2, 1 } }, false }, //
- GetTileDataAction{ { 2, { 2, 3, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 3, 1 } }, false }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, -1, 0 }, *tile_0_0_0_0 }, //
-
- GetTileDataAction{ { 1, { 1, 0, 0 } }, false }, // ideal tile 1/0/0
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, false }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, false }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
-
- GetTileDataAction{ { 1, { 1, 1, 0 } }, true }, // ideal tile 1/1/0
- RetainTileDataAction{ { 1, { 1, 1, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 2, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 2, 1 } }, false }, //
- GetTileDataAction{ { 2, { 2, 3, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 3, 1 } }, false }, //
+ source.idealTiles, source.zoomRange, 1);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 1, { 1, 1, 0 } }, NotFound }, // ideal tile 1/-1/0
+ CreateTileDataAction{ { 1, { 1, 1, 0 } } }, //
+ RetainTileDataAction{ { 1, { 1, 1, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 2, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 3, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 3, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, -1, 0 }, *tile_0_0_0_0 }, //
+
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile 1/0/0
+ CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
+
+ GetTileDataAction{ { 1, { 1, 1, 0 } }, Found }, // ideal tile 1/1/0
+ RetainTileDataAction{ { 1, { 1, 1, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 2, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 3, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 3, 1 } }, NotFound }, //
// do not ascent; 0/0/0 has been rendered already for 1/0/0
- GetTileDataAction{ { 1, { 1, 0, 0 } }, true }, // ideal tile 1/2/0
- RetainTileDataAction{ { 1, { 1, 0, 0 } } }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, false }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, false }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, false }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, true }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RenderTileAction{ { 0, 1, 0 }, *tile_0_0_0_0 }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile 1/2/0
+ RetainTileDataAction{ { 1, { 1, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 0, { 0, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 0, 1, 0 }, *tile_0_0_0_0 }, //
+ }),
+ log);
+}
+
+TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) {
+ ActionLog log;
+ MockSource source;
+ auto getTileData = getTileDataFn(log, source.dataTiles);
+ auto createTileData = createTileDataFn(log, source.dataTiles);
+ auto retainTileData = retainTileDataFn(log);
+ auto renderTile = renderTileFn(log);
+
+ source.idealTiles.emplace(UnwrappedTileID{ 6, 0, 0 });
+
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 6);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, NotFound }, // ideal tile, not found
+ CreateTileDataAction{ { 6, { 6, 0, 0 } } }, //
+ RetainTileDataAction{ { 6, { 6, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ }),
+ log);
+
+ // Repeat.
+ log.clear();
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 6);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, { 6, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ }),
+ log);
+
+ // Mark next level has having tried optional.
+ log.clear();
+ source.dataTiles[{ 6, { 6, 0, 0 } }]->triedOptional = true;
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 6);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, { 6, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent
+ CreateTileDataAction{ { 5, { 5, 0, 0 } } }, //
+ RetainTileDataAction{ { 5, { 5, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ }),
+ log);
+
+ // Repeat.
+ log.clear();
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 6);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, { 6, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, { 5, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ }),
+ log);
+
+ // Mark next level has having tried optional.
+ log.clear();
+ source.dataTiles[{ 5, { 5, 0, 0 } }]->triedOptional = true;
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 6);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, { 6, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, { 5, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
+ CreateTileDataAction{ { 4, { 4, 0, 0 } } }, //
+ RetainTileDataAction{ { 4, { 4, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ }),
+ log);
+
+ // Mark next level has having tried optional.
+ log.clear();
+ source.dataTiles[{ 4, { 4, 0, 0 } }]->triedOptional = true;
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 6);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, { 6, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, { 5, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 4, { 4, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
+ CreateTileDataAction{ { 3, { 3, 0, 0 } } }, //
+ RetainTileDataAction{ { 3, { 3, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ }),
+ log);
+
+ // Mark next level has having tried optional.
+ log.clear();
+ source.dataTiles[{ 3, { 3, 0, 0 } }]->triedOptional = true;
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 6);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, { 6, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, { 5, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 4, { 4, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 3, { 3, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
+ CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, { 2, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ }),
+ log);
+
+ // Mark as found
+ log.clear();
+ auto tile_3_3_0_0 = source.dataTiles[{ 3, { 3, 0, 0 } }].get();
+ tile_3_3_0_0->renderable = true;
+ algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
+ source.idealTiles, source.zoomRange, 6);
+ EXPECT_EQ(ActionLog({
+ GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, { 6, 0, 0 } }, Required }, //
+ GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, { 5, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 4, { 4, 0, 0 } }, Optional }, //
+ GetTileDataAction{ { 3, { 3, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 3, { 3, 0, 0 } }, Optional }, //
+ RenderTileAction{ { 3, 0, 0 }, *tile_3_3_0_0 }, //
}),
log);
}
diff --git a/test/api/annotations.cpp b/test/api/annotations.cpp
index 552a386f96..a1e67b29c4 100644
--- a/test/api/annotations.cpp
+++ b/test/api/annotations.cpp
@@ -8,6 +8,7 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/color.hpp>
using namespace mbgl;
@@ -41,6 +42,10 @@ TEST(Annotations, SymbolAnnotation) {
test.map.addAnnotationIcon("default_marker", namedMarker("default_marker.png"));
test.map.addAnnotation(SymbolAnnotation { Point<double>(0, 0), "default_marker" });
test.checkRendering("point_annotation");
+
+ // FIXME: https://github.com/mapbox/mapbox-gl-native/issues/5419
+ //test.map.setZoom(test.map.getMaxZoom());
+ //test.checkRendering("point_annotation");
}
TEST(Annotations, LineAnnotation) {
@@ -48,12 +53,15 @@ TEST(Annotations, LineAnnotation) {
LineString<double> line = {{ { 0, 0 }, { 45, 45 }, { 30, 0 } }};
LineAnnotation annotation { line };
- annotation.color = {{ 255, 0, 0, 1 }};
- annotation.width = 5;
+ annotation.color = { { 255, 0, 0, 1 } };
+ annotation.width = { 5 };
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
test.map.addAnnotation(annotation);
test.checkRendering("line_annotation");
+
+ test.map.setZoom(test.map.getMaxZoom());
+ test.checkRendering("line_annotation_max_zoom");
}
TEST(Annotations, FillAnnotation) {
@@ -61,11 +69,14 @@ TEST(Annotations, FillAnnotation) {
Polygon<double> polygon = {{ {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }};
FillAnnotation annotation { polygon };
- annotation.color = {{ 255, 0, 0, 1 }};
+ annotation.color = { { 255, 0, 0, 1 } };
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
test.map.addAnnotation(annotation);
test.checkRendering("fill_annotation");
+
+ test.map.setZoom(test.map.getMaxZoom());
+ test.checkRendering("fill_annotation_max_zoom");
}
TEST(Annotations, OverlappingFillAnnotation) {
@@ -114,29 +125,27 @@ TEST(Annotations, NonImmediateAdd) {
Polygon<double> polygon = {{ {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }};
FillAnnotation annotation { polygon };
- annotation.color = {{ 255, 0, 0, 1 }};
+ annotation.color = { { 255, 0, 0, 1 } };
test.map.addAnnotation(annotation);
test.checkRendering("non_immediate_add");
}
-TEST(Annotations, UpdateIcon) {
+TEST(Annotations, UpdateSymbolAnnotationGeometry) {
AnnotationTest test;
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
- test.map.addAnnotationIcon("flipped_marker", namedMarker("default_marker.png"));
- test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "flipped_marker" });
+ test.map.addAnnotationIcon("default_marker", namedMarker("default_marker.png"));
+ test.map.addAnnotationIcon("flipped_marker", namedMarker("flipped_marker.png"));
+ AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
test::render(test.map);
- test.map.removeAnnotationIcon("flipped_marker");
- test.map.addAnnotationIcon("flipped_marker", namedMarker("flipped_marker.png"));
- test.map.update(Update::Annotations);
-
- test.checkRendering("update_icon");
+ test.map.updateAnnotation(point, SymbolAnnotation { Point<double> { -10, 0 }, "default_marker" });
+ test.checkRendering("update_point");
}
-TEST(Annotations, UpdatePoint) {
+TEST(Annotations, UpdateSymbolAnnotationIcon) {
AnnotationTest test;
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
@@ -146,8 +155,8 @@ TEST(Annotations, UpdatePoint) {
test::render(test.map);
- test.map.updateAnnotation(point, SymbolAnnotation { Point<double> { -10, 0 }, "flipped_marker" });
- test.checkRendering("update_point");
+ test.map.updateAnnotation(point, SymbolAnnotation { Point<double> { 0, 0 }, "flipped_marker" });
+ test.checkRendering("update_icon");
}
TEST(Annotations, RemovePoint) {
@@ -168,8 +177,8 @@ TEST(Annotations, RemoveShape) {
LineString<double> line = {{ { 0, 0 }, { 45, 45 } }};
LineAnnotation annotation { line };
- annotation.color = {{ 255, 0, 0, 1 }};
- annotation.width = 5;
+ annotation.color = { { 255, 0, 0, 1 } };
+ annotation.width = { 5 };
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID shape = test.map.addAnnotation(annotation);
@@ -210,7 +219,7 @@ TEST(Annotations, QueryRenderedFeatures) {
test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
test::render(test.map);
-
+
auto features = test.map.queryRenderedFeatures(test.map.pixelForLatLng({ 0, 0 }));
- EXPECT_EQ(features.size(), 1);
+ EXPECT_EQ(features.size(), 1u);
}
diff --git a/test/api/api_misuse.cpp b/test/api/api_misuse.cpp
index 2400550a68..e9fe307718 100644
--- a/test/api/api_misuse.cpp
+++ b/test/api/api_misuse.cpp
@@ -13,7 +13,7 @@
using namespace mbgl;
TEST(API, RenderWithoutCallback) {
- FixtureLogObserver* log = new FixtureLogObserver();
+ auto log = new FixtureLogObserver();
Log::setObserver(std::unique_ptr<Log::Observer>(log));
util::RunLoop loop;
diff --git a/test/api/custom_layer.cpp b/test/api/custom_layer.cpp
index e0be341c4b..90c94aaea4 100644
--- a/test/api/custom_layer.cpp
+++ b/test/api/custom_layer.cpp
@@ -56,7 +56,7 @@ public:
MBGL_CHECK_ERROR(glUseProgram(program));
MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
- MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL));
+ MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
diff --git a/test/api/render_missing.cpp b/test/api/render_missing.cpp
index e0fb8e4c55..135c1ecae5 100644
--- a/test/api/render_missing.cpp
+++ b/test/api/render_missing.cpp
@@ -56,7 +56,7 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) {
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
- EXPECT_EQ(1, flo->count(FixtureLog::Message(
+ EXPECT_EQ(1u, flo->count(FixtureLog::Message(
EventSeverity::Error, Event::Style, -1,
std::string("Failed to load tile 0/0/0=>0 for source mapbox: " + message))));
auto unchecked = flo->unchecked();
diff --git a/test/api/repeated_render.cpp b/test/api/repeated_render.cpp
index 3a71ddb08d..cf71cb8416 100644
--- a/test/api/repeated_render.cpp
+++ b/test/api/repeated_render.cpp
@@ -42,8 +42,8 @@ TEST(API, RepeatedRender) {
loop.runOnce();
}
- ASSERT_EQ(256, result.width);
- ASSERT_EQ(512, result.height);
+ ASSERT_EQ(256u, result.width);
+ ASSERT_EQ(512u, result.height);
#if !TEST_READ_ONLY
util::write_file("test/fixtures/api/1.png", encodePNG(result));
#endif
@@ -60,8 +60,8 @@ TEST(API, RepeatedRender) {
loop.runOnce();
}
- ASSERT_EQ(256, result.width);
- ASSERT_EQ(512, result.height);
+ ASSERT_EQ(256u, result.width);
+ ASSERT_EQ(512u, result.height);
#if !TEST_READ_ONLY
util::write_file("test/fixtures/api/2.png", encodePNG(result));
#endif
diff --git a/test/api/set_style.cpp b/test/api/set_style.cpp
index fb5027c076..2399e4ea50 100644
--- a/test/api/set_style.cpp
+++ b/test/api/set_style.cpp
@@ -26,7 +26,7 @@ TEST(API, SetStyle) {
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
- EXPECT_EQ(1ul, flo->count({ EventSeverity::Error, Event::ParseStyle, -1,
+ EXPECT_EQ(1u, flo->count({ EventSeverity::Error, Event::ParseStyle, -1,
"Error parsing style JSON at 0: Invalid value." }));
auto unchecked = flo->unchecked();
EXPECT_TRUE(unchecked.empty()) << unchecked;
diff --git a/test/fixtures/annotations/fill_annotation_max_zoom/expected.png b/test/fixtures/annotations/fill_annotation_max_zoom/expected.png
new file mode 100644
index 0000000000..4e77e671cd
--- /dev/null
+++ b/test/fixtures/annotations/fill_annotation_max_zoom/expected.png
Binary files differ
diff --git a/test/fixtures/annotations/line_annotation_max_zoom/expected.png b/test/fixtures/annotations/line_annotation_max_zoom/expected.png
new file mode 100644
index 0000000000..8512d8c06a
--- /dev/null
+++ b/test/fixtures/annotations/line_annotation_max_zoom/expected.png
Binary files differ
diff --git a/test/fixtures/annotations/update_icon/expected.png b/test/fixtures/annotations/update_icon/expected.png
index 3b6ca22747..408d681ede 100644
--- a/test/fixtures/annotations/update_icon/expected.png
+++ b/test/fixtures/annotations/update_icon/expected.png
Binary files differ
diff --git a/test/fixtures/annotations/update_point/expected.png b/test/fixtures/annotations/update_point/expected.png
index 02cf77df8d..30aa39e380 100644
--- a/test/fixtures/annotations/update_point/expected.png
+++ b/test/fixtures/annotations/update_point/expected.png
Binary files differ
diff --git a/test/fixtures/resources/glyphs.pbf b/test/fixtures/resources/glyphs.pbf
index 0d160f7898..d2c2b633fa 100644
--- a/test/fixtures/resources/glyphs.pbf
+++ b/test/fixtures/resources/glyphs.pbf
Binary files differ
diff --git a/test/fixtures/resources/raster.png b/test/fixtures/resources/raster.png
deleted file mode 100644
index 78ad885baf..0000000000
--- a/test/fixtures/resources/raster.png
+++ /dev/null
Binary files differ
diff --git a/test/fixtures/resources/raster.tile b/test/fixtures/resources/raster.tile
new file mode 100644
index 0000000000..43cc9a0c77
--- /dev/null
+++ b/test/fixtures/resources/raster.tile
Binary files differ
diff --git a/test/fixtures/resources/source_raster.json b/test/fixtures/resources/source_raster.json
index b114988edb..2ef5ded027 100644
--- a/test/fixtures/resources/source_raster.json
+++ b/test/fixtures/resources/source_raster.json
@@ -1 +1 @@
-{"attribution":"<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">&copy; Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">&copy; OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a> <a href=\"https://www.digitalglobe.com/\" target=\"_blank\">&copy; DigitalGlobe</a>","autoscale":true,"bounds":[-180,-85,180,85],"center":[0,0,3],"description":"","id":"mapbox.satellite","maxzoom":19,"minzoom":0,"name":"Mapbox Satellite","private":true,"scheme":"xyz","tilejson":"2.0.0","tiles":["test/fixtures/resources/raster.png"]}
+{"attribution":"<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">&copy; Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">&copy; OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a> <a href=\"https://www.digitalglobe.com/\" target=\"_blank\">&copy; DigitalGlobe</a>","autoscale":true,"bounds":[-180,-85,180,85],"cacheControl":"max-age=43200,s-maxage=604800","center":[0,0,3],"created":1358310600000,"description":"","id":"mapbox.satellite","maxzoom":22,"minzoom":0,"modified":1446150592060,"name":"Mapbox Satellite","private":false,"scheme":"xyz","tilejson":"2.0.0","tiles":["https://a.tiles.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.png?access_token=pk.accesstoken","https://b.tiles.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.png?access_token=pk.accesstoken"],"webpage":"https://a.tiles.mapbox.com/v4/mapbox.satellite/page.html?access_token=pk.accesstoken"} \ No newline at end of file
diff --git a/test/fixtures/resources/source_vector.json b/test/fixtures/resources/source_vector.json
index db516f9f95..2cbbc5fd94 100644
--- a/test/fixtures/resources/source_vector.json
+++ b/test/fixtures/resources/source_vector.json
@@ -1 +1 @@
-{"attribution":"<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">&copy; Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">&copy; OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a>","bounds":[-180,-85.0511,180,85.0511],"center":[0,0,0],"format":"pbf","maxzoom":15,"minzoom":0,"name":"Mapbox Streets V6 + Vector Terrain V2","scheme":"xyz","tilejson":"2.0.0","tiles":["test/fixtures/resources/vector.pbf"],"vector_layers":[{"description":"Generalized landcover classification","fields":{"class":"One of: wood, scrub, grass, crop, snow"},"id":"landcover","maxzoom":22,"minzoom":0},{"description":"","fields":{"class":"One of: shadow, highlight","level":"Brightness %. One of: 94, 90, 89, 78, 67, 56"},"id":"hillshade","maxzoom":22,"minzoom":0},{"description":"Elevation contour polygons","fields":{"ele":"Integer. The elevation of the contour in meters.","index":"Indicator for every 2nd, 5th, or 10th contour. Coastlines are given -1. One of: 2, 5, 10, -1, null"},"id":"contour","maxzoom":22,"minzoom":0},{"description":"","fields":{"class":"One of: cemetery, hospital, industrial, park, parking, pitch, sand, school, wood","osm_id":"Unique OSM ID number","type":"OSM tag, more specific than class"},"id":"landuse"},{"description":"","fields":{"class":"One of: river, canal, stream, stream_intermittent, ditch, drain","osm_id":"Unique OSM ID number","type":"One of: river, canal, stream, ditch, drain"},"id":"waterway"},{"description":"","fields":{"osm_id":"Unique OSM ID number"},"id":"water"},{"description":"","fields":{"osm_id":"Unique OSM ID number","type":"One of: runway, taxiway, apron"},"id":"aeroway"},{"description":"","fields":{"class":"One of: fence, hedge, cliff, gate","osm_id":"Unique OSM ID number"},"id":"barrier_line"},{"description":"","fields":{"osm_id":"Unique OSM ID number"},"id":"building"},{"description":"","fields":{"class":"One of: wetland, wetland_noveg","osm_id":"Unique OSM ID number"},"id":"landuse_overlay"},{"description":"","fields":{"class":"One of: motorway, motorway_link, main, street, street_limited, service, driveway, path, major_rail, minor_rail, aerialway","layer":"Number used for ordering overlapping tunnels","oneway":"Number. Oneway roads are 1, all others are 0.","osm_id":"Unique OSM ID number","type":"The value of the tunnel's highway tag"},"id":"tunnel"},{"description":"","fields":{"class":"One of: motorway, motorway_link, main, street, street_limited, service, driveway, path, major_rail, minor_rail, aerialway","oneway":"Number. Oneway roads are 1, all others are 0.","osm_id":"Unique OSM ID number","type":"The value of the road's highway tag"},"id":"road"},{"description":"","fields":{"class":"One of: motorway, motorway_link, main, street, street_limited, service, driveway, path, major_rail, minor_rail, aerialway","layer":"Number used for ordering overlapping bridges","oneway":"Number. Oneway bridges are 1, all others are 0.","osm_id":"Unique OSM ID number","type":"The value of the bridge's highway tag"},"id":"bridge"},{"description":"","fields":{"admin_level":"The OSM administrative level of the boundary","disputed":"Number. Disputed boundaries are 1, all others are 0.","maritime":"Number. Maritime boundaries are 1, all others are 0."},"id":"admin"},{"description":"","fields":{"code":"ISO 3166-1 Alpha-2 code","name":"Local name of the country","name_de":"German name of the country","name_en":"English name of the country","name_es":"Spanish name of the country","name_fr":"French name of the country","name_ru":"Russian name of the country","name_zh":"Chinese name of the country","osm_id":"Unique OSM ID number","parent":"ISO 3166-1 Alpha-2 code of the administering/parent state, if any","scalerank":"Number, 1-6. Useful for styling text sizes.","type":"One of: country, territory, disputed territory, sar"},"id":"country_label"},{"description":"","fields":{"labelrank":"Number, 1-6. Useful for styling text sizes.","name":"Local or international name of the water body","name_de":"German name of the water body","name_en":"English name of the water body","name_es":"Spanish name of the water body","name_fr":"French name of the water body","name_ru":"Russian name of the water body","name_zh":"Chinese name of the water body","placement":"One of: point, line"},"id":"marine_label"},{"description":"","fields":{"abbr":"Abbreviated state name","area":"The area of the state in kilometers²","name":"Local name of the state","name_de":"German name of the state","name_en":"English name of the state","name_es":"Spanish name of the state","name_fr":"French name of the state","name_ru":"Russian name of the state","name_zh":"Chinese name of the state","osm_id":"Unique OSM ID number"},"id":"state_label"},{"description":"","fields":{"capital":"Admin level the city is a capital of, if any. One of: 2, 3, 4, null","ldir":"A hint for label placement at low zoom levels. One of: N, E, S, W, NE, SE, SW, NW, null","localrank":"Number. Priority relative to nearby places. Useful for limiting label density.","name":"Local name of the place","name_de":"German name of the place","name_en":"English name of the place","name_es":"Spanish name of the place","name_fr":"French name of the place","name_ru":"Russian name of the place","name_zh":"Chinese name of the place","osm_id":"Unique OSM ID number","scalerank":"Number, 0-9 or null. Useful for styling text & marker sizes.","type":"One of: city, town, village, hamlet, suburb, neighbourhood"},"id":"place_label"},{"description":"","fields":{"area":"The area of the water polygon in Mercator meters²","name":"Local name of the water body","name_de":"German name of the water body","name_en":"English name of the water body","name_es":"Spanish name of the water body","name_fr":"French name of the water body","name_ru":"Russian name of the water body","name_zh":"Chinese name of the water body","osm_id":"Unique OSM ID number"},"id":"water_label"},{"description":"","fields":{"address":"Street address of the POI","localrank":"Number. Priority relative to nearby POIs. Useful for limiting label density.","maki":"The name of the Maki icon that should be used for the POI","name":"Local name of the POI","name_de":"German name of the POI","name_en":"English name of the POI","name_es":"Spanish name of the POI","name_fr":"French name of the POI","name_ru":"Russian name of the POI","name_zh":"Chinese name of the POI","network":"For rail stations, the network(s) that the station serves. Useful for icon styling.","osm_id":"Unique OSM ID number","ref":"Short reference code, if any","scalerank":"Number. 1-4. Useful for styling icon sizes and minimum zoom levels.","type":"The original OSM tag value","website":"URL of the POI"},"id":"poi_label"},{"description":"","fields":{"class":"One of: motorway, motorway_link, main, street, street_limited, service, driveway, path","len":"Number. Length of the road segment in Mercator meters.","localrank":"Number. Used for shield points only. Priority relative to nearby shields. Useful for limiting shield density.","name":"Local name of the road","name_de":"German name of the road","name_en":"English name of the road","name_es":"Spanish name of the road","name_fr":"French name of the road","name_ru":"Russian name of the road","name_zh":"Chinese name of the road","osm_id":"Unique OSM ID number","ref":"Route number of the road","reflen":"Number. How many characters long the ref tag is. Useful for shield styling.","shield":"The shield style to use. One of: default, mx-federal, mx-state, us-highway, us-highway-alternate, us-highway-business, us-highway-duplex, us-interstate, us-interstate-business, us-interstate-duplex, us-interstate-truck, us-state"},"id":"road_label"},{"description":"","fields":{"class":"One of: river, canal, stream, stream_intermittent","name":"Local name of the waterway","name_de":"German name of the waterway","name_en":"English name of the waterway","name_es":"Spanish name of the waterway","name_fr":"French name of the waterway","name_ru":"Russian name of the waterway","name_zh":"Chinese name of the waterway","osm_id":"Unique OSM ID number","type":"One of: river, canal, stream"},"id":"waterway_label"},{"description":"","fields":{"house_num":"House number","osm_id":"Unique OSM ID number"},"id":"housenum_label"}]}
+{"attribution":"<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">&copy; Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">&copy; OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a>","bounds":[-180,-85.0511,180,85.0511],"center":[0,0,0],"created":1451865600000,"description":"","filesize":0,"fillzoom":8,"format":"pbf","id":"mapbox.mapbox-streets-v7","maxzoom":16,"minzoom":0,"name":"Mapbox Streets v7","private":false,"scheme":"xyz","tilejson":"2.0.0","tiles":["https://a.tiles.mapbox.com/v4/mapbox.mapbox-streets-v7/{z}/{x}/{y}.vector.pbf?access_token=pk.accesstoken","https://b.tiles.mapbox.com/v4/mapbox.mapbox-streets-v7/{z}/{x}/{y}.vector.pbf?access_token=pk.accesstoken"],"vector_layers":[{"description":"","fields":{"class":"One of: agriculture, cemetery, glacier, grass, hospital, industrial, park, parking, piste, pitch, rock, sand, school, scrub, wood, aboriginal lands","type":"OSM tag, more specific than class"},"id":"landuse","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"class":"One of: river, canal, stream, stream_intermittent, ditch, drain","type":"One of: river, canal, stream, ditch, drain"},"id":"waterway","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{},"id":"water","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"type":"One of: runway, taxiway, apron"},"id":"aeroway","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"class":"One of: fence, hedge, cliff, gate"},"id":"barrier_line","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"underground":"Text. Whether building is underground. One of: 'true', 'false'"},"id":"building","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"class":"One of: national_park, wetland, wetland_noveg","type":"OSM tag, more specific than class"},"id":"landuse_overlay","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"class":"One of: 'motorway', 'motorway_link', 'trunk', 'primary', 'secondary', 'tertiary', 'link', 'street', 'street_limited', 'pedestrian', 'construction', 'track', 'service', 'ferry', 'path', 'golf'","layer":"Number. Specifies z-ordering in the case of overlapping road segments. Common range is -5 to 5. Available from zoom level 13+.","oneway":"Text. Whether traffic on the road is one-way. One of: 'true', 'false'","structure":"Text. One of: 'none', 'bridge', 'tunnel', 'ford'. Available from zoom level 13+.","type":"In most cases, values will be that of the primary key from OpenStreetMap tags."},"id":"road","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"admin_level":"The OSM administrative level of the boundary","disputed":"Number. Disputed boundaries are 1, all others are 0.","iso_3166_1":"The ISO 3166-1 alpha-2 code(s) of the state(s) a boundary is part of. Format: 'AA' or 'AA-BB'","maritime":"Number. Maritime boundaries are 1, all others are 0."},"id":"admin","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"code":"ISO 3166-1 Alpha-2 code","name":"Local name of the country","name_de":"German name of the country","name_en":"English name of the country","name_es":"Spanish name of the country","name_fr":"French name of the country","name_ru":"Russian name of the country","name_zh":"Chinese name of the country","parent":"ISO 3166-1 Alpha-2 code of the administering/parent state, if any","scalerank":"Number, 1-6. Useful for styling text sizes.","type":"One of: country, territory, disputed territory, sar"},"id":"country_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"labelrank":"Number, 1-6. Useful for styling text sizes.","name":"Local or international name of the water body","name_de":"German name of the water body","name_en":"English name of the water body","name_es":"Spanish name of the water body","name_fr":"French name of the water body","name_ru":"Russian name of the water body","name_zh":"Chinese name of the water body","placement":"One of: point, line"},"id":"marine_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"abbr":"Abbreviated state name","area":"The area of the state in kilometers²","name":"Local name of the state","name_de":"German name of the state","name_en":"English name of the state","name_es":"Spanish name of the state","name_fr":"French name of the state","name_ru":"Russian name of the state","name_zh":"Chinese name of the state"},"id":"state_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"capital":"Admin level the city is a capital of, if any. One of: 2, 3, 4, 5, 6, null","ldir":"A hint for label placement at low zoom levels. One of: N, E, S, W, NE, SE, SW, NW, null","localrank":"Number. Priority relative to nearby places. Useful for limiting label density.","name":"Local name of the place","name_de":"German name of the place","name_en":"English name of the place","name_es":"Spanish name of the place","name_fr":"French name of the place","name_ru":"Russian name of the place","name_zh":"Chinese name of the place","scalerank":"Number, 0-9 or null. Useful for styling text & marker sizes.","type":"One of: city, town, village, hamlet, suburb, neighbourhood, island, islet, archipelago, residential, aboriginal_lands"},"id":"place_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"area":"The area of the water polygon in Mercator meters²","name":"Local name of the water body","name_de":"German name of the water body","name_en":"English name of the water body","name_es":"Spanish name of the water body","name_fr":"French name of the water body","name_ru":"Russian name of the water body","name_zh":"Chinese name of the water body"},"id":"water_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"maki":"One of: airport, airfield, heliport, rocket","name":"Local name of the airport","name_de":"German name of the airport","name_en":"English name of the airport","name_es":"Spanish name of the airport","name_fr":"French name of the airport","name_ru":"Russian name of the airport","name_zh":"Chinese name of the airport","ref":"A 3-4 character IATA, FAA, ICAO, or other reference code","scalerank":"Number 1-4. Useful for styling icon sizes."},"id":"airport_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"maki":"One of: rail, rail-metro, rail-light, entrance","name":"Local name of the station","name_de":"German name of the station","name_en":"English name of the station","name_es":"Spanish name of the station","name_fr":"French name of the station","name_ru":"Russian name of the station","name_zh":"Chinese name of the station","network":"The network(s) that the station serves. Useful for icon styling."},"id":"rail_station_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"elevation_ft":"Integer elevation in feet","elevation_m":"Integer elevation in meters","maki":"One of: 'mountain', 'volcano'","name":"Local name of the peak","name_de":"German name of the peak","name_en":"English name of the peak","name_es":"Spanish name of the peak","name_fr":"French name of the peak","name_ru":"Russian name of the peak","name_zh":"Chinese name of the peak"},"id":"mountain_peak_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"localrank":"Number. Priority relative to nearby POIs. Useful for limiting label density.","maki":"The name of the Maki icon that should be used for the POI","name":"Local name of the POI","name_de":"German name of the POI","name_en":"English name of the POI","name_es":"Spanish name of the POI","name_fr":"French name of the POI","name_ru":"Russian name of the POI","name_zh":"Chinese name of the POI","ref":"Short reference code, if any","scalerank":"Number. 1-5. Useful for styling icon sizes and minimum zoom levels.","type":"The original OSM tag value"},"id":"poi_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"class":"The class of road the junction is on. Matches the classes in the road layer.","name":"A longer name","ref":"A short identifier","reflen":"The number of characters in the ref field.","type":"The type of road the junction is on. Matches the types in the road layer."},"id":"motorway_junction","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"class":"One of: motorway, motorway_link, 'trunk', 'primary', 'secondary', 'tertiary', 'link', 'street', 'street_limited', 'pedestrian', 'construction', 'track', 'service', 'ferry', 'path', 'golf'","iso_3166_2":"Text. The ISO 3166-2 code of the state/province/region the road is in.","len":"Number. Approximate length of the road segment in Mercator meters.","localrank":"Number. Used for shield points only. Priority relative to nearby shields. Useful for limiting shield density.","name":"Local name of the road","name_de":"German name of the road","name_en":"English name of the road","name_es":"Spanish name of the road","name_fr":"French name of the road","name_ru":"Russian name of the road","name_zh":"Chinese name of the road","ref":"Route number of the road","reflen":"Number. How many characters long the ref tag is. Useful for shield styling.","shield":"The shield style to use. One of: default, mx-federal, mx-state, us-highway, us-highway-alternate, us-highway-business, us-highway-duplex, us-interstate, us-interstate-business, us-interstate-duplex, us-interstate-truck, us-state"},"id":"road_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"class":"One of: river, canal, stream, stream_intermittent","name":"Local name of the waterway","name_de":"German name of the waterway","name_en":"English name of the waterway","name_es":"Spanish name of the waterway","name_fr":"French name of the waterway","name_ru":"Russian name of the waterway","name_zh":"Chinese name of the waterway","type":"One of: river, canal, stream"},"id":"waterway_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"},{"description":"","fields":{"house_num":"House number"},"id":"housenum_label","source":"mapbox.mapbox-streets-v7","source_name":"Mapbox Streets V7"}],"webpage":"https://a.tiles.mapbox.com/v4/mapbox.mapbox-streets-v7/page.html?access_token=pk.accesstoken"} \ No newline at end of file
diff --git a/test/fixtures/resources/sprite.json b/test/fixtures/resources/sprite.json
index dcc2b4808c..2df69488e1 100644
--- a/test/fixtures/resources/sprite.json
+++ b/test/fixtures/resources/sprite.json
@@ -1 +1 @@
-{"background":{"x":0,"y":20,"width":50,"height":50,"pixelRatio":1,"sdf":false},"grass_pattern":{"x":100,"y":80,"width":50,"height":50,"pixelRatio":1,"sdf":false},"interstate_1":{"x":0,"y":100,"width":41,"height":40,"pixelRatio":1,"sdf":false},"interstate_2":{"x":0,"y":100,"width":41,"height":40,"pixelRatio":1,"sdf":false},"interstate_3":{"x":41,"y":100,"width":48,"height":39,"pixelRatio":1,"sdf":false},"us_state_1":{"x":0,"y":73,"width":29,"height":24,"pixelRatio":1,"sdf":false},"us_state_2":{"x":0,"y":73,"width":29,"height":24,"pixelRatio":1,"sdf":false},"us_state_3":{"x":30,"y":73,"width":32,"height":24,"pixelRatio":1,"sdf":false},"us_highway_1":{"x":0,"y":142,"width":29,"height":29,"pixelRatio":1,"sdf":false},"us_highway_2":{"x":30,"y":142,"width":33,"height":29,"pixelRatio":1,"sdf":false},"us_highway_3":{"x":64,"y":142,"width":36,"height":29,"pixelRatio":1,"sdf":false},"default_1":{"x":0,"y":0,"width":17,"height":16,"pixelRatio":1,"sdf":false},"default_2":{"x":17,"y":0,"width":22,"height":16,"pixelRatio":1,"sdf":false},"default_3":{"x":39,"y":0,"width":27,"height":16,"pixelRatio":1,"sdf":false},"default_4":{"x":66,"y":0,"width":32,"height":16,"pixelRatio":1,"sdf":false},"default_5":{"x":98,"y":0,"width":37,"height":16,"pixelRatio":1,"sdf":false},"default_6":{"x":135,"y":0,"width":42,"height":16,"pixelRatio":1,"sdf":false},"london-overground":{"x":70,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"london-underground":{"x":88,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"national-rail":{"x":106,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"dlr":{"x":106,"y":25,"width":18,"height":18,"pixelRatio":1,"sdf":false},"dlr.london-overground.london-underground.national-rail":{"x":70,"y":25,"width":72,"height":18,"pixelRatio":1,"sdf":false},"dlr.london-underground":{"x":88,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"dlr.london-underground.national-rail":{"x":88,"y":25,"width":54,"height":18,"pixelRatio":1,"sdf":false},"dlr.national-rail":{"x":106,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"london-overground.london-underground":{"x":70,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"london-overground.london-underground.national-rail":{"x":124,"y":25,"width":54,"height":18,"pixelRatio":1,"sdf":false},"london-overground.national-rail":{"x":124,"y":25,"width":36,"height":18,"pixelRatio":1,"sdf":false},"london-underground.national-rail":{"x":124,"y":43,"width":36,"height":18,"pixelRatio":1,"sdf":false},"metro":{"x":71,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"rer":{"x":87,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"metro.rer":{"x":71,"y":43,"width":34,"height":18,"pixelRatio":1,"sdf":false},"rer.transilien":{"x":87,"y":43,"width":36,"height":18,"pixelRatio":1,"sdf":false},"u-bahn":{"x":70,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"s-bahn":{"x":88,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"s-bahn.u-bahn":{"x":70,"y":62,"width":36,"height":18,"pixelRatio":1,"sdf":false},"washington-metro":{"x":106,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"wiener-linien":{"x":124,"y":62,"width":18,"height":18,"pixelRatio":1,"sdf":false},"moscow-metro":{"x":142,"y":61,"width":21,"height":18,"pixelRatio":1,"sdf":false},"generic-metro":{"x":160,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"generic-rail":{"x":178,"y":43,"width":18,"height":18,"pixelRatio":1,"sdf":false},"dot":{"x":166,"y":63,"width":13,"height":13,"pixelRatio":1,"sdf":false},"default_marker":{"x":0,"y":175,"width":33,"height":86,"pixelRatio":1,"sdf":false},"secondary_marker":{"x":33,"y":175,"width":33,"height":86,"pixelRatio":1,"sdf":false},"oneway_motorway":{"x":178,"y":24,"width":21,"height":19,"pixelRatio":1,"sdf":false},"oneway_road":{"x":178,"y":62,"width":21,"height":19,"pixelRatio":1,"sdf":false},"hospital_icon":{"x":157,"y":259,"width":18,"height":18,"pixelRatio":1,"sdf":false},"fire-station_icon":{"x":157,"y":241,"width":18,"height":18,"pixelRatio":1,"sdf":false},"cemetery_icon":{"x":157,"y":79,"width":18,"height":18,"pixelRatio":1,"sdf":false},"zoo_icon":{"x":177,"y":79,"width":18,"height":18,"pixelRatio":1,"sdf":false},"park_icon":{"x":177,"y":97,"width":18,"height":18,"pixelRatio":1,"sdf":false},"golf_icon":{"x":177,"y":115,"width":18,"height":18,"pixelRatio":1,"sdf":false},"school_icon":{"x":177,"y":133,"width":18,"height":18,"pixelRatio":1,"sdf":false},"monument_icon":{"x":177,"y":151,"width":18,"height":18,"pixelRatio":1,"sdf":false},"library_icon":{"x":177,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"museum_icon":{"x":177,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"college_icon":{"x":177,"y":205,"width":18,"height":18,"pixelRatio":1,"sdf":false},"religious-christian_icon":{"x":157,"y":115,"width":18,"height":18,"pixelRatio":1,"sdf":false},"religious-jewish_icon":{"x":157,"y":133,"width":18,"height":18,"pixelRatio":1,"sdf":false},"religious-muslim_icon":{"x":157,"y":151,"width":18,"height":18,"pixelRatio":1,"sdf":false},"government_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"post_icon":{"x":157,"y":205,"width":18,"height":18,"pixelRatio":1,"sdf":false},"embassy_icon":{"x":157,"y":223,"width":18,"height":18,"pixelRatio":1,"sdf":false},"police_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"marker_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"prison_icon":{"x":157,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"airfield_icon":{"x":157,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"airport_icon":{"x":157,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"harbor_icon":{"x":139,"y":169,"width":18,"height":18,"pixelRatio":1,"sdf":false},"generic_icon":{"x":139,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false},"hospital_striped":{"x":117,"y":135,"width":3,"height":3,"pixelRatio":1,"sdf":false},"school_striped":{"x":114,"y":135,"width":3,"height":3,"pixelRatio":1,"sdf":false},"sand_noise":{"x":75,"y":174,"width":50,"height":50,"pixelRatio":1,"sdf":false}} \ No newline at end of file
+{"pedestrian-polygon":{"width":64,"height":64,"x":0,"y":0,"pixelRatio":1},"turning-circle-outline":{"width":46,"height":46,"x":64,"y":0,"pixelRatio":1},"turning-circle":{"width":42,"height":42,"x":0,"y":64,"pixelRatio":1},"us-interstate-truck-2":{"width":20,"height":40,"x":42,"y":64,"pixelRatio":1},"us-interstate-truck-3":{"width":26,"height":40,"x":62,"y":64,"pixelRatio":1},"us-highway-alternate-2":{"width":20,"height":38,"x":88,"y":64,"pixelRatio":1},"us-highway-alternate-3":{"width":26,"height":38,"x":108,"y":64,"pixelRatio":1},"us-highway-business-2":{"width":20,"height":38,"x":134,"y":64,"pixelRatio":1},"us-highway-business-3":{"width":26,"height":38,"x":154,"y":64,"pixelRatio":1},"us-highway-bypass-2":{"width":20,"height":38,"x":180,"y":64,"pixelRatio":1},"us-highway-bypass-3":{"width":26,"height":38,"x":200,"y":64,"pixelRatio":1},"us-highway-truck-2":{"width":20,"height":38,"x":226,"y":64,"pixelRatio":1},"us-highway-truck-3":{"width":26,"height":38,"x":110,"y":0,"pixelRatio":1},"pe-national-2":{"width":18,"height":26,"x":136,"y":0,"pixelRatio":1},"pe-national-3":{"width":22,"height":26,"x":154,"y":0,"pixelRatio":1},"za-provincial-2":{"width":24,"height":24,"x":176,"y":0,"pixelRatio":1},"br-federal-3":{"width":26,"height":22,"x":200,"y":0,"pixelRatio":1},"in-national-2":{"width":16,"height":22,"x":226,"y":0,"pixelRatio":1},"in-national-3":{"width":20,"height":22,"x":0,"y":106,"pixelRatio":1},"in-national-4":{"width":24,"height":22,"x":20,"y":106,"pixelRatio":1},"in-state-2":{"width":16,"height":22,"x":44,"y":106,"pixelRatio":1},"in-state-3":{"width":20,"height":22,"x":60,"y":106,"pixelRatio":1},"mx-federal-2":{"width":18,"height":22,"x":80,"y":106,"pixelRatio":1},"mx-federal-3":{"width":23,"height":22,"x":98,"y":106,"pixelRatio":1},"mx-federal-4":{"width":28,"height":22,"x":121,"y":106,"pixelRatio":1},"mx-state-2":{"width":18,"height":22,"x":149,"y":106,"pixelRatio":1},"mx-state-3":{"width":23,"height":22,"x":167,"y":106,"pixelRatio":1},"mx-state-4":{"width":28,"height":22,"x":190,"y":106,"pixelRatio":1},"pe-regional-3":{"width":23,"height":22,"x":218,"y":106,"pixelRatio":1},"ro-communal-4":{"width":28,"height":22,"x":0,"y":128,"pixelRatio":1},"ro-communal-5":{"width":23,"height":22,"x":28,"y":128,"pixelRatio":1},"ro-communal-6":{"width":34,"height":22,"x":51,"y":128,"pixelRatio":1},"us-interstate-2":{"width":20,"height":22,"x":85,"y":128,"pixelRatio":1},"us-interstate-3":{"width":26,"height":22,"x":105,"y":128,"pixelRatio":1},"us-interstate-duplex-4":{"width":32,"height":22,"x":131,"y":128,"pixelRatio":1},"us-interstate-duplex-5":{"width":38,"height":22,"x":163,"y":128,"pixelRatio":1},"airfield-15":{"width":21,"height":21,"x":201,"y":128,"pixelRatio":1},"airport-15":{"width":21,"height":21,"x":222,"y":128,"pixelRatio":1},"alcohol-shop-15":{"width":21,"height":21,"x":0,"y":150,"pixelRatio":1},"amusement-park-15":{"width":21,"height":21,"x":21,"y":150,"pixelRatio":1},"aquarium-15":{"width":21,"height":21,"x":42,"y":150,"pixelRatio":1},"art-gallery-15":{"width":21,"height":21,"x":63,"y":150,"pixelRatio":1},"attraction-15":{"width":21,"height":21,"x":84,"y":150,"pixelRatio":1},"bakery-15":{"width":21,"height":21,"x":105,"y":150,"pixelRatio":1},"bank-15":{"width":21,"height":21,"x":126,"y":150,"pixelRatio":1},"bar-15":{"width":21,"height":21,"x":147,"y":150,"pixelRatio":1},"beer-15":{"width":21,"height":21,"x":168,"y":150,"pixelRatio":1},"bicycle-15":{"width":21,"height":21,"x":189,"y":150,"pixelRatio":1},"bicycle-share-15":{"width":21,"height":21,"x":210,"y":150,"pixelRatio":1},"bus-15":{"width":21,"height":21,"x":231,"y":150,"pixelRatio":1},"cafe-15":{"width":21,"height":21,"x":0,"y":171,"pixelRatio":1},"campsite-15":{"width":21,"height":21,"x":21,"y":171,"pixelRatio":1},"car-15":{"width":21,"height":21,"x":42,"y":171,"pixelRatio":1},"castle-15":{"width":21,"height":21,"x":63,"y":171,"pixelRatio":1},"cemetery-15":{"width":21,"height":21,"x":84,"y":171,"pixelRatio":1},"cinema-15":{"width":21,"height":21,"x":105,"y":171,"pixelRatio":1},"circle-15":{"width":21,"height":21,"x":126,"y":171,"pixelRatio":1},"circle-stroked-15":{"width":21,"height":21,"x":147,"y":171,"pixelRatio":1},"clothing-store-15":{"width":21,"height":21,"x":168,"y":171,"pixelRatio":1},"college-15":{"width":21,"height":21,"x":189,"y":171,"pixelRatio":1},"dentist-15":{"width":21,"height":21,"x":210,"y":171,"pixelRatio":1},"doctor-15":{"width":21,"height":21,"x":231,"y":171,"pixelRatio":1},"dog-park-15":{"width":21,"height":21,"x":0,"y":192,"pixelRatio":1},"drinking-water-15":{"width":21,"height":21,"x":21,"y":192,"pixelRatio":1},"embassy-15":{"width":21,"height":21,"x":42,"y":192,"pixelRatio":1},"entrance-15":{"width":21,"height":21,"x":63,"y":192,"pixelRatio":1},"fast-food-15":{"width":21,"height":21,"x":84,"y":192,"pixelRatio":1},"ferry-15":{"width":21,"height":21,"x":105,"y":192,"pixelRatio":1},"fire-station-15":{"width":21,"height":21,"x":126,"y":192,"pixelRatio":1},"fuel-15":{"width":21,"height":21,"x":147,"y":192,"pixelRatio":1},"garden-15":{"width":21,"height":21,"x":168,"y":192,"pixelRatio":1},"golf-15":{"width":21,"height":21,"x":189,"y":192,"pixelRatio":1},"grocery-15":{"width":21,"height":21,"x":210,"y":192,"pixelRatio":1},"harbor-15":{"width":21,"height":21,"x":231,"y":192,"pixelRatio":1},"heliport-15":{"width":21,"height":21,"x":0,"y":213,"pixelRatio":1},"hospital-15":{"width":21,"height":21,"x":21,"y":213,"pixelRatio":1},"ice-cream-15":{"width":21,"height":21,"x":42,"y":213,"pixelRatio":1},"information-15":{"width":21,"height":21,"x":63,"y":213,"pixelRatio":1},"laundry-15":{"width":21,"height":21,"x":84,"y":213,"pixelRatio":1},"library-15":{"width":21,"height":21,"x":105,"y":213,"pixelRatio":1},"lodging-15":{"width":21,"height":21,"x":126,"y":213,"pixelRatio":1},"monument-15":{"width":21,"height":21,"x":147,"y":213,"pixelRatio":1},"mountain-15":{"width":21,"height":21,"x":168,"y":213,"pixelRatio":1},"museum-15":{"width":21,"height":21,"x":189,"y":213,"pixelRatio":1},"music-15":{"width":21,"height":21,"x":210,"y":213,"pixelRatio":1},"park-15":{"width":21,"height":21,"x":231,"y":213,"pixelRatio":1},"pharmacy-15":{"width":21,"height":21,"x":0,"y":234,"pixelRatio":1},"picnic-site-15":{"width":21,"height":21,"x":21,"y":234,"pixelRatio":1},"place-of-worship-15":{"width":21,"height":21,"x":42,"y":234,"pixelRatio":1},"playground-15":{"width":21,"height":21,"x":63,"y":234,"pixelRatio":1},"police-15":{"width":21,"height":21,"x":84,"y":234,"pixelRatio":1},"post-15":{"width":21,"height":21,"x":105,"y":234,"pixelRatio":1},"prison-15":{"width":21,"height":21,"x":126,"y":234,"pixelRatio":1},"rail-15":{"width":21,"height":21,"x":147,"y":234,"pixelRatio":1},"rail-light-15":{"width":21,"height":21,"x":168,"y":234,"pixelRatio":1},"rail-metro-15":{"width":21,"height":21,"x":189,"y":234,"pixelRatio":1},"religious-christian-15":{"width":21,"height":21,"x":210,"y":234,"pixelRatio":1},"religious-jewish-15":{"width":21,"height":21,"x":231,"y":234,"pixelRatio":1},"religious-muslim-15":{"width":21,"height":21,"x":252,"y":150,"pixelRatio":1},"restaurant-15":{"width":21,"height":21,"x":273,"y":150,"pixelRatio":1},"rocket-15":{"width":21,"height":21,"x":294,"y":150,"pixelRatio":1},"school-15":{"width":21,"height":21,"x":315,"y":150,"pixelRatio":1},"shop-15":{"width":21,"height":21,"x":336,"y":150,"pixelRatio":1},"stadium-15":{"width":21,"height":21,"x":357,"y":150,"pixelRatio":1},"star-15":{"width":21,"height":21,"x":378,"y":150,"pixelRatio":1},"suitcase-15":{"width":21,"height":21,"x":399,"y":150,"pixelRatio":1},"swimming-15":{"width":21,"height":21,"x":420,"y":150,"pixelRatio":1},"theatre-15":{"width":21,"height":21,"x":441,"y":150,"pixelRatio":1},"toilet-15":{"width":21,"height":21,"x":462,"y":150,"pixelRatio":1},"town-hall-15":{"width":21,"height":21,"x":483,"y":150,"pixelRatio":1},"triangle-15":{"width":21,"height":21,"x":252,"y":171,"pixelRatio":1},"triangle-stroked-15":{"width":21,"height":21,"x":273,"y":171,"pixelRatio":1},"veterinary-15":{"width":21,"height":21,"x":294,"y":171,"pixelRatio":1},"volcano-15":{"width":21,"height":21,"x":315,"y":171,"pixelRatio":1},"zoo-15":{"width":21,"height":21,"x":336,"y":171,"pixelRatio":1},"br-state-2":{"width":20,"height":20,"x":357,"y":171,"pixelRatio":1},"br-state-3":{"width":28,"height":20,"x":377,"y":171,"pixelRatio":1},"hu-main-2":{"width":20,"height":20,"x":405,"y":171,"pixelRatio":1},"hu-main-3":{"width":26,"height":20,"x":425,"y":171,"pixelRatio":1},"hu-main-4":{"width":32,"height":20,"x":451,"y":171,"pixelRatio":1},"hu-main-5":{"width":38,"height":20,"x":252,"y":192,"pixelRatio":1},"hu-motorway-2":{"width":20,"height":20,"x":483,"y":171,"pixelRatio":1},"hu-motorway-3":{"width":26,"height":20,"x":290,"y":192,"pixelRatio":1},"nz-state-2":{"width":18,"height":20,"x":316,"y":192,"pixelRatio":1},"nz-state-3":{"width":23,"height":20,"x":334,"y":192,"pixelRatio":1},"ro-communal-2":{"width":20,"height":20,"x":357,"y":192,"pixelRatio":1},"ro-communal-3":{"width":26,"height":20,"x":377,"y":192,"pixelRatio":1},"ro-county-3":{"width":26,"height":20,"x":403,"y":192,"pixelRatio":1},"ro-county-4":{"width":32,"height":20,"x":429,"y":192,"pixelRatio":1},"ro-national-2":{"width":20,"height":20,"x":461,"y":192,"pixelRatio":1},"ro-national-3":{"width":26,"height":20,"x":481,"y":192,"pixelRatio":1},"us-highway-2":{"width":20,"height":20,"x":252,"y":213,"pixelRatio":1},"us-highway-3":{"width":26,"height":20,"x":272,"y":213,"pixelRatio":1},"us-highway-4":{"width":32,"height":20,"x":298,"y":213,"pixelRatio":1},"us-highway-duplex-3":{"width":26,"height":20,"x":330,"y":213,"pixelRatio":1},"us-highway-duplex-4":{"width":32,"height":20,"x":356,"y":213,"pixelRatio":1},"us-highway-duplex-5":{"width":38,"height":20,"x":388,"y":213,"pixelRatio":1},"us-interstate-business-2":{"width":20,"height":20,"x":426,"y":213,"pixelRatio":1},"us-interstate-business-3":{"width":26,"height":20,"x":446,"y":213,"pixelRatio":1},"us-state-2":{"width":20,"height":20,"x":472,"y":213,"pixelRatio":1},"us-state-3":{"width":26,"height":20,"x":252,"y":234,"pixelRatio":1},"us-state-4":{"width":32,"height":20,"x":278,"y":234,"pixelRatio":1},"za-national-2":{"width":20,"height":20,"x":492,"y":213,"pixelRatio":1},"barcelona-metro":{"width":19,"height":19,"x":310,"y":234,"pixelRatio":1},"boston-t":{"width":19,"height":19,"x":329,"y":234,"pixelRatio":1},"de-s-bahn":{"width":19,"height":19,"x":348,"y":234,"pixelRatio":1},"de-s-bahn.de-u-bahn":{"width":35,"height":19,"x":367,"y":234,"pixelRatio":1},"delhi-metro":{"width":19,"height":19,"x":402,"y":234,"pixelRatio":1},"kiev-metro":{"width":19,"height":19,"x":421,"y":234,"pixelRatio":1},"madrid-metro":{"width":19,"height":19,"x":440,"y":234,"pixelRatio":1},"new-york-subway":{"width":19,"height":19,"x":459,"y":234,"pixelRatio":1},"oslo-metro":{"width":19,"height":19,"x":478,"y":234,"pixelRatio":1},"paris-metro":{"width":19,"height":19,"x":241,"y":106,"pixelRatio":1},"paris-metro.paris-rer":{"width":37,"height":19,"x":260,"y":106,"pixelRatio":1},"paris-rer":{"width":19,"height":19,"x":297,"y":106,"pixelRatio":1},"paris-rer.paris-transilien":{"width":35,"height":19,"x":316,"y":106,"pixelRatio":1},"stockholm-metro":{"width":19,"height":19,"x":351,"y":106,"pixelRatio":1},"taipei-metro":{"width":19,"height":19,"x":370,"y":106,"pixelRatio":1},"vienna-u-bahn":{"width":19,"height":19,"x":389,"y":106,"pixelRatio":1},"airfield-11":{"width":17,"height":17,"x":408,"y":106,"pixelRatio":1},"airport-11":{"width":17,"height":17,"x":425,"y":106,"pixelRatio":1},"alcohol-shop-11":{"width":17,"height":17,"x":442,"y":106,"pixelRatio":1},"amusement-park-11":{"width":17,"height":17,"x":459,"y":106,"pixelRatio":1},"aquarium-11":{"width":17,"height":17,"x":476,"y":106,"pixelRatio":1},"art-gallery-11":{"width":17,"height":17,"x":493,"y":106,"pixelRatio":1},"attraction-11":{"width":17,"height":17,"x":243,"y":128,"pixelRatio":1},"bakery-11":{"width":17,"height":17,"x":260,"y":128,"pixelRatio":1},"bank-11":{"width":17,"height":17,"x":277,"y":128,"pixelRatio":1},"bar-11":{"width":17,"height":17,"x":294,"y":128,"pixelRatio":1},"beer-11":{"width":17,"height":17,"x":311,"y":128,"pixelRatio":1},"bicycle-11":{"width":17,"height":17,"x":328,"y":128,"pixelRatio":1},"bicycle-share-11":{"width":17,"height":17,"x":345,"y":128,"pixelRatio":1},"bus-11":{"width":17,"height":17,"x":362,"y":128,"pixelRatio":1},"cafe-11":{"width":17,"height":17,"x":379,"y":128,"pixelRatio":1},"campsite-11":{"width":17,"height":17,"x":396,"y":128,"pixelRatio":1},"car-11":{"width":17,"height":17,"x":413,"y":128,"pixelRatio":1},"castle-11":{"width":17,"height":17,"x":430,"y":128,"pixelRatio":1},"cemetery-11":{"width":17,"height":17,"x":447,"y":128,"pixelRatio":1},"chongqing-rail-transit":{"width":25,"height":17,"x":464,"y":128,"pixelRatio":1},"cinema-11":{"width":17,"height":17,"x":489,"y":128,"pixelRatio":1},"circle-11":{"width":17,"height":17,"x":246,"y":64,"pixelRatio":1},"circle-stroked-11":{"width":17,"height":17,"x":263,"y":64,"pixelRatio":1},"clothing-store-11":{"width":17,"height":17,"x":280,"y":64,"pixelRatio":1},"college-11":{"width":17,"height":17,"x":297,"y":64,"pixelRatio":1},"de-u-bahn":{"width":17,"height":17,"x":314,"y":64,"pixelRatio":1},"dentist-11":{"width":17,"height":17,"x":331,"y":64,"pixelRatio":1},"doctor-11":{"width":17,"height":17,"x":348,"y":64,"pixelRatio":1},"dog-park-11":{"width":17,"height":17,"x":365,"y":64,"pixelRatio":1},"drinking-water-11":{"width":17,"height":17,"x":382,"y":64,"pixelRatio":1},"embassy-11":{"width":17,"height":17,"x":399,"y":64,"pixelRatio":1},"entrance":{"width":17,"height":17,"x":416,"y":64,"pixelRatio":1},"entrance-11":{"width":17,"height":17,"x":433,"y":64,"pixelRatio":1},"fast-food-11":{"width":17,"height":17,"x":450,"y":64,"pixelRatio":1},"ferry-11":{"width":17,"height":17,"x":467,"y":64,"pixelRatio":1},"fire-station-11":{"width":17,"height":17,"x":484,"y":64,"pixelRatio":1},"fuel-11":{"width":17,"height":17,"x":242,"y":0,"pixelRatio":1},"garden-11":{"width":17,"height":17,"x":259,"y":0,"pixelRatio":1},"golf-11":{"width":17,"height":17,"x":276,"y":0,"pixelRatio":1},"grocery-11":{"width":17,"height":17,"x":293,"y":0,"pixelRatio":1},"harbor-11":{"width":17,"height":17,"x":310,"y":0,"pixelRatio":1},"heliport-11":{"width":17,"height":17,"x":327,"y":0,"pixelRatio":1},"hong-kong-mtr":{"width":19,"height":17,"x":344,"y":0,"pixelRatio":1},"hospital-11":{"width":17,"height":17,"x":363,"y":0,"pixelRatio":1},"ice-cream-11":{"width":17,"height":17,"x":380,"y":0,"pixelRatio":1},"information-11":{"width":17,"height":17,"x":397,"y":0,"pixelRatio":1},"laundry-11":{"width":17,"height":17,"x":414,"y":0,"pixelRatio":1},"library-11":{"width":17,"height":17,"x":431,"y":0,"pixelRatio":1},"lodging-11":{"width":17,"height":17,"x":448,"y":0,"pixelRatio":1},"mexico-city-metro":{"width":17,"height":17,"x":465,"y":0,"pixelRatio":1},"milan-metro":{"width":17,"height":17,"x":482,"y":0,"pixelRatio":1},"monument-11":{"width":17,"height":17,"x":0,"y":255,"pixelRatio":1},"moscow-metro":{"width":17,"height":17,"x":17,"y":255,"pixelRatio":1},"mountain-11":{"width":17,"height":17,"x":34,"y":255,"pixelRatio":1},"museum-11":{"width":17,"height":17,"x":51,"y":255,"pixelRatio":1},"music-11":{"width":17,"height":17,"x":68,"y":255,"pixelRatio":1},"osaka-subway":{"width":20,"height":17,"x":85,"y":255,"pixelRatio":1},"paris-transilien":{"width":17,"height":17,"x":105,"y":255,"pixelRatio":1},"park-11":{"width":17,"height":17,"x":122,"y":255,"pixelRatio":1},"pharmacy-11":{"width":17,"height":17,"x":139,"y":255,"pixelRatio":1},"philadelphia-septa":{"width":19,"height":17,"x":156,"y":255,"pixelRatio":1},"picnic-site-11":{"width":17,"height":17,"x":175,"y":255,"pixelRatio":1},"place-of-worship-11":{"width":17,"height":17,"x":192,"y":255,"pixelRatio":1},"playground-11":{"width":17,"height":17,"x":209,"y":255,"pixelRatio":1},"police-11":{"width":17,"height":17,"x":226,"y":255,"pixelRatio":1},"post-11":{"width":17,"height":17,"x":243,"y":255,"pixelRatio":1},"prison-11":{"width":17,"height":17,"x":260,"y":255,"pixelRatio":1},"rail":{"width":17,"height":17,"x":277,"y":255,"pixelRatio":1},"rail-11":{"width":17,"height":17,"x":294,"y":255,"pixelRatio":1},"rail-light":{"width":17,"height":17,"x":311,"y":255,"pixelRatio":1},"rail-light-11":{"width":17,"height":17,"x":328,"y":255,"pixelRatio":1},"rail-metro":{"width":17,"height":17,"x":345,"y":255,"pixelRatio":1},"rail-metro-11":{"width":17,"height":17,"x":362,"y":255,"pixelRatio":1},"religious-christian-11":{"width":17,"height":17,"x":379,"y":255,"pixelRatio":1},"religious-jewish-11":{"width":17,"height":17,"x":396,"y":255,"pixelRatio":1},"religious-muslim-11":{"width":17,"height":17,"x":413,"y":255,"pixelRatio":1},"restaurant-11":{"width":17,"height":17,"x":430,"y":255,"pixelRatio":1},"rocket-11":{"width":17,"height":17,"x":447,"y":255,"pixelRatio":1},"san-francisco-bart":{"width":17,"height":17,"x":464,"y":255,"pixelRatio":1},"school-11":{"width":17,"height":17,"x":481,"y":255,"pixelRatio":1},"shop-11":{"width":17,"height":17,"x":0,"y":272,"pixelRatio":1},"singapore-mrt":{"width":17,"height":17,"x":17,"y":272,"pixelRatio":1},"stadium-11":{"width":17,"height":17,"x":34,"y":272,"pixelRatio":1},"star-11":{"width":17,"height":17,"x":51,"y":272,"pixelRatio":1},"suitcase-11":{"width":17,"height":17,"x":68,"y":272,"pixelRatio":1},"swimming-11":{"width":17,"height":17,"x":85,"y":272,"pixelRatio":1},"theatre-11":{"width":17,"height":17,"x":102,"y":272,"pixelRatio":1},"toilet-11":{"width":17,"height":17,"x":119,"y":272,"pixelRatio":1},"tokyo-metro":{"width":17,"height":17,"x":136,"y":272,"pixelRatio":1},"town-hall-11":{"width":17,"height":17,"x":153,"y":272,"pixelRatio":1},"triangle-11":{"width":17,"height":17,"x":170,"y":272,"pixelRatio":1},"triangle-stroked-11":{"width":17,"height":17,"x":187,"y":272,"pixelRatio":1},"veterinary-11":{"width":17,"height":17,"x":204,"y":272,"pixelRatio":1},"volcano-11":{"width":17,"height":17,"x":221,"y":272,"pixelRatio":1},"washington-metro":{"width":17,"height":17,"x":238,"y":272,"pixelRatio":1},"zoo-11":{"width":17,"height":17,"x":255,"y":272,"pixelRatio":1},"ch-motorway-2":{"width":22,"height":16,"x":272,"y":272,"pixelRatio":1},"ch-motorway-3":{"width":28,"height":16,"x":294,"y":272,"pixelRatio":1},"ch-motorway-4":{"width":34,"height":16,"x":322,"y":272,"pixelRatio":1},"de-motorway-2":{"width":22,"height":16,"x":356,"y":272,"pixelRatio":1},"de-motorway-3":{"width":28,"height":16,"x":378,"y":272,"pixelRatio":1},"gb-national-rail.london-dlr":{"width":36,"height":16,"x":406,"y":272,"pixelRatio":1},"gb-national-rail.london-dlr.london-overground.london-tfl-rail.london-underground":{"width":93,"height":16,"x":0,"y":289,"pixelRatio":1},"gb-national-rail.london-dlr.london-overground.london-underground":{"width":74,"height":16,"x":93,"y":289,"pixelRatio":1},"gb-national-rail.london-dlr.london-underground":{"width":55,"height":16,"x":167,"y":289,"pixelRatio":1},"gb-national-rail.london-overground":{"width":36,"height":16,"x":222,"y":289,"pixelRatio":1},"gb-national-rail.london-overground.london-tfl-rail.london-underground":{"width":74,"height":16,"x":258,"y":289,"pixelRatio":1},"gb-national-rail.london-overground.london-underground":{"width":55,"height":16,"x":332,"y":289,"pixelRatio":1},"gb-national-rail.london-tfl-rail":{"width":36,"height":16,"x":387,"y":289,"pixelRatio":1},"gb-national-rail.london-tfl-rail.london-overground":{"width":55,"height":16,"x":423,"y":289,"pixelRatio":1},"gb-national-rail.london-tfl-rail.london-underground":{"width":55,"height":16,"x":442,"y":272,"pixelRatio":1},"gb-national-rail.london-underground":{"width":36,"height":16,"x":0,"y":305,"pixelRatio":1},"gr-motorway-2":{"width":22,"height":16,"x":478,"y":289,"pixelRatio":1},"gr-motorway-3":{"width":28,"height":16,"x":36,"y":305,"pixelRatio":1},"gr-motorway-4":{"width":34,"height":16,"x":64,"y":305,"pixelRatio":1},"hr-motorway-3":{"width":28,"height":16,"x":98,"y":305,"pixelRatio":1},"hr-motorway-4":{"width":34,"height":16,"x":126,"y":305,"pixelRatio":1},"london-dlr":{"width":20,"height":16,"x":160,"y":305,"pixelRatio":1},"london-dlr.london-tfl-rail":{"width":39,"height":16,"x":180,"y":305,"pixelRatio":1},"london-dlr.london-tfl-rail.london-underground":{"width":58,"height":16,"x":219,"y":305,"pixelRatio":1},"london-dlr.london-underground":{"width":39,"height":16,"x":277,"y":305,"pixelRatio":1},"london-overground":{"width":20,"height":16,"x":316,"y":305,"pixelRatio":1},"london-overground.london-tfl-rail":{"width":39,"height":16,"x":336,"y":305,"pixelRatio":1},"london-overground.london-tfl-rail.london-underground":{"width":58,"height":16,"x":375,"y":305,"pixelRatio":1},"london-overground.london-underground":{"width":39,"height":16,"x":433,"y":305,"pixelRatio":1},"london-tfl-rail":{"width":20,"height":16,"x":472,"y":305,"pixelRatio":1},"london-tfl-rail.london-underground":{"width":39,"height":16,"x":0,"y":321,"pixelRatio":1},"london-underground":{"width":20,"height":16,"x":492,"y":305,"pixelRatio":1},"si-motorway-2":{"width":22,"height":16,"x":39,"y":321,"pixelRatio":1},"at-expressway-2":{"width":20,"height":14,"x":61,"y":321,"pixelRatio":1},"at-expressway-3":{"width":26,"height":14,"x":81,"y":321,"pixelRatio":1},"at-motorway-2":{"width":20,"height":14,"x":107,"y":321,"pixelRatio":1},"at-motorway-3":{"width":26,"height":14,"x":127,"y":321,"pixelRatio":1},"at-state-b-2":{"width":20,"height":14,"x":153,"y":321,"pixelRatio":1},"at-state-b-3":{"width":26,"height":14,"x":173,"y":321,"pixelRatio":1},"bg-motorway-2":{"width":20,"height":14,"x":199,"y":321,"pixelRatio":1},"bg-national-2":{"width":20,"height":14,"x":219,"y":321,"pixelRatio":1},"ch-main-2":{"width":20,"height":14,"x":239,"y":321,"pixelRatio":1},"ch-main-3":{"width":26,"height":14,"x":259,"y":321,"pixelRatio":1},"cz-expressway-2":{"width":20,"height":14,"x":285,"y":321,"pixelRatio":1},"cz-expressway-3":{"width":26,"height":14,"x":305,"y":321,"pixelRatio":1},"cz-motorway-2":{"width":20,"height":14,"x":331,"y":321,"pixelRatio":1},"cz-road-2":{"width":20,"height":14,"x":351,"y":321,"pixelRatio":1},"cz-road-3":{"width":26,"height":14,"x":371,"y":321,"pixelRatio":1},"de-federal-2":{"width":20,"height":14,"x":397,"y":321,"pixelRatio":1},"de-federal-3":{"width":26,"height":14,"x":417,"y":321,"pixelRatio":1},"de-federal-4":{"width":32,"height":14,"x":443,"y":321,"pixelRatio":1},"default-2":{"width":20,"height":14,"x":475,"y":321,"pixelRatio":1},"default-3":{"width":26,"height":14,"x":0,"y":337,"pixelRatio":1},"default-4":{"width":32,"height":14,"x":26,"y":337,"pixelRatio":1},"default-5":{"width":38,"height":14,"x":58,"y":337,"pixelRatio":1},"default-6":{"width":44,"height":14,"x":96,"y":337,"pixelRatio":1},"dk-primary-2":{"width":20,"height":14,"x":140,"y":337,"pixelRatio":1},"dk-secondary-3":{"width":26,"height":14,"x":160,"y":337,"pixelRatio":1},"e-road-2":{"width":20,"height":14,"x":186,"y":337,"pixelRatio":1},"e-road-3":{"width":26,"height":14,"x":206,"y":337,"pixelRatio":1},"e-road-4":{"width":32,"height":14,"x":232,"y":337,"pixelRatio":1},"fi-main-2":{"width":20,"height":14,"x":264,"y":337,"pixelRatio":1},"fi-regional-3":{"width":26,"height":14,"x":284,"y":337,"pixelRatio":1},"fi-trunk-2":{"width":20,"height":14,"x":310,"y":337,"pixelRatio":1},"gb-national-rail":{"width":17,"height":14,"x":330,"y":337,"pixelRatio":1},"gr-national-2":{"width":20,"height":14,"x":347,"y":337,"pixelRatio":1},"gr-national-3":{"width":26,"height":14,"x":367,"y":337,"pixelRatio":1},"gr-national-4":{"width":32,"height":14,"x":393,"y":337,"pixelRatio":1},"hr-county-4":{"width":32,"height":14,"x":425,"y":337,"pixelRatio":1},"hr-state-2":{"width":20,"height":14,"x":457,"y":337,"pixelRatio":1},"hr-state-3":{"width":26,"height":14,"x":477,"y":337,"pixelRatio":1},"marker-15":{"width":6,"height":14,"x":503,"y":337,"pixelRatio":1},"motorway-exit-1":{"width":20,"height":14,"x":0,"y":351,"pixelRatio":1},"motorway-exit-2":{"width":20,"height":14,"x":20,"y":351,"pixelRatio":1},"motorway-exit-3":{"width":26,"height":14,"x":40,"y":351,"pixelRatio":1},"motorway-exit-4":{"width":32,"height":14,"x":66,"y":351,"pixelRatio":1},"motorway-exit-5":{"width":38,"height":14,"x":98,"y":351,"pixelRatio":1},"motorway-exit-6":{"width":44,"height":14,"x":136,"y":351,"pixelRatio":1},"motorway-exit-7":{"width":50,"height":14,"x":180,"y":351,"pixelRatio":1},"motorway-exit-8":{"width":56,"height":14,"x":230,"y":351,"pixelRatio":1},"pl-expressway-2":{"width":20,"height":14,"x":286,"y":351,"pixelRatio":1},"pl-expressway-3":{"width":26,"height":14,"x":306,"y":351,"pixelRatio":1},"pl-motorway-2":{"width":20,"height":14,"x":332,"y":351,"pixelRatio":1},"pl-motorway-3":{"width":26,"height":14,"x":352,"y":351,"pixelRatio":1},"pl-national-2":{"width":20,"height":14,"x":378,"y":351,"pixelRatio":1},"pl-voivodeship-3":{"width":26,"height":14,"x":398,"y":351,"pixelRatio":1},"ro-motorway-2":{"width":20,"height":14,"x":424,"y":351,"pixelRatio":1},"ro-motorway-3":{"width":26,"height":14,"x":444,"y":351,"pixelRatio":1},"rs-motorway-3":{"width":26,"height":14,"x":470,"y":351,"pixelRatio":1},"rs-state-1b-2":{"width":20,"height":14,"x":0,"y":365,"pixelRatio":1},"rs-state-2a-3":{"width":26,"height":14,"x":20,"y":365,"pixelRatio":1},"rs-state-2b-3":{"width":26,"height":14,"x":46,"y":365,"pixelRatio":1},"se-main-2":{"width":20,"height":14,"x":72,"y":365,"pixelRatio":1},"se-main-3":{"width":26,"height":14,"x":92,"y":365,"pixelRatio":1},"si-expressway-3":{"width":26,"height":14,"x":118,"y":365,"pixelRatio":1},"si-main-2":{"width":20,"height":14,"x":144,"y":365,"pixelRatio":1},"si-main-3":{"width":26,"height":14,"x":164,"y":365,"pixelRatio":1},"sk-highway-2":{"width":20,"height":14,"x":190,"y":365,"pixelRatio":1},"sk-road-2":{"width":20,"height":14,"x":210,"y":365,"pixelRatio":1},"sk-road-3":{"width":26,"height":14,"x":230,"y":365,"pixelRatio":1},"sk-road-4":{"width":32,"height":14,"x":256,"y":365,"pixelRatio":1},"sk-road-5":{"width":38,"height":14,"x":288,"y":365,"pixelRatio":1},"za-metropolitan-2":{"width":20,"height":14,"x":326,"y":365,"pixelRatio":1},"za-regional-3":{"width":26,"height":14,"x":346,"y":365,"pixelRatio":1},"dot-11":{"width":11,"height":11,"x":496,"y":351,"pixelRatio":1},"marker-11":{"width":6,"height":11,"x":372,"y":365,"pixelRatio":1},"dot-10":{"width":10,"height":10,"x":378,"y":365,"pixelRatio":1},"dot-9":{"width":9,"height":9,"x":388,"y":365,"pixelRatio":1},"oneway-large":{"width":14,"height":6,"x":397,"y":365,"pixelRatio":1},"oneway-white-large":{"width":14,"height":6,"x":411,"y":365,"pixelRatio":1},"oneway-small":{"width":11,"height":5,"x":425,"y":365,"pixelRatio":1},"oneway-white-small":{"width":11,"height":5,"x":436,"y":365,"pixelRatio":1}} \ No newline at end of file
diff --git a/test/fixtures/resources/sprite.png b/test/fixtures/resources/sprite.png
index 967f2e76a6..b4db4423c7 100644
--- a/test/fixtures/resources/sprite.png
+++ b/test/fixtures/resources/sprite.png
Binary files differ
diff --git a/test/fixtures/resources/style.json b/test/fixtures/resources/style.json
deleted file mode 100644
index 858e2de037..0000000000
--- a/test/fixtures/resources/style.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "version": 8,
- "name": "Test",
- "sources": {
- "vectorsource": {
- "url": "test/fixtures/resources/source_vector.json",
- "type": "vector"
- },
- "rastersource": {
- "url": "test/fixtures/resources/source_raster.json",
- "type": "raster",
- "tileSize": 512
- }
- },
- "sprite": "test/fixtures/resources/sprite",
- "glyphs": "test/fixtures/resources/glyphs.pbf",
- "layers": [{
- "id": "road",
- "type": "symbol",
- "source": "vectorsource",
- "source-layer": "road_label",
- "layout": {
- "text-font": ["Open Sans Regular", "Arial Unicode MS Regular"],
- "text-field": "{name_en}"
- }
- }, {
- "id": "poi",
- "type": "symbol",
- "source": "vectorsource",
- "source-layer": "poi_label",
- "layout": {
- "text-font": ["Open Sans Regular", "Arial Unicode MS Regular"],
- "icon-image": "{maki}_icon"
- }
- }, {
- "id": "rasterlayer",
- "type": "fill",
- "source": "rastersource"
- }]
-}
diff --git a/test/fixtures/resources/style_raster.json b/test/fixtures/resources/style_raster.json
new file mode 100644
index 0000000000..f0e971daef
--- /dev/null
+++ b/test/fixtures/resources/style_raster.json
@@ -0,0 +1 @@
+{"version":8,"name":"Satellite","metadata":{"mapbox:autocomposite":true,"mapbox:type":"default"},"sources":{"mapbox":{"type":"raster","url":"mapbox://mapbox.satellite","tileSize":256}},"sprite":"mapbox://sprites/mapbox/satellite-v9","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"}],"created":0,"modified":0,"owner":"mapbox","id":"satellite-v9","draft":false} \ No newline at end of file
diff --git a/test/fixtures/resources/style_vector.json b/test/fixtures/resources/style_vector.json
new file mode 100644
index 0000000000..ec0ad95156
--- /dev/null
+++ b/test/fixtures/resources/style_vector.json
@@ -0,0 +1 @@
+{"version":8,"name":"Mapbox Streets","metadata":{"mapbox:autocomposite":true,"mapbox:type":"default","mapbox:groups":{"1444934828655.3389":{"name":"Aeroways","collapsed":true},"1444933322393.2852":{"name":"POI labels (scalerank 1)","collapsed":true},"1444855898284.2651":{"name":"Aeroways","collapsed":true},"1444862578782.6787":{"name":"Road labels","collapsed":true},"1444934749452.0452":{"name":"Wetlands","collapsed":true},"1444862074717.8372":{"name":"Waterways","collapsed":true},"1444855868004.2437":{"name":"Landuse","collapsed":true},"1444855786460.0557":{"name":"Roads","collapsed":true},"1444856071629.7817":{"name":"Place labels","collapsed":true},"1444933575858.6992":{"name":"Highway shields","collapsed":true},"1444934295202.7542":{"name":"Admin boundaries","collapsed":true},"1444856904773.373":{"name":"Land barriers","collapsed":true},"1444856931506.5164":{"name":"Barriers","collapsed":true},"1444856151690.9143":{"name":"State labels","collapsed":true},"1444933721429.3076":{"name":"Road labels","collapsed":true},"1444933358918.2366":{"name":"POI labels (scalerank 2)","collapsed":true},"1444933808272.805":{"name":"Water labels","collapsed":true},"1444933372896.5967":{"name":"POI labels (scalerank 3)","collapsed":true},"1444855799204.86":{"name":"Bridges","collapsed":true},"1444856087950.3635":{"name":"Marine labels","collapsed":true},"1456969573402.7817":{"name":"Hillshading","collapsed":true},"1444856869758.2375":{"name":"Wetlands","collapsed":true},"1444862510685.128":{"name":"City labels","collapsed":true},"1444856954425.4016":{"name":"Buildings","collapsed":true},"1444855769305.6016":{"name":"Tunnels","collapsed":true},"1456970288113.8113":{"name":"Landcover","collapsed":true},"1444856144497.7825":{"name":"Country labels","collapsed":true},"1444856712129.5933":{"name":"Waterways","collapsed":true},"1444933456003.5437":{"name":"POI labels (scalerank 4)","collapsed":true}}},"sources":{"composite":{"url":"mapbox://mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7","type":"vector"}},"sprite":"mapbox://sprites/mapbox/streets-v9","glyphs":"mapbox://fonts/mapbox/{fontstack}/{range}.pbf","layers":[{"id":"background","type":"background","layout":{},"paint":{"background-color":{"base":1,"stops":[[11,"hsl(35, 32%, 91%)"],[13,"hsl(35, 12%, 89%)"]]}}},{"id":"landcover_snow","type":"fill","metadata":{"mapbox:group":"1456970288113.8113"},"source":"composite","source-layer":"landcover","filter":["==","class","snow"],"layout":{},"paint":{"fill-color":"hsl(0, 0%, 100%)","fill-opacity":0.2,"fill-antialias":false}},{"id":"landcover_wood","type":"fill","metadata":{"mapbox:group":"1456970288113.8113"},"source":"composite","source-layer":"landcover","maxzoom":14,"filter":["==","class","wood"],"layout":{},"paint":{"fill-color":"hsl(75, 62%, 81%)","fill-opacity":{"base":1.5,"stops":[[2,0.3],[7,0]]},"fill-antialias":false}},{"id":"landcover_scrub","type":"fill","metadata":{"mapbox:group":"1456970288113.8113"},"source":"composite","source-layer":"landcover","maxzoom":14,"filter":["==","class","scrub"],"layout":{},"paint":{"fill-color":"hsl(75, 62%, 81%)","fill-opacity":{"base":1.5,"stops":[[2,0.3],[7,0]]},"fill-antialias":false}},{"id":"landcover_grass","type":"fill","metadata":{"mapbox:group":"1456970288113.8113"},"source":"composite","source-layer":"landcover","maxzoom":14,"filter":["==","class","grass"],"layout":{},"paint":{"fill-color":"hsl(75, 62%, 81%)","fill-opacity":{"base":1.5,"stops":[[2,0.3],[7,0]]},"fill-antialias":false}},{"id":"landcover_crop","type":"fill","metadata":{"mapbox:group":"1456970288113.8113"},"source":"composite","source-layer":"landcover","maxzoom":14,"filter":["==","class","crop"],"layout":{},"paint":{"fill-color":"hsl(75, 62%, 81%)","fill-opacity":{"base":1.5,"stops":[[2,0.3],[7,0]]},"fill-antialias":false}},{"id":"national_park","type":"fill","source":"composite","source-layer":"landuse_overlay","filter":["==","class","national_park"],"layout":{},"paint":{"fill-color":"hsl(100, 58%, 76%)","fill-opacity":{"base":1,"stops":[[5,0],[6,0.5]]}}},{"id":"hospital","type":"fill","source":"composite","source-layer":"landuse","filter":["==","class","hospital"],"layout":{},"paint":{"fill-color":{"base":1,"stops":[[15.5,"hsl(340, 37%, 87%)"],[16,"hsl(340, 63%, 89%)"]]}}},{"id":"school","type":"fill","source":"composite","source-layer":"landuse","filter":["==","class","school"],"layout":{},"paint":{"fill-color":{"base":1,"stops":[[15.5,"hsl(50, 47%, 81%)"],[16,"hsl(50, 63%, 84%)"]]}}},{"id":"park","type":"fill","source":"composite","source-layer":"landuse","filter":["==","class","park"],"layout":{},"paint":{"fill-color":"hsl(100, 58%, 76%)","fill-opacity":{"base":1,"stops":[[5,0],[6,1]]}}},{"id":"pitch","type":"fill","source":"composite","source-layer":"landuse","filter":["==","class","pitch"],"layout":{},"paint":{"fill-color":"hsl(100, 57%, 72%)"}},{"id":"pitch-line","type":"line","source":"composite","source-layer":"landuse","minzoom":15,"filter":["==","class","pitch"],"layout":{"line-join":"miter"},"paint":{"line-color":"hsl(75, 57%, 84%)"}},{"id":"cemetery","type":"fill","source":"composite","source-layer":"landuse","filter":["==","class","cemetery"],"layout":{},"paint":{"fill-color":"hsl(75, 37%, 81%)"}},{"id":"industrial","type":"fill","source":"composite","source-layer":"landuse","filter":["==","class","industrial"],"layout":{},"paint":{"fill-color":{"base":1,"stops":[[15.5,"hsl(230, 15%, 86%)"],[16,"hsl(230, 29%, 89%)"]]}}},{"id":"sand","type":"fill","source":"composite","source-layer":"landuse","filter":["==","class","sand"],"layout":{},"paint":{"fill-color":"hsl(60, 46%, 87%)"}},{"id":"hillshade_highlight_bright","type":"fill","metadata":{"mapbox:group":"1456969573402.7817"},"source":"composite","source-layer":"hillshade","maxzoom":16,"filter":["==","level",94],"layout":{},"paint":{"fill-color":"hsl(0, 0%, 100%)","fill-opacity":{"stops":[[14,0.12],[16,0]]},"fill-antialias":false}},{"id":"hillshade_highlight_med","type":"fill","metadata":{"mapbox:group":"1456969573402.7817"},"source":"composite","source-layer":"hillshade","maxzoom":16,"filter":["==","level",90],"layout":{},"paint":{"fill-color":"hsl(0, 0%, 100%)","fill-opacity":{"stops":[[14,0.12],[16,0]]},"fill-antialias":false}},{"id":"hillshade_shadow_faint","type":"fill","metadata":{"mapbox:group":"1456969573402.7817"},"source":"composite","source-layer":"hillshade","maxzoom":16,"filter":["==","level",89],"layout":{},"paint":{"fill-color":"hsl(56, 59%, 22%)","fill-opacity":{"stops":[[14,0.05],[16,0]]},"fill-antialias":false}},{"id":"hillshade_shadow_med","type":"fill","metadata":{"mapbox:group":"1456969573402.7817"},"source":"composite","source-layer":"hillshade","maxzoom":16,"filter":["==","level",78],"layout":{},"paint":{"fill-color":"hsl(56, 59%, 22%)","fill-opacity":{"stops":[[14,0.05],[16,0]]},"fill-antialias":false}},{"id":"hillshade_shadow_dark","type":"fill","metadata":{"mapbox:group":"1456969573402.7817"},"source":"composite","source-layer":"hillshade","maxzoom":16,"filter":["==","level",67],"layout":{},"paint":{"fill-color":"hsl(56, 59%, 22%)","fill-opacity":{"stops":[[14,0.06],[16,0]]},"fill-antialias":false}},{"id":"hillshade_shadow_extreme","type":"fill","metadata":{"mapbox:group":"1456969573402.7817"},"source":"composite","source-layer":"hillshade","maxzoom":16,"filter":["==","level",56],"layout":{},"paint":{"fill-color":"hsl(56, 59%, 22%)","fill-opacity":{"stops":[[14,0.06],[16,0]]},"fill-antialias":false}},{"id":"waterway-river-canal","type":"line","source":"composite","source-layer":"waterway","minzoom":8,"filter":["in","class","canal","river"],"layout":{"line-cap":{"base":1,"stops":[[0,"butt"],[11,"round"]]},"line-join":"round"},"paint":{"line-color":"hsl(205, 87%, 76%)","line-width":{"base":1.3,"stops":[[8.5,0.1],[20,8]]},"line-opacity":{"base":1,"stops":[[8,0],[8.5,1]]}}},{"id":"waterway-small","type":"line","source":"composite","source-layer":"waterway","minzoom":13,"filter":["!in","class","canal","river"],"layout":{"line-join":"round","line-cap":"round"},"paint":{"line-color":"hsl(205, 87%, 76%)","line-width":{"base":1.35,"stops":[[13.5,0.1],[20,3]]},"line-opacity":{"base":1,"stops":[[13,0],[13.5,1]]}}},{"id":"water-shadow","type":"fill","source":"composite","source-layer":"water","layout":{},"paint":{"fill-color":"hsl(215, 84%, 69%)","fill-translate":{"base":1.2,"stops":[[7,[0,0]],[16,[-1,-1]]]},"fill-translate-anchor":"viewport","fill-opacity":1}},{"id":"water","ref":"water-shadow","paint":{"fill-color":"hsl(196, 80%, 70%)"}},{"id":"barrier_line-land-polygon","type":"fill","source":"composite","source-layer":"barrier_line","filter":["all",["==","$type","Polygon"],["==","class","land"]],"layout":{},"paint":{"fill-color":"hsl(35, 12%, 89%)"}},{"id":"barrier_line-land-line","type":"line","source":"composite","source-layer":"barrier_line","filter":["all",["==","$type","LineString"],["==","class","land"]],"layout":{"line-cap":"round"},"paint":{"line-width":{"base":1.99,"stops":[[14,0.75],[20,40]]},"line-color":"hsl(35, 12%, 89%)"}},{"id":"aeroway-polygon","type":"fill","metadata":{"mapbox:group":"1444934828655.3389"},"source":"composite","source-layer":"aeroway","minzoom":11,"filter":["all",["==","$type","Polygon"],["!=","type","apron"]],"layout":{},"paint":{"fill-color":{"base":1,"stops":[[15,"hsl(230, 23%, 82%)"],[16,"hsl(230, 37%, 84%)"]]},"fill-opacity":{"base":1,"stops":[[11,0],[11.5,1]]}}},{"id":"aeroway-runway","type":"line","metadata":{"mapbox:group":"1444934828655.3389"},"source":"composite","source-layer":"aeroway","minzoom":9,"filter":["all",["==","$type","LineString"],["==","type","runway"]],"layout":{},"paint":{"line-color":{"base":1,"stops":[[15,"hsl(230, 23%, 82%)"],[16,"hsl(230, 37%, 84%)"]]},"line-width":{"base":1.5,"stops":[[9,1],[18,80]]}}},{"id":"aeroway-taxiway","type":"line","metadata":{"mapbox:group":"1444934828655.3389"},"source":"composite","source-layer":"aeroway","minzoom":9,"filter":["all",["==","$type","LineString"],["==","type","taxiway"]],"layout":{},"paint":{"line-color":{"base":1,"stops":[[15,"hsl(230, 23%, 82%)"],[16,"hsl(230, 37%, 84%)"]]},"line-width":{"base":1.5,"stops":[[10,0.5],[18,20]]}}},{"id":"building-line","type":"line","source":"composite","source-layer":"building","minzoom":15,"filter":["==","underground","false"],"layout":{},"paint":{"line-color":"hsl(230, 24%, 87%)","line-width":{"base":1.5,"stops":[[15,0.75],[20,3]]},"line-opacity":{"base":1,"stops":[[15.5,0],[16,1]]}}},{"id":"building","type":"fill","source":"composite","source-layer":"building","minzoom":15,"filter":["==","underground","false"],"layout":{},"paint":{"fill-color":{"base":1,"stops":[[15,"hsl(35, 11%, 88%)"],[16,"hsl(35, 8%, 85%)"]]},"fill-opacity":{"base":1,"stops":[[15.5,0],[16,1]]},"fill-outline-color":"hsl(35, 6%, 79%)"}},{"id":"tunnel-street-low","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":11,"filter":["all",["==","class","street"],["==","structure","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"stops":[[11.5,0],[12,1],[14,1],[14.01,0]]}}},{"id":"tunnel-street_limited-low","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":11,"filter":["all",["==","class","street_limited"],["==","structure","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"stops":[[11.5,0],[12,1],[14,1],[14.01,0]]}}},{"id":"tunnel-service-link-track-case","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":14,"filter":["all",["in","class","link","service","track"],["==","structure","tunnel"],["!=","type","trunk_link"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(230, 19%, 75%)","line-gap-width":{"base":1.5,"stops":[[14,0.5],[18,12]]},"line-dasharray":[3,3]}},{"id":"tunnel-street_limited-case","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-street_limited-low","paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(230, 19%, 75%)","line-gap-width":{"base":1.5,"stops":[[13,0],[14,2],[18,18]]},"line-dasharray":[3,3],"line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"tunnel-street-case","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-street-low","paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(230, 19%, 75%)","line-gap-width":{"base":1.5,"stops":[[13,0],[14,2],[18,18]]},"line-dasharray":[3,3],"line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"tunnel-secondary-tertiary-case","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","filter":["all",["in","class","secondary","tertiary"],["==","structure","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.2,"stops":[[10,0.75],[18,2]]},"line-dasharray":[3,3],"line-gap-width":{"base":1.5,"stops":[[8.5,0.5],[10,0.75],[18,26]]},"line-color":"hsl(230, 19%, 75%)"}},{"id":"tunnel-primary-case","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","filter":["all",["==","class","primary"],["==","structure","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-dasharray":[3,3],"line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":"hsl(230, 19%, 75%)"}},{"id":"tunnel-trunk_link-case","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["==","structure","tunnel"],["==","type","trunk_link"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-dasharray":[3,3]}},{"id":"tunnel-motorway_link-case","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["==","class","motorway_link"],["==","structure","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-dasharray":[3,3]}},{"id":"tunnel-trunk-case","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","filter":["all",["==","structure","tunnel"],["==","type","trunk"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-opacity":1,"line-dasharray":[3,3]}},{"id":"tunnel-motorway-case","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","filter":["all",["==","class","motorway"],["==","structure","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-opacity":1,"line-dasharray":[3,3]}},{"id":"tunnel-construction","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":14,"filter":["all",["==","class","construction"],["==","structure","tunnel"]],"layout":{"line-join":"miter"},"paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(230, 24%, 87%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]},"line-dasharray":{"base":1,"stops":[[14,[0.4,0.8]],[15,[0.3,0.6]],[16,[0.2,0.3]],[17,[0.2,0.25]],[18,[0.15,0.15]]]}}},{"id":"tunnel-path","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","filter":["all",["==","class","path"],["==","structure","tunnel"],["!=","type","steps"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[15,1],[18,4]]},"line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.75,1]],[16,[1,0.75]],[17,[1,0.5]]]},"line-color":"hsl(35, 26%, 95%)","line-opacity":{"base":1,"stops":[[14,0],[14.25,1]]}}},{"id":"tunnel-steps","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","filter":["all",["==","$type","LineString"],["all",["==","structure","tunnel"],["==","type","steps"]]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[15,1],[16,1.6],[18,6]]},"line-color":"hsl(35, 26%, 95%)","line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.75,1]],[16,[1,0.75]],[17,[0.3,0.3]]]},"line-opacity":{"base":1,"stops":[[14,0],[14.25,1]]}}},{"id":"tunnel-trunk_link","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-trunk_link-case","paint":{"line-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-color":"hsl(46, 77%, 78%)","line-opacity":1,"line-dasharray":[1,0]}},{"id":"tunnel-motorway_link","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-motorway_link-case","paint":{"line-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-color":"hsl(26, 100%, 78%)","line-opacity":1,"line-dasharray":[1,0]}},{"id":"tunnel-pedestrian","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["==","$type","LineString"],["all",["==","class","pedestrian"],["==","structure","tunnel"]]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[14,0.5],[18,12]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":1,"line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.5,0.4]],[16,[1,0.2]]]}}},{"id":"tunnel-service-link-track","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-service-link-track-case","paint":{"line-width":{"base":1.5,"stops":[[14,0.5],[18,12]]},"line-color":"hsl(0, 0%, 100%)","line-dasharray":[1,0]}},{"id":"tunnel-street_limited","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-street_limited-low","paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(35, 14%, 93%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"tunnel-street","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-street-low","paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"tunnel-secondary-tertiary","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-secondary-tertiary-case","paint":{"line-width":{"base":1.5,"stops":[[8.5,0.5],[10,0.75],[18,26]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":1,"line-dasharray":[1,0],"line-blur":0}},{"id":"tunnel-primary","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-primary-case","paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":1,"line-dasharray":[1,0],"line-blur":0}},{"id":"tunnel-oneway-arrows-blue-minor","type":"symbol","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":16,"filter":["all",["in","class","link","path","pedestrian","service","track"],["==","oneway","true"],["==","structure","tunnel"],["!=","type","trunk_link"]],"layout":{"symbol-placement":"line","icon-image":{"base":1,"stops":[[17,"oneway-small"],[18,"oneway-large"]]},"symbol-spacing":200,"icon-padding":2},"paint":{}},{"id":"tunnel-oneway-arrows-blue-major","type":"symbol","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":15,"filter":["all",["in","class","primary","secondary","street","street_limited","tertiary"],["==","oneway","true"],["==","structure","tunnel"],["!=","type","trunk_link"]],"layout":{"symbol-placement":"line","icon-image":{"base":1,"stops":[[16,"oneway-small"],[17,"oneway-large"]]},"symbol-spacing":200,"icon-padding":2},"paint":{}},{"id":"tunnel-trunk","type":"line","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","filter":["all",["==","class","trunk"],["==","structure","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":"hsl(46, 77%, 78%)"}},{"id":"tunnel-motorway","metadata":{"mapbox:group":"1444855769305.6016"},"ref":"tunnel-motorway-case","paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-dasharray":[1,0],"line-opacity":1,"line-color":"hsl(26, 100%, 78%)","line-blur":0}},{"id":"tunnel-oneway-arrows-white","type":"symbol","metadata":{"mapbox:group":"1444855769305.6016"},"source":"composite","source-layer":"road","minzoom":16,"filter":["all",["in","class","link","motorway","motorway_link","trunk"],["==","oneway","true"],["==","structure","tunnel"],["!in","type","primary_link","secondary_link","tertiary_link"]],"layout":{"symbol-placement":"line","icon-image":{"base":1,"stops":[[16,"oneway-white-small"],[17,"oneway-white-large"]]},"symbol-spacing":200,"icon-padding":2},"paint":{}},{"id":"ferry","type":"line","source":"composite","source-layer":"road","filter":["==","type","ferry"],"layout":{"line-join":"round"},"paint":{"line-color":{"base":1,"stops":[[15,"hsl(205, 73%, 63%)"],[17,"hsl(230, 73%, 63%)"]]},"line-opacity":1,"line-width":{"base":1.5,"stops":[[14,0.5],[20,1]]},"line-dasharray":{"base":1,"stops":[[12,[1,0]],[13,[12,4]]]}}},{"id":"ferry_auto","type":"line","source":"composite","source-layer":"road","filter":["==","type","ferry_auto"],"layout":{"line-join":"round"},"paint":{"line-color":{"base":1,"stops":[[15,"hsl(205, 73%, 63%)"],[17,"hsl(230, 73%, 63%)"]]},"line-opacity":1,"line-width":{"base":1.5,"stops":[[14,0.5],[20,1]]}}},{"id":"road-path-bg","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","filter":["all",["==","$type","LineString"],["all",["==","class","path"],["!in","structure","bridge","tunnel"],["!in","type","crossing","sidewalk","steps"]]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[15,2],[18,7]]},"line-dasharray":[1,0],"line-color":"hsl(230, 17%, 82%)","line-blur":0,"line-opacity":{"base":1,"stops":[[14,0],[14.25,0.75]]}}},{"id":"road-steps-bg","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","filter":["all",["==","$type","LineString"],["all",["!in","structure","bridge","tunnel"],["==","type","steps"]]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[15,2],[17,4.6],[18,7]]},"line-color":"hsl(230, 17%, 82%)","line-dasharray":[1,0],"line-opacity":{"base":1,"stops":[[14,0],[14.25,0.75]]}}},{"id":"road-sidewalk-bg","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":16,"filter":["all",["==","$type","LineString"],["all",["!in","structure","bridge","tunnel"],["in","type","crossing","sidewalk"]]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[15,2],[18,7]]},"line-dasharray":[1,0],"line-color":"hsl(230, 17%, 82%)","line-blur":0,"line-opacity":{"base":1,"stops":[[16,0],[16.25,0.75]]}}},{"id":"turning-features-outline","type":"symbol","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":15,"filter":["in","class","turning_circle","turning_loop"],"layout":{"icon-image":"turning-circle-outline","icon-size":{"base":1.5,"stops":[[14,0.122],[18,0.969],[20,1]]},"icon-allow-overlap":true,"icon-ignore-placement":true,"icon-padding":0,"icon-rotation-alignment":"map"},"paint":{}},{"id":"road-pedestrian-case","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":12,"filter":["all",["==","$type","LineString"],["all",["==","class","pedestrian"],["==","structure","none"]]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[14,2],[18,14.5]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":0,"line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"road-street-low","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":11,"filter":["all",["==","$type","LineString"],["all",["==","class","street"],["==","structure","none"]]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"stops":[[11,0],[11.25,1],[14,1],[14.01,0]]}}},{"id":"road-street_limited-low","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":11,"filter":["all",["==","$type","LineString"],["all",["==","class","street_limited"],["==","structure","none"]]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"stops":[[11,0],[11.25,1],[14,1],[14.01,0]]}}},{"id":"road-service-link-track-case","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":14,"filter":["all",["in","class","link","service","track"],["!in","structure","bridge","tunnel"],["!=","type","trunk_link"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":{"base":1.5,"stops":[[14,0.5],[18,12]]}}},{"id":"road-street_limited-case","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-street_limited-low","paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":{"base":1.5,"stops":[[13,0],[14,2],[18,18]]},"line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"road-street-case","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-street-low","paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":{"base":1.5,"stops":[[13,0],[14,2],[18,18]]},"line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"road-secondary-tertiary-case","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","filter":["all",["in","class","secondary","tertiary"],["!in","structure","bridge","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.2,"stops":[[10,0.75],[18,2]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":{"base":1.5,"stops":[[8.5,0.5],[10,0.75],[18,26]]},"line-opacity":{"base":1,"stops":[[9.99,0],[10,1]]}}},{"id":"road-primary-case","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","filter":["all",["==","class","primary"],["!in","structure","bridge","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-opacity":{"base":1,"stops":[[9.99,0],[10,1]]}}},{"id":"road-motorway_link-case","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":10,"filter":["all",["==","class","motorway_link"],["!in","structure","bridge","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-opacity":{"base":1,"stops":[[10.99,0],[11,1]]}}},{"id":"road-trunk_link-case","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":11,"filter":["all",["!in","structure","bridge","tunnel"],["==","type","trunk_link"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-opacity":{"base":1,"stops":[[10.99,0],[11,1]]}}},{"id":"road-trunk-case","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","filter":["all",["==","class","trunk"],["!in","structure","bridge","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-opacity":{"base":1,"stops":[[6,0],[6.1,1]]}}},{"id":"road-motorway-case","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","filter":["all",["==","class","motorway"],["!in","structure","bridge","tunnel"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]}}},{"id":"road-construction","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":14,"filter":["all",["==","$type","LineString"],["all",["==","class","construction"],["==","structure","none"]]],"layout":{"line-join":"miter"},"paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(230, 24%, 87%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]},"line-dasharray":{"base":1,"stops":[[14,[0.4,0.8]],[15,[0.3,0.6]],[16,[0.2,0.3]],[17,[0.2,0.25]],[18,[0.15,0.15]]]}}},{"id":"road-sidewalks","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-sidewalk-bg","paint":{"line-width":{"base":1.5,"stops":[[15,1],[18,4]]},"line-color":"hsl(0, 0%, 100%)","line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.75,1]],[16,[1,0.75]],[17,[1,0.5]]]},"line-opacity":{"base":1,"stops":[[16,0],[16.25,1]]}}},{"id":"road-path","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-path-bg","paint":{"line-width":{"base":1.5,"stops":[[15,1],[18,4]]},"line-color":"hsl(0, 0%, 100%)","line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.75,1]],[16,[1,0.75]],[17,[1,0.5]]]},"line-opacity":{"base":1,"stops":[[14,0],[14.25,1]]}}},{"id":"road-steps","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-steps-bg","paint":{"line-width":{"base":1.5,"stops":[[15,1],[16,1.6],[18,6]]},"line-color":"hsl(0, 0%, 100%)","line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.75,1]],[16,[1,0.75]],[17,[0.3,0.3]]]},"line-opacity":{"base":1,"stops":[[14,0],[14.25,1]]}}},{"id":"road-trunk_link","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-trunk_link-case","paint":{"line-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-color":"hsl(46, 85%, 67%)","line-opacity":1}},{"id":"road-motorway_link","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-motorway_link-case","paint":{"line-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-color":"hsl(26, 100%, 68%)","line-opacity":1}},{"id":"road-pedestrian","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-pedestrian-case","paint":{"line-width":{"base":1.5,"stops":[[14,0.5],[18,12]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":1,"line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.5,0.4]],[16,[1,0.2]]]}}},{"id":"road-pedestrian-polygon-fill","type":"fill","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":12,"filter":["all",["==","$type","Polygon"],["all",["in","class","path","pedestrian"],["==","structure","none"]]],"layout":{},"paint":{"fill-color":{"base":1,"stops":[[16,"hsl(230, 16%, 94%)"],[16.25,"hsl(230, 50%, 98%)"]]},"fill-outline-color":"hsl(230, 26%, 88%)","fill-opacity":1}},{"id":"road-pedestrian-polygon-pattern","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-pedestrian-polygon-fill","paint":{"fill-color":"hsl(0, 0%, 100%)","fill-outline-color":"hsl(35, 10%, 83%)","fill-pattern":"pedestrian-polygon","fill-opacity":{"base":1,"stops":[[16,0],[16.25,1]]}}},{"id":"road-service-link-track","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-service-link-track-case","paint":{"line-width":{"base":1.5,"stops":[[14,0.5],[18,12]]},"line-color":"hsl(0, 0%, 100%)"}},{"id":"road-street_limited","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-street_limited-low","paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(35, 14%, 93%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"road-street","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-street-low","paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"road-secondary-tertiary","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-secondary-tertiary-case","paint":{"line-width":{"base":1.5,"stops":[[8.5,0.5],[10,0.75],[18,26]]},"line-color":{"base":1,"stops":[[5,"hsl(35, 32%, 91%)"],[8,"hsl(0, 0%, 100%)"]]},"line-opacity":{"base":1.2,"stops":[[5,0],[5.5,1]]}}},{"id":"road-primary","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-primary-case","paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":{"base":1,"stops":[[5,"hsl(35, 32%, 91%)"],[7,"hsl(0, 0%, 100%)"]]},"line-opacity":1}},{"id":"road-oneway-arrows-blue-minor","type":"symbol","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":16,"filter":["all",["in","class","link","path","pedestrian","service","track"],["==","oneway","true"],["!in","structure","bridge","tunnel"],["!=","type","trunk_link"]],"layout":{"symbol-placement":"line","icon-image":{"base":1,"stops":[[17,"oneway-small"],[18,"oneway-large"]]},"icon-rotation-alignment":"map","icon-padding":2,"symbol-spacing":200},"paint":{}},{"id":"road-oneway-arrows-blue-major","type":"symbol","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":15,"filter":["all",["in","class","primary","secondary","street","street_limited","tertiary"],["==","oneway","true"],["!in","structure","bridge","tunnel"],["!=","type","trunk_link"]],"layout":{"symbol-placement":"line","icon-image":{"base":1,"stops":[[16,"oneway-small"],[17,"oneway-large"]]},"icon-rotation-alignment":"map","icon-padding":2,"symbol-spacing":200},"paint":{}},{"id":"road-trunk","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-trunk-case","paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":{"base":1,"stops":[[6,"hsl(0, 0%, 100%)"],[6.1,"hsl(46, 80%, 60%)"],[9,"hsl(46, 85%, 67%)"]]}}},{"id":"road-motorway","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-motorway-case","paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":{"base":1,"stops":[[8,"hsl(26, 87%, 62%)"],[9,"hsl(26, 100%, 68%)"]]}}},{"id":"road-rail","type":"line","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["in","class","major_rail","minor_rail"],["!in","structure","bridge","tunnel"]],"layout":{"line-join":"round"},"paint":{"line-color":{"stops":[[13,"hsl(50, 17%, 82%)"],[16,"hsl(230, 10%, 74%)"]]},"line-width":{"base":1.5,"stops":[[14,0.5],[20,1]]}}},{"id":"road-rail-tracks","metadata":{"mapbox:group":"1444855786460.0557"},"ref":"road-rail","paint":{"line-color":{"stops":[[13,"hsl(50, 17%, 82%)"],[16,"hsl(230, 10%, 74%)"]]},"line-width":{"base":1.5,"stops":[[14,4],[20,8]]},"line-dasharray":[0.1,15],"line-opacity":{"base":1,"stops":[[13.75,0],[14,1]]}}},{"id":"road-oneway-arrows-white","type":"symbol","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":16,"filter":["all",["in","class","link","motorway","motorway_link","trunk"],["==","oneway","true"],["!in","structure","bridge","tunnel"],["!in","type","primary_link","secondary_link","tertiary_link"]],"layout":{"symbol-placement":"line","icon-image":{"base":1,"stops":[[16,"oneway-white-small"],[17,"oneway-white-large"]]},"icon-padding":2,"symbol-spacing":200},"paint":{}},{"id":"turning-features","type":"symbol","metadata":{"mapbox:group":"1444855786460.0557"},"source":"composite","source-layer":"road","minzoom":15,"filter":["in","class","turning_circle","turning_loop"],"layout":{"icon-image":"turning-circle","icon-size":{"base":1.5,"stops":[[14,0.095],[18,1]]},"icon-allow-overlap":true,"icon-ignore-placement":true,"icon-padding":0,"icon-rotation-alignment":"map"},"paint":{}},{"id":"bridge-path-bg","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","path"],["==","structure","bridge"],["!=","type","steps"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[15,2],[18,7]]},"line-dasharray":[1,0],"line-color":"hsl(230, 17%, 82%)","line-blur":0,"line-opacity":{"base":1,"stops":[[15,0],[15.25,1]]}}},{"id":"bridge-steps-bg","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","$type","LineString"],["all",["==","structure","bridge"],["==","type","steps"]]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[15,2],[17,4.6],[18,7]]},"line-color":"hsl(230, 17%, 82%)","line-dasharray":[1,0],"line-opacity":{"base":1,"stops":[[14,0],[14.25,0.75]]}}},{"id":"bridge-pedestrian-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["==","$type","LineString"],["all",["==","class","pedestrian"],["==","structure","bridge"]]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[14,2],[18,14.5]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":0,"line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"bridge-street-low","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":11,"filter":["all",["==","class","street"],["==","structure","bridge"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"stops":[[11.5,0],[12,1],[14,1],[14.01,0]]}}},{"id":"bridge-street_limited-low","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":11,"filter":["all",["==","class","street_limited"],["==","structure","bridge"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"stops":[[11.5,0],[12,1],[14,1],[14.01,0]]}}},{"id":"bridge-service-link-track-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":14,"filter":["all",["in","class","link","service","track"],["==","structure","bridge"],["!=","type","trunk_link"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":{"base":1.5,"stops":[[14,0.5],[18,12]]}}},{"id":"bridge-street_limited-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":11,"filter":["all",["==","class","street_limited"],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":{"base":1.5,"stops":[[13,0],[14,2],[18,18]]}}},{"id":"bridge-street-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":11,"filter":["all",["==","class","street"],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(230, 24%, 87%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]},"line-gap-width":{"base":1.5,"stops":[[13,0],[14,2],[18,18]]}}},{"id":"bridge-secondary-tertiary-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["in","class","secondary","tertiary"],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.2,"stops":[[10,0.75],[18,2]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":{"base":1.5,"stops":[[8.5,0.5],[10,0.75],[18,26]]},"line-translate":[0,0]}},{"id":"bridge-primary-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","primary"],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(230, 24%, 87%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-translate":[0,0]}},{"id":"bridge-trunk_link-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["!in","layer",2,3,4,5],["==","structure","bridge"],["==","type","trunk_link"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-opacity":{"base":1,"stops":[[10.99,0],[11,1]]}}},{"id":"bridge-motorway_link-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["==","class","motorway_link"],["!in","layer",2,3,4,5],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-opacity":1}},{"id":"bridge-trunk-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","trunk"],["!in","layer",2,3,4,5],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]}}},{"id":"bridge-motorway-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","motorway"],["!in","layer",2,3,4,5],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]}}},{"id":"bridge-construction","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":14,"filter":["all",["==","class","construction"],["==","structure","bridge"]],"layout":{"line-join":"miter"},"paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(230, 24%, 87%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]},"line-dasharray":{"base":1,"stops":[[14,[0.4,0.8]],[15,[0.3,0.6]],[16,[0.2,0.3]],[17,[0.2,0.25]],[18,[0.15,0.15]]]}}},{"id":"bridge-path","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","path"],["==","structure","bridge"],["!=","type","steps"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[15,1],[18,4]]},"line-color":"hsl(0, 0%, 100%)","line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.75,1]],[16,[1,0.75]],[17,[1,0.5]]]},"line-opacity":{"base":1,"stops":[[14,0],[14.25,1]]}}},{"id":"bridge-steps","metadata":{"mapbox:group":"1444855799204.86"},"ref":"bridge-steps-bg","paint":{"line-width":{"base":1.5,"stops":[[15,1],[16,1.6],[18,6]]},"line-color":"hsl(0, 0%, 100%)","line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.75,1]],[16,[1,0.75]],[17,[0.3,0.3]]]},"line-opacity":{"base":1,"stops":[[14,0],[14.25,1]]}}},{"id":"bridge-trunk_link","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["!in","layer",2,3,4,5],["==","structure","bridge"],["==","type","trunk_link"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-color":"hsl(46, 85%, 67%)"}},{"id":"bridge-motorway_link","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["==","class","motorway_link"],["!in","layer",2,3,4,5],["==","structure","bridge"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-color":"hsl(26, 100%, 68%)"}},{"id":"bridge-pedestrian","metadata":{"mapbox:group":"1444855799204.86"},"ref":"bridge-pedestrian-case","paint":{"line-width":{"base":1.5,"stops":[[14,0.5],[18,12]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":1,"line-dasharray":{"base":1,"stops":[[14,[1,0]],[15,[1.5,0.4]],[16,[1,0.2]]]}}},{"id":"bridge-service-link-track","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":14,"filter":["all",["in","class","link","service","track"],["==","structure","bridge"],["!=","type","trunk_link"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[14,0.5],[18,12]]},"line-color":"hsl(0, 0%, 100%)"}},{"id":"bridge-street_limited","metadata":{"mapbox:group":"1444855799204.86"},"ref":"bridge-street_limited-low","paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(35, 14%, 93%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"bridge-street","metadata":{"mapbox:group":"1444855799204.86"},"ref":"bridge-street-low","paint":{"line-width":{"base":1.5,"stops":[[12.5,0.5],[14,2],[18,18]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"base":1,"stops":[[13.99,0],[14,1]]}}},{"id":"bridge-secondary-tertiary","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","structure","bridge"],["in","type","secondary","tertiary"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[8.5,0.5],[10,0.75],[18,26]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":{"base":1.2,"stops":[[5,0],[5.5,1]]}}},{"id":"bridge-primary","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","structure","bridge"],["==","type","primary"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":"hsl(0, 0%, 100%)","line-opacity":1}},{"id":"bridge-oneway-arrows-blue-minor","type":"symbol","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":16,"filter":["all",["in","class","link","path","pedestrian","service","track"],["==","oneway","true"],["==","structure","bridge"]],"layout":{"symbol-placement":"line","icon-image":{"base":1,"stops":[[17,"oneway-small"],[18,"oneway-large"]]},"symbol-spacing":200,"icon-rotation-alignment":"map","icon-padding":2},"paint":{}},{"id":"bridge-oneway-arrows-blue-major","type":"symbol","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":15,"filter":["all",["in","class","primary","secondary","street","street_limited","tertiary"],["==","oneway","true"],["==","structure","bridge"]],"layout":{"symbol-placement":"line","icon-image":{"base":1,"stops":[[16,"oneway-small"],[17,"oneway-large"]]},"symbol-spacing":200,"icon-rotation-alignment":"map","icon-padding":2},"paint":{}},{"id":"bridge-trunk","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","trunk"],["!in","layer",2,3,4,5],["==","structure","bridge"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":"hsl(46, 85%, 67%)"}},{"id":"bridge-motorway","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","motorway"],["!in","layer",2,3,4,5],["==","structure","bridge"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":"hsl(26, 100%, 68%)"}},{"id":"bridge-rail","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["in","class","major_rail","minor_rail"],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-color":{"stops":[[13,"hsl(50, 17%, 82%)"],[16,"hsl(230, 10%, 74%)"]]},"line-width":{"base":1.5,"stops":[[14,0.5],[20,1]]}}},{"id":"bridge-rail-tracks","metadata":{"mapbox:group":"1444855799204.86"},"ref":"bridge-rail","paint":{"line-color":{"stops":[[13,"hsl(50, 17%, 82%)"],[16,"hsl(230, 10%, 74%)"]]},"line-width":{"base":1.5,"stops":[[14,4],[20,8]]},"line-dasharray":[0.1,15],"line-opacity":{"base":1,"stops":[[13.75,0],[20,1]]}}},{"id":"bridge-trunk_link-2-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",[">=","layer",2],["==","structure","bridge"],["==","type","trunk_link"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-opacity":{"base":1,"stops":[[10.99,0],[11,1]]}}},{"id":"bridge-motorway_link-2-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["==","class","motorway_link"],[">=","layer",2],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.75],[20,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-opacity":1}},{"id":"bridge-trunk-2-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","trunk"],[">=","layer",2],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]}}},{"id":"bridge-motorway-2-case","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","motorway"],[">=","layer",2],["==","structure","bridge"]],"layout":{"line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[10,1],[16,2]]},"line-color":"hsl(0, 0%, 100%)","line-gap-width":{"base":1.5,"stops":[[5,0.75],[18,32]]}}},{"id":"bridge-trunk_link-2","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",[">=","layer",2],["==","structure","bridge"],["==","type","trunk_link"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-color":"hsl(46, 85%, 67%)"}},{"id":"bridge-motorway_link-2","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":13,"filter":["all",["==","class","motorway_link"],[">=","layer",2],["==","structure","bridge"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[12,0.5],[14,2],[18,18]]},"line-color":"hsl(26, 100%, 68%)"}},{"id":"bridge-trunk-2","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","trunk"],[">=","layer",2],["==","structure","bridge"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":"hsl(46, 85%, 67%)"}},{"id":"bridge-motorway-2","type":"line","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","filter":["all",["==","class","motorway"],[">=","layer",2],["==","structure","bridge"]],"layout":{"line-cap":"round","line-join":"round"},"paint":{"line-width":{"base":1.5,"stops":[[5,0.75],[18,32]]},"line-color":"hsl(26, 100%, 68%)"}},{"id":"bridge-oneway-arrows-white","type":"symbol","metadata":{"mapbox:group":"1444855799204.86"},"source":"composite","source-layer":"road","minzoom":16,"filter":["all",["in","class","link","motorway","motorway_link","trunk"],["==","oneway","true"],["==","structure","bridge"],["!in","type","primary_link","secondary_link","tertiary_link"]],"layout":{"symbol-placement":"line","icon-image":{"base":1,"stops":[[16,"oneway-white-small"],[17,"oneway-white-large"]]},"symbol-spacing":200,"icon-padding":2},"paint":{}},{"id":"aerialway","type":"line","source":"composite","source-layer":"road","minzoom":13,"filter":["==","class","aerialway"],"layout":{"line-join":"round"},"paint":{"line-color":"hsl(230, 10%, 74%)","line-width":{"base":1.5,"stops":[[14,0.5],[20,1]]}}},{"id":"admin-3-4-boundaries-bg","type":"line","metadata":{"mapbox:group":"1444934295202.7542"},"source":"composite","source-layer":"admin","filter":["all",[">=","admin_level",3],["==","maritime",0]],"layout":{"line-join":"bevel"},"paint":{"line-color":{"base":1,"stops":[[8,"hsl(35, 12%, 89%)"],[16,"hsl(230, 49%, 90%)"]]},"line-width":{"base":1,"stops":[[7,3.75],[12,5.5]]},"line-opacity":{"base":1,"stops":[[7,0],[8,0.75]]},"line-dasharray":[1,0],"line-translate":[0,0],"line-blur":{"base":1,"stops":[[3,0],[8,3]]}}},{"id":"admin-2-boundaries-bg","type":"line","metadata":{"mapbox:group":"1444934295202.7542"},"source":"composite","source-layer":"admin","minzoom":1,"filter":["all",["==","admin_level",2],["==","maritime",0]],"layout":{"line-join":"miter"},"paint":{"line-width":{"base":1,"stops":[[3,3.5],[10,8]]},"line-color":{"base":1,"stops":[[6,"hsl(35, 12%, 89%)"],[8,"hsl(230, 49%, 90%)"]]},"line-opacity":{"base":1,"stops":[[3,0],[4,0.5]]},"line-translate":[0,0],"line-blur":{"base":1,"stops":[[3,0],[10,2]]}}},{"id":"admin-3-4-boundaries","type":"line","metadata":{"mapbox:group":"1444934295202.7542"},"source":"composite","source-layer":"admin","filter":["all",[">=","admin_level",3],["==","maritime",0]],"layout":{"line-join":"round","line-cap":"round"},"paint":{"line-dasharray":{"base":1,"stops":[[6,[2,0]],[7,[2,2,6,2]]]},"line-width":{"base":1,"stops":[[7,0.75],[12,1.5]]},"line-opacity":{"base":1,"stops":[[2,0],[3,1]]},"line-color":{"base":1,"stops":[[3,"hsl(230, 14%, 77%)"],[7,"hsl(230, 8%, 62%)"]]}}},{"id":"admin-2-boundaries","type":"line","metadata":{"mapbox:group":"1444934295202.7542"},"source":"composite","source-layer":"admin","minzoom":1,"filter":["all",["==","admin_level",2],["==","disputed",0],["==","maritime",0]],"layout":{"line-join":"round","line-cap":"round"},"paint":{"line-color":"hsl(230, 8%, 51%)","line-width":{"base":1,"stops":[[3,0.5],[10,2]]}}},{"id":"admin-2-boundaries-dispute","type":"line","metadata":{"mapbox:group":"1444934295202.7542"},"source":"composite","source-layer":"admin","minzoom":1,"filter":["all",["==","admin_level",2],["==","disputed",1],["==","maritime",0]],"layout":{"line-join":"round"},"paint":{"line-dasharray":[1.5,1.5],"line-color":"hsl(230, 8%, 51%)","line-width":{"base":1,"stops":[[3,0.5],[10,2]]}}},{"id":"housenum-label","type":"symbol","source":"composite","source-layer":"housenum_label","minzoom":17,"layout":{"text-field":"{house_num}","text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"text-padding":4,"text-max-width":7,"text-size":9.5},"paint":{"text-color":"hsl(35, 2%, 69%)","text-halo-color":"hsl(35, 8%, 85%)","text-halo-width":0.5,"text-halo-blur":0}},{"id":"waterway-label","type":"symbol","source":"composite","source-layer":"waterway_label","minzoom":12,"filter":["in","class","canal","river"],"layout":{"text-field":"{name_en}","text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"symbol-placement":"line","text-max-angle":30,"text-size":{"base":1,"stops":[[13,12],[18,16]]}},"paint":{"text-halo-width":0.5,"text-halo-color":"hsl(196, 80%, 70%)","text-color":"hsl(230, 48%, 44%)","text-halo-blur":0.5}},{"id":"poi-scalerank4-l15","type":"symbol","metadata":{"mapbox:group":"1444933456003.5437"},"source":"composite","source-layer":"poi_label","minzoom":17,"filter":["all",[">=","localrank",15],["!in","maki","campsite","cemetery","dog-park","garden","golf","park","picnic-site","playground","zoo"],["==","scalerank",4]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[16,11],[20,13]]},"icon-image":"{maki}-11","text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":2,"text-offset":[0,0.65],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(26, 25%, 32%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"poi-scalerank4-l1","type":"symbol","metadata":{"mapbox:group":"1444933456003.5437"},"source":"composite","source-layer":"poi_label","minzoom":15,"filter":["all",["<=","localrank",14],["!in","maki","campsite","cemetery","dog-park","garden","golf","park","picnic-site","playground","zoo"],["==","scalerank",4]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[16,11],[20,13]]},"icon-image":"{maki}-11","text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":1,"text-offset":[0,0.65],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(26, 25%, 32%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"poi-parks_scalerank4","type":"symbol","metadata":{"mapbox:group":"1444933456003.5437"},"source":"composite","source-layer":"poi_label","minzoom":15,"filter":["all",["in","maki","campsite","cemetery","dog-park","garden","golf","park","picnic-site","playground","zoo"],["==","scalerank",4]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[16,11],[20,13]]},"icon-image":"{maki}-11","text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":1,"text-offset":[0,0.65],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(100, 100%, 20%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"poi-scalerank3","type":"symbol","metadata":{"mapbox:group":"1444933372896.5967"},"source":"composite","source-layer":"poi_label","filter":["all",["!in","maki","campsite","cemetery","dog-park","garden","golf","park","picnic-site","playground","zoo"],["==","scalerank",3]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[16,11],[20,13]]},"icon-image":"{maki}-11","text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":1,"text-offset":[0,0.65],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(26, 25%, 32%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"poi-parks-scalerank3","type":"symbol","metadata":{"mapbox:group":"1444933372896.5967"},"source":"composite","source-layer":"poi_label","filter":["all",["in","maki","campsite","cemetery","dog-park","garden","golf","park","picnic-site","playground","zoo"],["==","scalerank",3]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[16,11],[20,13]]},"icon-image":"{maki}-11","text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":2,"text-offset":[0,0.65],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(100, 100%, 20%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"road-label-small","type":"symbol","metadata":{"mapbox:group":"1444933721429.3076"},"source":"composite","source-layer":"road_label","minzoom":15,"filter":["all",["==","$type","LineString"],["!in","class","link","motorway","pedestrian","primary","secondary","street","street_limited","tertiary","trunk"]],"layout":{"text-size":{"base":1,"stops":[[15,10],[20,13]]},"text-max-angle":30,"symbol-spacing":250,"text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"symbol-placement":"line","text-padding":1,"text-rotation-alignment":"map","text-field":"{name_en}","text-letter-spacing":0.01},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1.25,"text-halo-blur":1}},{"id":"road-label-medium","type":"symbol","metadata":{"mapbox:group":"1444933721429.3076"},"source":"composite","source-layer":"road_label","minzoom":11,"filter":["all",["==","$type","LineString"],["in","class","link","pedestrian","street","street_limited"]],"layout":{"text-size":{"base":1,"stops":[[11,10],[20,14]]},"text-max-angle":30,"symbol-spacing":250,"text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"symbol-placement":"line","text-padding":1,"text-rotation-alignment":"map","text-field":"{name_en}","text-letter-spacing":0.01},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1}},{"id":"road-label-large","type":"symbol","metadata":{"mapbox:group":"1444933721429.3076"},"source":"composite","source-layer":"road_label","filter":["in","class","motorway","primary","secondary","tertiary","trunk"],"layout":{"text-size":{"base":1,"stops":[[9,10],[20,16]]},"text-max-angle":30,"symbol-spacing":250,"text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"symbol-placement":"line","text-padding":1,"text-rotation-alignment":"map","text-field":"{name_en}","text-letter-spacing":0.01},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsla(0, 0%, 100%, 0.75)","text-halo-width":1,"text-halo-blur":1}},{"id":"road-shields-black","type":"symbol","metadata":{"mapbox:group":"1444933575858.6992"},"source":"composite","source-layer":"road_label","filter":["all",["<=","reflen",6],["!in","shield","at-expressway","at-motorway","at-state-b","bg-motorway","bg-national","ch-main","ch-motorway","cz-motorway","cz-road","de-motorway","e-road","fi-main","gr-motorway","gr-national","hr-motorway","hr-state","hu-main","hu-motorway","nz-state","pl-expressway","pl-motorway","pl-national","ro-county","ro-motorway","ro-national","rs-motorway","rs-state-1b","se-main","si-expressway","si-motorway","sk-highway","sk-road","us-interstate","us-interstate-business","us-interstate-duplex","us-interstate-truck","za-metropolitan","za-national","za-provincial","za-regional"]],"layout":{"text-size":9,"icon-image":"{shield}-{reflen}","icon-rotation-alignment":"viewport","text-max-angle":38,"symbol-spacing":{"base":1,"stops":[[11,150],[14,200]]},"text-font":["DIN Offc Pro Bold","Arial Unicode MS Bold"],"symbol-placement":{"base":1,"stops":[[10,"point"],[11,"line"]]},"text-padding":2,"text-rotation-alignment":"viewport","text-field":"{ref}","text-letter-spacing":0.05,"icon-padding":2},"paint":{"text-color":"hsl(0, 0%, 7%)","icon-halo-color":"rgba(0, 0, 0, 1)","icon-halo-width":1,"text-opacity":1,"icon-color":"white","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0}},{"id":"road-shields-white","type":"symbol","metadata":{"mapbox:group":"1444933575858.6992"},"source":"composite","source-layer":"road_label","filter":["all",["<=","reflen",6],["in","shield","at-expressway","at-motorway","at-state-b","bg-motorway","bg-national","ch-main","ch-motorway","cz-motorway","cz-road","de-motorway","e-road","fi-main","gr-motorway","gr-national","hr-motorway","hr-state","hu-main","hu-motorway","nz-state","pl-expressway","pl-motorway","pl-national","ro-county","ro-motorway","ro-national","rs-motorway","rs-state-1b","se-main","si-expressway","si-motorway","sk-highway","sk-road","us-interstate","us-interstate-business","us-interstate-duplex","us-interstate-truck","za-metropolitan","za-national","za-provincial","za-regional"]],"layout":{"text-size":9,"icon-image":"{shield}-{reflen}","icon-rotation-alignment":"viewport","text-max-angle":38,"symbol-spacing":{"base":1,"stops":[[11,150],[14,200]]},"text-font":["DIN Offc Pro Bold","Arial Unicode MS Bold"],"symbol-placement":{"base":1,"stops":[[10,"point"],[11,"line"]]},"text-padding":2,"text-rotation-alignment":"viewport","text-field":"{ref}","text-letter-spacing":0.05,"icon-padding":2},"paint":{"text-color":"hsl(0, 0%, 100%)","icon-halo-color":"rgba(0, 0, 0, 1)","icon-halo-width":1,"text-opacity":1,"icon-color":"white","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0}},{"id":"motorway-junction","type":"symbol","metadata":{"mapbox:group":"1444933575858.6992"},"source":"composite","source-layer":"motorway_junction","minzoom":14,"filter":[">","reflen",0],"layout":{"text-field":"{ref}","text-size":9,"icon-image":"motorway-exit-{reflen}","text-font":["DIN Offc Pro Bold","Arial Unicode MS Bold"]},"paint":{"text-color":"hsl(0, 0%, 100%)","text-translate":[0,0]}},{"id":"poi-scalerank2","type":"symbol","metadata":{"mapbox:group":"1444933358918.2366"},"source":"composite","source-layer":"poi_label","filter":["all",["!in","maki","campsite","cemetery","dog-park","garden","golf","park","picnic-site","playground","zoo"],["==","scalerank",2]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[14,11],[20,14]]},"icon-image":{"stops":[[14,"{maki}-11"],[15,"{maki}-15"]]},"text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":2,"text-offset":[0,0.65],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(26, 25%, 32%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"poi-parks-scalerank2","type":"symbol","metadata":{"mapbox:group":"1444933358918.2366"},"source":"composite","source-layer":"poi_label","filter":["all",["in","maki","campsite","cemetery","dog-park","garden","golf","park","picnic-site","playground","zoo"],["==","scalerank",2]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[14,11],[20,14]]},"icon-image":{"stops":[[14,"{maki}-11"],[15,"{maki}-15"]]},"text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":2,"text-offset":[0,0.65],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(100, 100%, 20%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"rail-label","type":"symbol","source":"composite","source-layer":"rail_station_label","minzoom":12,"filter":["!=","maki","entrance"],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[16,11],[20,13]]},"icon-image":"{network}","symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-offset":[0,0.85],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":{"base":1,"stops":[[0,""],[13,"{name_en}"]]},"text-letter-spacing":0.01,"icon-padding":0,"text-max-width":7},"paint":{"text-color":"hsl(230, 48%, 44%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"icon-halo-width":4,"icon-halo-color":"#fff","text-opacity":{"base":1,"stops":[[13.99,0],[14,1]]},"text-halo-blur":0.5}},{"id":"water-label-sm","type":"symbol","metadata":{"mapbox:group":"1444933808272.805"},"source":"composite","source-layer":"water_label","minzoom":15,"filter":["<=","area",10000],"layout":{"text-field":"{name_en}","text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"text-max-width":7,"text-size":{"base":1,"stops":[[16,13],[20,16]]}},"paint":{"text-color":"hsl(230, 48%, 44%)"}},{"id":"water-label","type":"symbol","metadata":{"mapbox:group":"1444933808272.805"},"source":"composite","source-layer":"water_label","minzoom":5,"filter":[">","area",10000],"layout":{"text-field":"{name_en}","text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"text-max-width":7,"text-size":{"base":1,"stops":[[13,13],[18,18]]}},"paint":{"text-color":"hsl(230, 48%, 44%)"}},{"id":"place-residential","type":"symbol","source":"composite","source-layer":"place_label","minzoom":16,"maxzoom":18,"filter":["all",["<=","localrank",10],["==","type","residential"]],"layout":{"text-line-height":1.2,"text-size":{"base":1,"stops":[[10,11],[18,14]]},"text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"text-padding":2,"visibility":"none","text-offset":[0,0],"text-rotation-alignment":"viewport","text-field":"{name_en}","text-max-width":7},"paint":{"text-color":"hsl(26, 25%, 32%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1,"text-halo-blur":0.5}},{"id":"poi-parks-scalerank1","type":"symbol","metadata":{"mapbox:group":"1444933322393.2852"},"source":"composite","source-layer":"poi_label","filter":["all",["in","maki","campsite","cemetery","dog-park","garden","golf","park","picnic-site","playground","zoo"],["<=","scalerank",1]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[10,11],[18,14]]},"icon-image":{"stops":[[13,"{maki}-11"],[14,"{maki}-15"]]},"text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":2,"text-offset":[0,0.65],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(100, 100%, 20%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"poi-scalerank1","type":"symbol","metadata":{"mapbox:group":"1444933322393.2852"},"source":"composite","source-layer":"poi_label","filter":["all",["!in","maki","campsite","cemetery","dog-park","garden","golf","park","picnic-site","playground","zoo"],["<=","scalerank",1]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[10,11],[18,14]]},"icon-image":{"stops":[[13,"{maki}-11"],[14,"{maki}-15"]]},"text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":2,"text-offset":[0,0.65],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(26, 25%, 32%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"airport-label","type":"symbol","source":"composite","source-layer":"airport_label","minzoom":9,"filter":["<=","scalerank",2],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[10,12],[18,18]]},"icon-image":{"stops":[[12,"{maki}-11"],[13,"{maki}-15"]]},"symbol-spacing":250,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-padding":2,"text-offset":[0,0.75],"text-rotation-alignment":"viewport","text-anchor":"top","text-field":{"stops":[[11,"{ref}"],[12,"{name_en}"]]},"text-letter-spacing":0.01,"text-max-width":9},"paint":{"text-color":"hsl(230, 48%, 44%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":0.5,"text-halo-blur":0.5}},{"id":"place-islet-archipelago-aboriginal","type":"symbol","source":"composite","source-layer":"place_label","maxzoom":16,"filter":["in","type","aboriginal_lands","archipelago","islet"],"layout":{"text-line-height":1.2,"text-size":{"base":1,"stops":[[10,11],[18,16]]},"text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"text-padding":2,"text-offset":[0,0],"text-rotation-alignment":"viewport","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":8},"paint":{"text-color":"hsl(230, 29%, 35%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1}},{"id":"place-neighbourhood","type":"symbol","source":"composite","source-layer":"place_label","minzoom":10,"maxzoom":16,"filter":["==","type","neighbourhood"],"layout":{"text-field":"{name_en}","text-transform":"uppercase","text-letter-spacing":0.1,"text-max-width":7,"text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"text-padding":3,"text-size":{"base":1,"stops":[[12,11],[16,16]]}},"paint":{"text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1,"text-color":"hsl(230, 29%, 35%)","text-halo-blur":0.5}},{"id":"place-suburb","type":"symbol","source":"composite","source-layer":"place_label","minzoom":10,"maxzoom":16,"filter":["==","type","suburb"],"layout":{"text-field":"{name_en}","text-transform":"uppercase","text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"text-letter-spacing":0.15,"text-max-width":7,"text-padding":3,"text-size":{"base":1,"stops":[[11,11],[15,18]]}},"paint":{"text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1,"text-color":"hsl(230, 29%, 35%)","text-halo-blur":0.5}},{"id":"place-hamlet","type":"symbol","source":"composite","source-layer":"place_label","minzoom":10,"maxzoom":16,"filter":["==","type","hamlet"],"layout":{"text-field":"{name_en}","text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"text-size":{"base":1,"stops":[[12,11.5],[15,16]]}},"paint":{"text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1.25,"text-color":"hsl(0, 0%, 0%)"}},{"id":"place-village","type":"symbol","source":"composite","source-layer":"place_label","minzoom":8,"maxzoom":15,"filter":["==","type","village"],"layout":{"text-field":"{name_en}","text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"text-max-width":7,"text-size":{"base":1,"stops":[[10,11.5],[16,18]]}},"paint":{"text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1.25,"text-color":"hsl(0, 0%, 0%)"}},{"id":"place-town","type":"symbol","source":"composite","source-layer":"place_label","minzoom":6,"maxzoom":15,"filter":["==","type","town"],"layout":{"icon-image":"dot-9","text-font":{"base":1,"stops":[[11,["DIN Offc Pro Regular","Arial Unicode MS Regular"]],[12,["DIN Offc Pro Medium","Arial Unicode MS Regular"]]]},"text-offset":{"base":1,"stops":[[7,[0,-0.15]],[8,[0,0]]]},"text-anchor":{"base":1,"stops":[[7,"bottom"],[8,"center"]]},"text-field":"{name_en}","text-max-width":7,"text-size":{"base":1,"stops":[[7,11.5],[15,20]]}},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1.25,"icon-opacity":{"base":1,"stops":[[7.99,1],[8,0]]}}},{"id":"place-island","type":"symbol","source":"composite","source-layer":"place_label","maxzoom":16,"filter":["==","type","island"],"layout":{"text-line-height":1.2,"text-size":{"base":1,"stops":[[10,11],[18,16]]},"text-max-angle":38,"symbol-spacing":250,"text-font":["DIN Offc Pro Regular","Arial Unicode MS Regular"],"text-padding":2,"text-offset":[0,0],"text-rotation-alignment":"viewport","text-field":"{name_en}","text-letter-spacing":0.01,"text-max-width":7},"paint":{"text-color":"hsl(230, 29%, 35%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1}},{"id":"place-city-sm","type":"symbol","metadata":{"mapbox:group":"1444862510685.128"},"source":"composite","source-layer":"place_label","maxzoom":14,"filter":["all",["!in","scalerank",0,1,2,3,4,5],["==","type","city"]],"layout":{"text-size":{"base":1,"stops":[[6,12],[14,22]]},"icon-image":"dot-9","text-font":{"base":1,"stops":[[7,["DIN Offc Pro Regular","Arial Unicode MS Regular"]],[8,["DIN Offc Pro Medium","Arial Unicode MS Regular"]]]},"text-offset":{"base":1,"stops":[[7.99,[0,-0.2]],[8,[0,0]]]},"text-anchor":{"base":1,"stops":[[7,"bottom"],[8,"center"]]},"text-field":"{name_en}","text-max-width":7},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1.25,"icon-opacity":{"base":1,"stops":[[7.99,1],[8,0]]}}},{"id":"place-city-md-s","type":"symbol","metadata":{"mapbox:group":"1444862510685.128"},"source":"composite","source-layer":"place_label","maxzoom":14,"filter":["all",["in","ldir","E","S","SE","SW"],["in","scalerank",3,4,5],["==","type","city"]],"layout":{"text-field":"{name_en}","icon-image":"dot-10","text-anchor":{"base":1,"stops":[[7,"top"],[8,"center"]]},"text-offset":{"base":1,"stops":[[7.99,[0,0.1]],[8,[0,0]]]},"text-font":{"base":1,"stops":[[7,["DIN Offc Pro Regular","Arial Unicode MS Regular"]],[8,["DIN Offc Pro Medium","Arial Unicode MS Regular"]]]},"text-size":{"base":0.9,"stops":[[5,12],[12,22]]}},"paint":{"text-halo-width":1,"text-halo-color":"hsl(0, 0%, 100%)","text-color":"hsl(0, 0%, 0%)","text-halo-blur":1,"icon-opacity":{"base":1,"stops":[[7.99,1],[8,0]]}}},{"id":"place-city-md-n","type":"symbol","metadata":{"mapbox:group":"1444862510685.128"},"source":"composite","source-layer":"place_label","maxzoom":14,"filter":["all",["in","ldir","N","NE","NW","W"],["in","scalerank",3,4,5],["==","type","city"]],"layout":{"icon-image":"dot-10","text-font":{"base":1,"stops":[[7,["DIN Offc Pro Regular","Arial Unicode MS Regular"]],[8,["DIN Offc Pro Medium","Arial Unicode MS Regular"]]]},"text-offset":{"base":1,"stops":[[7.99,[0,-0.25]],[8,[0,0]]]},"text-anchor":{"base":1,"stops":[[7,"bottom"],[8,"center"]]},"text-field":"{name_en}","text-max-width":7,"text-size":{"base":0.9,"stops":[[5,12],[12,22]]}},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1,"icon-opacity":{"base":1,"stops":[[7.99,1],[8,0]]},"text-halo-blur":1}},{"id":"place-city-lg-s","type":"symbol","metadata":{"mapbox:group":"1444862510685.128"},"source":"composite","source-layer":"place_label","minzoom":1,"maxzoom":14,"filter":["all",["in","ldir","E","S","SE","SW"],["<=","scalerank",2],["==","type","city"]],"layout":{"icon-image":"dot-11","text-font":{"base":1,"stops":[[7,["DIN Offc Pro Regular","Arial Unicode MS Regular"]],[8,["DIN Offc Pro Medium","Arial Unicode MS Regular"]]]},"text-offset":{"base":1,"stops":[[7.99,[0,0.15]],[8,[0,0]]]},"text-anchor":{"base":1,"stops":[[7,"top"],[8,"center"]]},"text-field":"{name_en}","text-max-width":7,"text-size":{"base":0.9,"stops":[[4,12],[10,22]]}},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1,"icon-opacity":{"base":1,"stops":[[7.99,1],[8,0]]},"text-halo-blur":1}},{"id":"place-city-lg-n","type":"symbol","metadata":{"mapbox:group":"1444862510685.128"},"source":"composite","source-layer":"place_label","minzoom":1,"maxzoom":14,"filter":["all",["in","ldir","N","NE","NW","W"],["<=","scalerank",2],["==","type","city"]],"layout":{"icon-image":"dot-11","text-font":{"base":1,"stops":[[7,["DIN Offc Pro Regular","Arial Unicode MS Regular"]],[8,["DIN Offc Pro Medium","Arial Unicode MS Regular"]]]},"text-offset":{"base":1,"stops":[[7.99,[0,-0.25]],[8,[0,0]]]},"text-anchor":{"base":1,"stops":[[7,"bottom"],[8,"center"]]},"text-field":"{name_en}","text-max-width":7,"text-size":{"base":0.9,"stops":[[4,12],[10,22]]}},"paint":{"text-color":"hsl(0, 0%, 0%)","text-opacity":1,"text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1,"icon-opacity":{"base":1,"stops":[[7.99,1],[8,0]]},"text-halo-blur":1}},{"id":"marine-label-sm-ln","type":"symbol","metadata":{"mapbox:group":"1444856087950.3635"},"source":"composite","source-layer":"marine_label","minzoom":3,"maxzoom":10,"filter":["all",["==","$type","LineString"],[">=","labelrank",4]],"layout":{"text-line-height":1.1,"text-size":{"base":1,"stops":[[3,12],[6,16]]},"symbol-spacing":{"base":1,"stops":[[4,100],[6,400]]},"text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"symbol-placement":"line","text-field":"{name_en}","text-letter-spacing":0.1,"text-max-width":5},"paint":{"text-color":"hsl(205, 83%, 88%)"}},{"id":"marine-label-sm-pt","type":"symbol","metadata":{"mapbox:group":"1444856087950.3635"},"source":"composite","source-layer":"marine_label","minzoom":3,"maxzoom":10,"filter":["all",["==","$type","Point"],[">=","labelrank",4]],"layout":{"text-field":"{name_en}","text-max-width":5,"text-letter-spacing":0.1,"text-line-height":1.5,"text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"text-size":{"base":1,"stops":[[3,12],[6,16]]}},"paint":{"text-color":"hsl(205, 83%, 88%)"}},{"id":"marine-label-md-ln","type":"symbol","metadata":{"mapbox:group":"1444856087950.3635"},"source":"composite","source-layer":"marine_label","minzoom":2,"maxzoom":8,"filter":["all",["==","$type","LineString"],["in","labelrank",2,3]],"layout":{"text-line-height":1.1,"text-size":{"base":1.1,"stops":[[2,12],[5,20]]},"symbol-spacing":250,"text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"symbol-placement":"line","text-field":"{name_en}","text-letter-spacing":0.15,"text-max-width":5},"paint":{"text-color":"hsl(205, 83%, 88%)"}},{"id":"marine-label-md-pt","type":"symbol","metadata":{"mapbox:group":"1444856087950.3635"},"source":"composite","source-layer":"marine_label","minzoom":2,"maxzoom":8,"filter":["all",["==","$type","Point"],["in","labelrank",2,3]],"layout":{"text-field":"{name_en}","text-max-width":5,"text-letter-spacing":0.15,"text-line-height":1.5,"text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"text-size":{"base":1.1,"stops":[[2,14],[5,20]]}},"paint":{"text-color":"hsl(205, 83%, 88%)"}},{"id":"marine-label-lg-ln","type":"symbol","metadata":{"mapbox:group":"1444856087950.3635"},"source":"composite","source-layer":"marine_label","minzoom":1,"maxzoom":4,"filter":["all",["==","$type","LineString"],["==","labelrank",1]],"layout":{"text-field":"{name_en}","text-max-width":4,"text-letter-spacing":0.25,"text-line-height":1.1,"symbol-placement":"line","text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"text-size":{"base":1,"stops":[[1,14],[4,30]]}},"paint":{"text-color":"hsl(205, 83%, 88%)"}},{"id":"marine-label-lg-pt","type":"symbol","metadata":{"mapbox:group":"1444856087950.3635"},"source":"composite","source-layer":"marine_label","minzoom":1,"maxzoom":4,"filter":["all",["==","$type","Point"],["==","labelrank",1]],"layout":{"text-field":"{name_en}","text-max-width":4,"text-letter-spacing":0.25,"text-line-height":1.5,"text-font":["DIN Offc Pro Italic","Arial Unicode MS Regular"],"text-size":{"base":1,"stops":[[1,14],[4,30]]}},"paint":{"text-color":"hsl(205, 83%, 88%)"}},{"id":"state-label-sm","type":"symbol","metadata":{"mapbox:group":"1444856151690.9143"},"source":"composite","source-layer":"state_label","minzoom":3,"maxzoom":9,"filter":["<","area",20000],"layout":{"text-size":{"base":1,"stops":[[6,10],[9,14]]},"text-transform":"uppercase","text-font":["DIN Offc Pro Bold","Arial Unicode MS Bold"],"text-field":{"base":1,"stops":[[0,"{abbr}"],[6,"{name_en}"]]},"text-letter-spacing":0.15,"text-max-width":5},"paint":{"text-opacity":1,"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1}},{"id":"state-label-md","type":"symbol","metadata":{"mapbox:group":"1444856151690.9143"},"source":"composite","source-layer":"state_label","minzoom":3,"maxzoom":8,"filter":["all",["<","area",80000],[">=","area",20000]],"layout":{"text-size":{"base":1,"stops":[[5,10],[8,16]]},"text-transform":"uppercase","text-font":["DIN Offc Pro Bold","Arial Unicode MS Bold"],"text-field":{"base":1,"stops":[[0,"{abbr}"],[5,"{name_en}"]]},"text-letter-spacing":0.15,"text-max-width":6},"paint":{"text-opacity":1,"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1}},{"id":"state-label-lg","type":"symbol","metadata":{"mapbox:group":"1444856151690.9143"},"source":"composite","source-layer":"state_label","minzoom":3,"maxzoom":7,"filter":[">=","area",80000],"layout":{"text-size":{"base":1,"stops":[[4,10],[7,18]]},"text-transform":"uppercase","text-font":["DIN Offc Pro Bold","Arial Unicode MS Bold"],"text-padding":1,"text-field":{"base":1,"stops":[[0,"{abbr}"],[4,"{name_en}"]]},"text-letter-spacing":0.15,"text-max-width":6},"paint":{"text-opacity":1,"text-color":"hsl(0, 0%, 0%)","text-halo-color":"hsl(0, 0%, 100%)","text-halo-width":1}},{"id":"country-label-sm","type":"symbol","metadata":{"mapbox:group":"1444856144497.7825"},"source":"composite","source-layer":"country_label","minzoom":1,"maxzoom":10,"filter":[">=","scalerank",5],"layout":{"text-field":"{name_en}","text-max-width":6,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-size":{"base":0.9,"stops":[[5,14],[9,22]]}},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":{"base":1,"stops":[[2,"rgba(255,255,255,0.75)"],[3,"hsl(0, 0%, 100%)"]]},"text-halo-width":1.25}},{"id":"country-label-md","type":"symbol","metadata":{"mapbox:group":"1444856144497.7825"},"source":"composite","source-layer":"country_label","minzoom":1,"maxzoom":8,"filter":["in","scalerank",3,4],"layout":{"text-field":{"base":1,"stops":[[0,"{code}"],[2,"{name_en}"]]},"text-max-width":6,"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-size":{"base":1,"stops":[[3,10],[8,24]]}},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":{"base":1,"stops":[[2,"rgba(255,255,255,0.75)"],[3,"hsl(0, 0%, 100%)"]]},"text-halo-width":1.25}},{"id":"country-label-lg","type":"symbol","metadata":{"mapbox:group":"1444856144497.7825"},"source":"composite","source-layer":"country_label","minzoom":1,"maxzoom":7,"filter":["in","scalerank",1,2],"layout":{"text-field":"{name_en}","text-max-width":{"base":1,"stops":[[0,5],[3,6]]},"text-font":["DIN Offc Pro Medium","Arial Unicode MS Regular"],"text-size":{"base":1,"stops":[[1,10],[6,24]]}},"paint":{"text-color":"hsl(0, 0%, 0%)","text-halo-color":{"base":1,"stops":[[2,"rgba(255,255,255,0.75)"],[3,"hsl(0, 0%, 100%)"]]},"text-halo-width":1.25}}],"created":0,"modified":0,"owner":"mapbox","id":"streets-v9","draft":false} \ No newline at end of file
diff --git a/test/fixtures/resources/vector.pbf b/test/fixtures/resources/vector.pbf
deleted file mode 100644
index cea355eeff..0000000000
--- a/test/fixtures/resources/vector.pbf
+++ /dev/null
Binary files differ
diff --git a/test/fixtures/resources/vector.tile b/test/fixtures/resources/vector.tile
new file mode 100644
index 0000000000..47e6188854
--- /dev/null
+++ b/test/fixtures/resources/vector.tile
Binary files differ
diff --git a/test/fixtures/style_parser/circle-blur.info.json b/test/fixtures/style_parser/circle-blur.info.json
index 397e4cd4d1..40c4632c0e 100644
--- a/test/fixtures/style_parser/circle-blur.info.json
+++ b/test/fixtures/style_parser/circle-blur.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "value of 'circle-blur' must be a number, or a number function"]
+ [1, "WARNING", "ParseStyle", "value must be a number"]
]
}
}
diff --git a/test/fixtures/style_parser/circle-blur.style.json b/test/fixtures/style_parser/circle-blur.style.json
index 8140ad5e47..9c82142c21 100644
--- a/test/fixtures/style_parser/circle-blur.style.json
+++ b/test/fixtures/style_parser/circle-blur.style.json
@@ -12,7 +12,7 @@
"type": "circle",
"source": "mapbox",
"paint": {
- "circle-blur": null
+ "circle-blur": "no"
}
}]
}
diff --git a/test/fixtures/style_parser/circle-color.info.json b/test/fixtures/style_parser/circle-color.info.json
index 70375ce8f5..0dfa6371b7 100644
--- a/test/fixtures/style_parser/circle-color.info.json
+++ b/test/fixtures/style_parser/circle-color.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "value of 'circle-color' must be a string"]
+ [1, "WARNING", "ParseStyle", "value must be a string"]
]
}
}
diff --git a/test/fixtures/style_parser/circle-color.style.json b/test/fixtures/style_parser/circle-color.style.json
index 44c32f99ce..e9c508304b 100644
--- a/test/fixtures/style_parser/circle-color.style.json
+++ b/test/fixtures/style_parser/circle-color.style.json
@@ -12,7 +12,7 @@
"type": "circle",
"source": "mapbox",
"paint": {
- "circle-color": null
+ "circle-color": 1
}
}]
}
diff --git a/test/fixtures/style_parser/circle-opacity.info.json b/test/fixtures/style_parser/circle-opacity.info.json
index 3e8662bdbe..40c4632c0e 100644
--- a/test/fixtures/style_parser/circle-opacity.info.json
+++ b/test/fixtures/style_parser/circle-opacity.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "value of 'circle-opacity' must be a number, or a number function"]
+ [1, "WARNING", "ParseStyle", "value must be a number"]
]
}
}
diff --git a/test/fixtures/style_parser/circle-opacity.style.json b/test/fixtures/style_parser/circle-opacity.style.json
index 601e81a51b..a993182b1b 100644
--- a/test/fixtures/style_parser/circle-opacity.style.json
+++ b/test/fixtures/style_parser/circle-opacity.style.json
@@ -12,7 +12,7 @@
"type": "circle",
"source": "mapbox",
"paint": {
- "circle-opacity": null
+ "circle-opacity": "no"
}
}]
}
diff --git a/test/fixtures/style_parser/circle-radius.info.json b/test/fixtures/style_parser/circle-radius.info.json
index 7e87aa4fdb..40c4632c0e 100644
--- a/test/fixtures/style_parser/circle-radius.info.json
+++ b/test/fixtures/style_parser/circle-radius.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "value of 'circle-radius' must be a number, or a number function"]
+ [1, "WARNING", "ParseStyle", "value must be a number"]
]
}
}
diff --git a/test/fixtures/style_parser/circle-radius.style.json b/test/fixtures/style_parser/circle-radius.style.json
index a7fb28b2d3..f12cebff97 100644
--- a/test/fixtures/style_parser/circle-radius.style.json
+++ b/test/fixtures/style_parser/circle-radius.style.json
@@ -12,7 +12,7 @@
"type": "circle",
"source": "mapbox",
"paint": {
- "circle-radius": null
+ "circle-radius": "no"
}
}]
}
diff --git a/test/fixtures/style_parser/function-numeric.info.json b/test/fixtures/style_parser/function-numeric.info.json
index e2170eed4f..7dd5a69561 100644
--- a/test/fixtures/style_parser/function-numeric.info.json
+++ b/test/fixtures/style_parser/function-numeric.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "function argument must be a numeric value"]
+ [1, "WARNING", "ParseStyle", "function stop must be an array"]
]
}
}
diff --git a/test/fixtures/style_parser/function-type.info.json b/test/fixtures/style_parser/function-type.info.json
index 1549262bb0..c9e26537f7 100644
--- a/test/fixtures/style_parser/function-type.info.json
+++ b/test/fixtures/style_parser/function-type.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "function must specify a function type"]
+ [1, "WARNING", "ParseStyle", "function value must specify stops"]
]
}
}
diff --git a/test/fixtures/style_parser/geojson-data-inline.style.json b/test/fixtures/style_parser/geojson-data-inline.style.json
index fc4fe97c78..a5d19eea60 100644
--- a/test/fixtures/style_parser/geojson-data-inline.style.json
+++ b/test/fixtures/style_parser/geojson-data-inline.style.json
@@ -3,7 +3,7 @@
"sources": {
"mapbox": {
"type": "geojson",
- "data": { "type": "Feature", "geometry": { "type": "Point", "coordinates": [100.0, 0.0] } }
+ "data": { "type": "Feature", "geometry": { "type": "Point", "coordinates": [100.0, 0.0] }, "properties": {} }
}
}
}
diff --git a/test/fixtures/style_parser/geojson-invalid-data.info.json b/test/fixtures/style_parser/geojson-invalid-data.info.json
index ec4a7e2b75..86f1ef6edd 100644
--- a/test/fixtures/style_parser/geojson-invalid-data.info.json
+++ b/test/fixtures/style_parser/geojson-invalid-data.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "ERROR", "ParseStyle", "GeoJSON data must be a URL or an object"]
+ [1, "WARNING", "ParseStyle", "GeoJSON data must be a URL or an object"]
]
}
}
diff --git a/test/fixtures/style_parser/geojson-missing-data.info.json b/test/fixtures/style_parser/geojson-missing-data.info.json
index 2c4806c3cf..594d01d19d 100644
--- a/test/fixtures/style_parser/geojson-missing-data.info.json
+++ b/test/fixtures/style_parser/geojson-missing-data.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "ERROR", "ParseStyle", "GeoJSON source must have a data value"]
+ [1, "WARNING", "ParseStyle", "GeoJSON source must have a data value"]
]
}
}
diff --git a/test/fixtures/style_parser/line-opacity.info.json b/test/fixtures/style_parser/line-opacity.info.json
index 36f02e6e02..40c4632c0e 100644
--- a/test/fixtures/style_parser/line-opacity.info.json
+++ b/test/fixtures/style_parser/line-opacity.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "value of 'line-opacity' must be a number, or a number function"]
+ [1, "WARNING", "ParseStyle", "value must be a number"]
]
}
}
diff --git a/test/fixtures/style_parser/line-opacity.style.json b/test/fixtures/style_parser/line-opacity.style.json
index 712b2b6f50..7d29fb312a 100644
--- a/test/fixtures/style_parser/line-opacity.style.json
+++ b/test/fixtures/style_parser/line-opacity.style.json
@@ -16,7 +16,7 @@
"paint": {
"line-color": "#008",
"line-width": 0.9,
- "line-opacity": null
+ "line-opacity": "no"
}
}]
}
diff --git a/test/fixtures/style_parser/line-translate.info.json b/test/fixtures/style_parser/line-translate.info.json
index ff126bbf22..16df094290 100644
--- a/test/fixtures/style_parser/line-translate.info.json
+++ b/test/fixtures/style_parser/line-translate.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "value of 'line-translate' must be an array of two numbers"]
+ [1, "WARNING", "ParseStyle", "value must be an array of two numbers"]
]
}
}
diff --git a/test/fixtures/style_parser/line-translate.style.json b/test/fixtures/style_parser/line-translate.style.json
index a32b2d8ee4..4890a8b6df 100644
--- a/test/fixtures/style_parser/line-translate.style.json
+++ b/test/fixtures/style_parser/line-translate.style.json
@@ -12,7 +12,7 @@
"type": "line",
"source": "mapbox",
"paint": {
- "line-translate": null
+ "line-translate": "no"
}
}]
}
diff --git a/test/fixtures/style_parser/line-width.info.json b/test/fixtures/style_parser/line-width.info.json
index af2f9b284a..40c4632c0e 100644
--- a/test/fixtures/style_parser/line-width.info.json
+++ b/test/fixtures/style_parser/line-width.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "value of 'line-width' must be a number, or a number function"]
+ [1, "WARNING", "ParseStyle", "value must be a number"]
]
}
}
diff --git a/test/fixtures/style_parser/line-width.style.json b/test/fixtures/style_parser/line-width.style.json
index e5fe6fa8e1..1f677460c4 100644
--- a/test/fixtures/style_parser/line-width.style.json
+++ b/test/fixtures/style_parser/line-width.style.json
@@ -14,7 +14,7 @@
"source-layer": "waterway",
"filter": ["in", "type", "river", "canal"],
"paint": {
- "line-width": null
+ "line-width": "no"
}
}]
}
diff --git a/test/fixtures/style_parser/non-object.info.json b/test/fixtures/style_parser/non-object.info.json
new file mode 100644
index 0000000000..d1b67e2ea6
--- /dev/null
+++ b/test/fixtures/style_parser/non-object.info.json
@@ -0,0 +1,7 @@
+{
+ "default": {
+ "log": [
+ [1, "ERROR", "ParseStyle", "Style JSON must be an object"]
+ ]
+ }
+} \ No newline at end of file
diff --git a/test/fixtures/style_parser/non-object.style.json b/test/fixtures/style_parser/non-object.style.json
new file mode 100644
index 0000000000..3cc762b550
--- /dev/null
+++ b/test/fixtures/style_parser/non-object.style.json
@@ -0,0 +1 @@
+"" \ No newline at end of file
diff --git a/test/fixtures/style_parser/stop-zoom-value.info.json b/test/fixtures/style_parser/stop-zoom-value.info.json
index deaba65e25..386dce9d29 100644
--- a/test/fixtures/style_parser/stop-zoom-value.info.json
+++ b/test/fixtures/style_parser/stop-zoom-value.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "stop must have zoom level and value specification"]
+ [1, "WARNING", "ParseStyle", "function stop must have two elements"]
]
}
}
diff --git a/test/fixtures/style_parser/stops-array.info.json b/test/fixtures/style_parser/stops-array.info.json
index 3324958c85..ed66513fee 100644
--- a/test/fixtures/style_parser/stops-array.info.json
+++ b/test/fixtures/style_parser/stops-array.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "stops function must specify a stops array"]
+ [1, "WARNING", "ParseStyle", "function stops must be an array"]
]
}
}
diff --git a/test/fixtures/style_parser/text-size.info.json b/test/fixtures/style_parser/text-size.info.json
index 871a6ad499..40c4632c0e 100644
--- a/test/fixtures/style_parser/text-size.info.json
+++ b/test/fixtures/style_parser/text-size.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "value of 'text-size' must be a number, or a number function"]
+ [1, "WARNING", "ParseStyle", "value must be a number"]
]
}
}
diff --git a/test/fixtures/style_parser/text-size.style.json b/test/fixtures/style_parser/text-size.style.json
index ee6a6c39e4..dafa87d63b 100644
--- a/test/fixtures/style_parser/text-size.style.json
+++ b/test/fixtures/style_parser/text-size.style.json
@@ -14,7 +14,7 @@
"source-layer": "country_label",
"filter": ["==", "$type", "Point"],
"layout": {
- "text-size": null
+ "text-size": "no"
}
}]
}
diff --git a/test/fixtures/style_parser/version-not-number.info.json b/test/fixtures/style_parser/version-not-number.info.json
new file mode 100644
index 0000000000..130c334170
--- /dev/null
+++ b/test/fixtures/style_parser/version-not-number.info.json
@@ -0,0 +1,7 @@
+{
+ "default": {
+ "log": [
+ [1, "WARNING", "ParseStyle", "current renderer implementation only supports style spec version 8; using an outdated style will cause rendering errors"]
+ ]
+ }
+} \ No newline at end of file
diff --git a/test/fixtures/style_parser/version-not-number.style.json b/test/fixtures/style_parser/version-not-number.style.json
new file mode 100644
index 0000000000..ae3ef858a6
--- /dev/null
+++ b/test/fixtures/style_parser/version-not-number.style.json
@@ -0,0 +1 @@
+{"version":"8"} \ No newline at end of file
diff --git a/test/gl/object.cpp b/test/gl/object.cpp
index f2b21ec9f4..1506c33956 100644
--- a/test/gl/object.cpp
+++ b/test/gl/object.cpp
@@ -6,7 +6,6 @@
#include <mbgl/gl/gl_helper.hpp>
#include <mbgl/gl/gl_config.hpp>
#include <mbgl/gl/object_store.hpp>
-#include <mbgl/gl/texture_pool.hpp>
#include <memory>
@@ -15,15 +14,17 @@ namespace {
static bool getFlag = false;
static bool setFlag = false;
-}; // namespace
+} // namespace
struct MockGLObject {
using Type = bool;
- static const Type Default = false;
+ static const Type Default;
static Type Get() { getFlag = true; return true; }
static void Set(const Type&) { setFlag = true; }
};
+const bool MockGLObject::Default = false;
+
TEST(GLObject, Preserve) {
getFlag = false;
setFlag = false;
@@ -62,7 +63,7 @@ TEST(GLObject, Value) {
object->reset();
EXPECT_EQ(object->getCurrent(), false);
- EXPECT_TRUE(object->getDirty());
+ EXPECT_FALSE(object->getDirty());
EXPECT_TRUE(setFlag);
}
@@ -74,118 +75,43 @@ TEST(GLObject, Store) {
EXPECT_TRUE(store.empty());
mbgl::gl::UniqueProgram program = store.createProgram();
- EXPECT_NE(program.get(), 0);
+ EXPECT_NE(program.get(), 0u);
program.reset();
EXPECT_FALSE(store.empty());
store.performCleanup();
EXPECT_TRUE(store.empty());
mbgl::gl::UniqueShader shader = store.createShader(GL_VERTEX_SHADER);
- EXPECT_NE(shader.get(), 0);
+ EXPECT_NE(shader.get(), 0u);
shader.reset();
EXPECT_FALSE(store.empty());
store.performCleanup();
EXPECT_TRUE(store.empty());
mbgl::gl::UniqueBuffer buffer = store.createBuffer();
- EXPECT_NE(buffer.get(), 0);
+ EXPECT_NE(buffer.get(), 0u);
buffer.reset();
EXPECT_FALSE(store.empty());
store.performCleanup();
EXPECT_TRUE(store.empty());
mbgl::gl::UniqueTexture texture = store.createTexture();
- EXPECT_NE(texture.get(), 0);
+ EXPECT_NE(texture.get(), 0u);
texture.reset();
EXPECT_FALSE(store.empty());
store.performCleanup();
+ EXPECT_FALSE(store.empty());
+ store.reset();
EXPECT_TRUE(store.empty());
mbgl::gl::UniqueVAO vao = store.createVAO();
- EXPECT_NE(vao.get(), 0);
+ EXPECT_NE(vao.get(), 0u);
vao.reset();
EXPECT_FALSE(store.empty());
store.performCleanup();
EXPECT_TRUE(store.empty());
- mbgl::gl::UniqueTexturePool texturePool = store.createTexturePool();
- for (auto& id : texturePool.get()) {
- EXPECT_NE(id, 0);
- }
- EXPECT_TRUE(texturePool.get().size() == size_t(mbgl::gl::TextureMax));
- texturePool.reset();
- EXPECT_FALSE(store.empty());
- store.performCleanup();
- EXPECT_TRUE(store.empty());
-
- view.deactivate();
-}
-
-TEST(GLObject, TexturePool) {
- mbgl::HeadlessView view(std::make_shared<mbgl::HeadlessDisplay>(), 1);
- view.activate();
-
- mbgl::gl::ObjectStore store;
- EXPECT_TRUE(store.empty());
-
- mbgl::gl::TexturePool pool;
-
- std::vector<mbgl::gl::PooledTexture> ids;
-
- // Fill an entire texture pool.
- for (auto i = 0; i != mbgl::gl::TextureMax; ++i) {
- ids.push_back(pool.acquireTexture(store));
- EXPECT_EQ(ids.back().get(), GLuint(i + 1));
- EXPECT_TRUE(store.empty());
- }
-
- // Reuse texture ids from the same pool.
- for (auto i = 0; i != mbgl::gl::TextureMax; ++i) {
- ids[i].reset();
- ids.push_back(pool.acquireTexture(store));
- EXPECT_EQ(ids.back().get(), GLuint(i + 1));
- EXPECT_TRUE(store.empty());
- }
-
- // Trigger a new texture pool creation.
- {
- mbgl::gl::PooledTexture id = pool.acquireTexture(store);
- EXPECT_EQ(id, mbgl::gl::TextureMax + 1);
- EXPECT_TRUE(store.empty());
-
- id.reset();
-
- // Last used texture from pool triggers pool recycling.
- EXPECT_FALSE(store.empty());
-
- store.performCleanup();
- EXPECT_TRUE(store.empty());
- }
-
- // First pool is still full, thus creating a new pool.
- mbgl::gl::PooledTexture id1 = pool.acquireTexture(store);
- EXPECT_GT(id1.get(), mbgl::gl::TextureMax);
- EXPECT_TRUE(store.empty());
-
- // Release all textures from the first pool.
- ids.clear();
- EXPECT_FALSE(store.empty());
-
- store.performCleanup();
- EXPECT_TRUE(store.empty());
-
- // The first pool is now gone, the next pool is now in use.
- mbgl::gl::PooledTexture id2 = pool.acquireTexture(store);
- EXPECT_GT(id2.get(), id1.get());
-
- id2.reset();
- EXPECT_TRUE(store.empty());
-
- // Last used texture from the pool triggers pool recycling.
- id1.reset();
- EXPECT_FALSE(store.empty());
-
- store.performCleanup();
+ store.reset();
EXPECT_TRUE(store.empty());
view.deactivate();
diff --git a/test/include/mbgl/test.hpp b/test/include/mbgl/test.hpp
index e398174ef7..ad5b868f30 100644
--- a/test/include/mbgl/test.hpp
+++ b/test/include/mbgl/test.hpp
@@ -4,4 +4,4 @@ namespace mbgl {
int runTests(int argc, char* argv[]);
-}
+} // namespace mbgl
diff --git a/test/map/map.cpp b/test/map/map.cpp
index 57adee567d..b304712e1f 100644
--- a/test/map/map.cpp
+++ b/test/map/map.cpp
@@ -9,6 +9,7 @@
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/util/color.hpp>
using namespace mbgl;
using namespace mbgl::style;
@@ -67,7 +68,7 @@ TEST(Map, AddLayer) {
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
auto layer = std::make_unique<BackgroundLayer>("background");
- layer->setBackgroundColor({{{ 1, 0, 0, 1 }}});
+ layer->setBackgroundColor({{ 1, 0, 0, 1 }});
map.addLayer(std::move(layer));
test::checkImage("test/fixtures/map/add_layer", test::render(map));
@@ -80,7 +81,7 @@ TEST(Map, RemoveLayer) {
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
auto layer = std::make_unique<BackgroundLayer>("background");
- layer->setBackgroundColor({{{ 1, 0, 0, 1 }}});
+ layer->setBackgroundColor({{ 1, 0, 0, 1 }});
map.addLayer(std::move(layer));
map.removeLayer("background");
diff --git a/test/sprite/sprite_atlas.cpp b/test/sprite/sprite_atlas.cpp
index 08b8e54002..d9f96d57d1 100644
--- a/test/sprite/sprite_atlas.cpp
+++ b/test/sprite/sprite_atlas.cpp
@@ -52,8 +52,8 @@ TEST(Sprite, SpriteAtlas) {
EXPECT_EQ(20, metro.pos.h);
EXPECT_EQ(18, metro.spriteImage->getWidth());
EXPECT_EQ(18, metro.spriteImage->getHeight());
- EXPECT_EQ(18, metro.spriteImage->image.width);
- EXPECT_EQ(18, metro.spriteImage->image.height);
+ EXPECT_EQ(18u, metro.spriteImage->image.width);
+ EXPECT_EQ(18u, metro.spriteImage->image.height);
EXPECT_EQ(1.0f, metro.spriteImage->pixelRatio);
EXPECT_TRUE(atlas.getData());
@@ -108,8 +108,8 @@ TEST(Sprite, SpriteAtlasSize) {
EXPECT_EQ(16, metro.pos.h);
EXPECT_EQ(18, metro.spriteImage->getWidth());
EXPECT_EQ(18, metro.spriteImage->getHeight());
- EXPECT_EQ(18, metro.spriteImage->image.width);
- EXPECT_EQ(18, metro.spriteImage->image.height);
+ EXPECT_EQ(18u, metro.spriteImage->image.width);
+ EXPECT_EQ(18u, metro.spriteImage->image.height);
EXPECT_EQ(1.0f, metro.spriteImage->pixelRatio);
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlassize.png"),
@@ -135,8 +135,8 @@ TEST(Sprite, SpriteAtlasUpdates) {
EXPECT_EQ(16, one.pos.h);
EXPECT_EQ(16, one.spriteImage->getWidth());
EXPECT_EQ(12, one.spriteImage->getHeight());
- EXPECT_EQ(16, one.spriteImage->image.width);
- EXPECT_EQ(12, one.spriteImage->image.height);
+ EXPECT_EQ(16u, one.spriteImage->image.width);
+ EXPECT_EQ(12u, one.spriteImage->image.height);
EXPECT_EQ(1.0f, one.spriteImage->pixelRatio);
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlas-empty.png"),
diff --git a/test/sprite/sprite_image.cpp b/test/sprite/sprite_image.cpp
index 29e96cf134..f8982826a3 100644
--- a/test/sprite/sprite_image.cpp
+++ b/test/sprite/sprite_image.cpp
@@ -40,9 +40,9 @@ TEST(Sprite, SpriteImage) {
PremultipliedImage image(32, 24);
SpriteImage sprite(std::move(image), 2.0);
EXPECT_EQ(16, sprite.getWidth());
- EXPECT_EQ(32, sprite.image.width);
+ EXPECT_EQ(32u, sprite.image.width);
EXPECT_EQ(12, sprite.getHeight());
- EXPECT_EQ(24, sprite.image.height);
+ EXPECT_EQ(24u, sprite.image.height);
EXPECT_EQ(2, sprite.pixelRatio);
}
@@ -50,8 +50,8 @@ TEST(Sprite, SpriteImageFractionalRatio) {
PremultipliedImage image(20, 12);
SpriteImage sprite(std::move(image), 1.5);
EXPECT_EQ(float(20.0 / 1.5), sprite.getWidth());
- EXPECT_EQ(20, sprite.image.width);
+ EXPECT_EQ(20u, sprite.image.width);
EXPECT_EQ(float(12.0 / 1.5), sprite.getHeight());
- EXPECT_EQ(12, sprite.image.height);
+ EXPECT_EQ(12u, sprite.image.height);
EXPECT_EQ(1.5, sprite.pixelRatio);
}
diff --git a/test/sprite/sprite_parser.cpp b/test/sprite/sprite_parser.cpp
index 318911674e..85a5387898 100644
--- a/test/sprite/sprite_parser.cpp
+++ b/test/sprite/sprite_parser.cpp
@@ -60,8 +60,8 @@ TEST(Sprite, SpriteImageCreation1x) {
ASSERT_TRUE(sprite.get());
EXPECT_EQ(18, sprite->getWidth());
EXPECT_EQ(18, sprite->getHeight());
- EXPECT_EQ(18, sprite->image.width);
- EXPECT_EQ(18, sprite->image.height);
+ EXPECT_EQ(18u, sprite->image.width);
+ EXPECT_EQ(18u, sprite->image.height);
EXPECT_EQ(1, sprite->pixelRatio);
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1x-museum.png"),
sprite->image);
@@ -76,8 +76,8 @@ TEST(Sprite, SpriteImageCreation2x) {
ASSERT_TRUE(sprite.get());
EXPECT_EQ(18, sprite->getWidth());
EXPECT_EQ(18, sprite->getHeight());
- EXPECT_EQ(36, sprite->image.width);
- EXPECT_EQ(36, sprite->image.height);
+ EXPECT_EQ(36u, sprite->image.width);
+ EXPECT_EQ(36u, sprite->image.height);
EXPECT_EQ(2, sprite->pixelRatio);
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation2x.png"),
sprite->image);
@@ -91,8 +91,8 @@ TEST(Sprite, SpriteImageCreation1_5x) {
ASSERT_TRUE(sprite.get());
EXPECT_EQ(24, sprite->getWidth());
EXPECT_EQ(24, sprite->getHeight());
- EXPECT_EQ(36, sprite->image.width);
- EXPECT_EQ(36, sprite->image.height);
+ EXPECT_EQ(36u, sprite->image.width);
+ EXPECT_EQ(36u, sprite->image.height);
EXPECT_EQ(1.5, sprite->pixelRatio);
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-museum.png"),
sprite->image);
@@ -102,8 +102,8 @@ TEST(Sprite, SpriteImageCreation1_5x) {
ASSERT_TRUE(sprite2.get());
EXPECT_EQ(float(35 / 1.5), sprite2->getWidth());
EXPECT_EQ(float(35 / 1.5), sprite2->getHeight());
- EXPECT_EQ(35, sprite2->image.width);
- EXPECT_EQ(35, sprite2->image.height);
+ EXPECT_EQ(35u, sprite2->image.width);
+ EXPECT_EQ(35u, sprite2->image.height);
EXPECT_EQ(1.5, sprite2->pixelRatio);
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-hospital.png"),
sprite2->image);
@@ -198,8 +198,8 @@ TEST(Sprite, SpriteParsing) {
auto sprite = images.find("generic-metro")->second;
EXPECT_EQ(18, sprite->getWidth());
EXPECT_EQ(18, sprite->getHeight());
- EXPECT_EQ(18, sprite->image.width);
- EXPECT_EQ(18, sprite->image.height);
+ EXPECT_EQ(18u, sprite->image.width);
+ EXPECT_EQ(18u, sprite->image.height);
EXPECT_EQ(1, sprite->pixelRatio);
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->image);
}
diff --git a/test/sprite/sprite_store.cpp b/test/sprite/sprite_store.cpp
index 90f1e3b3f1..3c20d3c50e 100644
--- a/test/sprite/sprite_store.cpp
+++ b/test/sprite/sprite_store.cpp
@@ -153,13 +153,12 @@ TEST(SpriteStore, ReplaceWithDifferentDimensions) {
class SpriteStoreTest {
public:
- SpriteStoreTest()
- : spriteStore(1.0) {}
+ SpriteStoreTest() = default;
util::RunLoop loop;
StubFileSource fileSource;
StubStyleObserver observer;
- SpriteStore spriteStore;
+ SpriteStore spriteStore = { 1.0 };
void run() {
// Squelch logging.
@@ -181,14 +180,14 @@ Response successfulSpriteImageResponse(const Resource& resource) {
Response response;
response.data = std::make_shared<std::string>(util::read_file(resource.url));
return response;
-};
+}
Response successfulSpriteJSONResponse(const Resource& resource) {
EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url);
Response response;
response.data = std::make_shared<std::string>(util::read_file(resource.url));
return response;
-};
+}
Response failedSpriteResponse(const Resource&) {
Response response;
@@ -196,13 +195,13 @@ Response failedSpriteResponse(const Resource&) {
Response::Error::Reason::Other,
"Failed by the test case");
return response;
-};
+}
Response corruptSpriteResponse(const Resource&) {
Response response;
response.data = std::make_shared<std::string>("CORRUPT");
return response;
-};
+}
TEST(SpriteStore, LoadingSuccess) {
SpriteStoreTest test;
diff --git a/test/src/mbgl/test/fixture_log_observer.cpp b/test/src/mbgl/test/fixture_log_observer.cpp
index 302fdc7081..fc0239bb1c 100644
--- a/test/src/mbgl/test/fixture_log_observer.cpp
+++ b/test/src/mbgl/test/fixture_log_observer.cpp
@@ -1,13 +1,14 @@
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/test/util.hpp>
+#include <mbgl/util/enum.hpp>
namespace mbgl {
FixtureLog::Message::Message(EventSeverity severity_,
Event event_,
int64_t code_,
- const std::string& msg_)
- : severity(severity_), event(event_), code(code_), msg(msg_) {
+ std::string msg_)
+ : severity(severity_), event(event_), code(code_), msg(std::move(msg_)) {
}
bool FixtureLog::Message::operator==(const Message& rhs) const {
@@ -96,7 +97,8 @@ std::vector<FixtureLog::Message> FixtureLogObserver::unchecked() const {
}
::std::ostream& operator<<(::std::ostream& os, const FixtureLog::Message& message) {
- os << "[\"" << message.severity << "\", \"" << message.event << "\"";
+ os << "[\"" << Enum<EventSeverity>::toString(message.severity) << "\", \"";
+ os << Enum<Event>::toString(message.event) << "\"";
os << ", " << message.code;
os << ", \"" << message.msg << "\"";
return os << "]" << std::endl;
diff --git a/test/src/mbgl/test/fixture_log_observer.hpp b/test/src/mbgl/test/fixture_log_observer.hpp
index 1eabe10095..918bbee18c 100644
--- a/test/src/mbgl/test/fixture_log_observer.hpp
+++ b/test/src/mbgl/test/fixture_log_observer.hpp
@@ -12,7 +12,7 @@ namespace mbgl {
class FixtureLog {
public:
struct Message {
- Message(EventSeverity severity_, Event event_, int64_t code_, const std::string &msg_);
+ Message(EventSeverity severity_, Event event_, int64_t code_, std::string msg_);
Message();
bool operator==(const Message& rhs) const;
@@ -30,10 +30,10 @@ public:
using LogMessage = Message;
Observer(FixtureLog* log = nullptr);
- ~Observer();
+ ~Observer() override;
// Log::Observer implementation
- virtual bool onRecord(EventSeverity severity,
+ bool onRecord(EventSeverity severity,
Event event,
int64_t code,
const std::string& msg) override;
diff --git a/test/src/mbgl/test/stub_file_source.cpp b/test/src/mbgl/test/stub_file_source.cpp
index 89ccb3b335..75d7e2d072 100644
--- a/test/src/mbgl/test/stub_file_source.cpp
+++ b/test/src/mbgl/test/stub_file_source.cpp
@@ -10,21 +10,25 @@ public:
: fileSource(fileSource_) {
}
- ~StubFileRequest() {
- fileSource.pending.erase(this);
+ ~StubFileRequest() override {
+ fileSource.remove(this);
}
StubFileSource& fileSource;
};
StubFileSource::StubFileSource() {
- timer.start(10ms, 10ms, [this] {
+ timer.start(1ms, 1ms, [this] {
// Explicit copy to avoid iterator invalidation if ~StubFileRequest gets called within the loop.
auto pending_ = pending;
for (auto& pair : pending_) {
optional<Response> res = std::get<1>(pair.second)(std::get<0>(pair.second));
if (res) {
std::get<2>(pair.second)(*res);
+
+ if (!res->error) {
+ remove(pair.first);
+ }
}
}
});
@@ -38,6 +42,13 @@ std::unique_ptr<AsyncRequest> StubFileSource::request(const Resource& resource,
return std::move(req);
}
+void StubFileSource::remove(AsyncRequest* req) {
+ auto it = pending.find(req);
+ if (it != pending.end()) {
+ pending.erase(it);
+ }
+}
+
optional<Response> StubFileSource::defaultResponse(const Resource& resource) {
switch (resource.kind) {
case Resource::Kind::Style:
diff --git a/test/src/mbgl/test/stub_file_source.hpp b/test/src/mbgl/test/stub_file_source.hpp
index b88fb59907..ee4175cc3f 100644
--- a/test/src/mbgl/test/stub_file_source.hpp
+++ b/test/src/mbgl/test/stub_file_source.hpp
@@ -13,6 +13,7 @@ public:
~StubFileSource() override;
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
+ void remove(AsyncRequest*);
using ResponseFunction = std::function<optional<Response> (const Resource&)>;
@@ -30,8 +31,6 @@ public:
ResponseFunction spriteImageResponse;
private:
- friend class StubFileRequest;
-
// The default behavior is to throw if no per-kind callback has been set.
optional<Response> defaultResponse(const Resource&);
diff --git a/test/src/mbgl/test/stub_style_observer.hpp b/test/src/mbgl/test/stub_style_observer.hpp
index d70fc5e44c..ae0a23f0aa 100644
--- a/test/src/mbgl/test/stub_style_observer.hpp
+++ b/test/src/mbgl/test/stub_style_observer.hpp
@@ -43,8 +43,8 @@ public:
if (tileError) tileError(source, tileID, error);
}
- void onResourceLoaded() override {
- if (resourceLoaded) resourceLoaded();
+ void onNeedsRepaint() override {
+ if (needsRepaint) needsRepaint();
};
void onResourceError(std::exception_ptr error) override {
@@ -59,6 +59,6 @@ public:
std::function<void (Source&, std::exception_ptr)> sourceError;
std::function<void (Source&, const OverscaledTileID&, bool isNewTile)> tileLoaded;
std::function<void (Source&, const OverscaledTileID&, std::exception_ptr)> tileError;
- std::function<void ()> resourceLoaded;
+ std::function<void ()> needsRepaint;
std::function<void (std::exception_ptr)> resourceError;
};
diff --git a/test/src/mbgl/test/test.cpp b/test/src/mbgl/test/test.cpp
index 39afcc97e4..4c3dad1727 100644
--- a/test/src/mbgl/test/test.cpp
+++ b/test/src/mbgl/test/test.cpp
@@ -14,4 +14,4 @@ int runTests(int argc, char *argv[]) {
return RUN_ALL_TESTS();
}
-}
+} // namespace mbgl
diff --git a/test/src/mbgl/test/util.hpp b/test/src/mbgl/test/util.hpp
index 76c00d8434..30108a8866 100644
--- a/test/src/mbgl/test/util.hpp
+++ b/test/src/mbgl/test/util.hpp
@@ -73,5 +73,5 @@ void checkImage(const std::string& base,
double imageThreshold = 0,
double pixelThreshold = 0);
-}
-}
+} // namespace test
+} // namespace mbgl
diff --git a/test/storage/asset_file_source.cpp b/test/storage/asset_file_source.cpp
index 64f7254807..ab214ff793 100644
--- a/test/storage/asset_file_source.cpp
+++ b/test/storage/asset_file_source.cpp
@@ -21,7 +21,7 @@ std::string getFileSourceRoot() {
using namespace mbgl;
-TEST(AssetFileSource, Stress) {
+TEST(AssetFileSource, Load) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
diff --git a/test/storage/headers.cpp b/test/storage/headers.cpp
index b879c43b12..b7dcfc025d 100644
--- a/test/storage/headers.cpp
+++ b/test/storage/headers.cpp
@@ -12,12 +12,12 @@ TEST(HTTPHeader, Parsing) {
cc = http::CacheControl::parse(R"#(max-age =34)#");
ASSERT_TRUE(bool(cc.maxAge));
- EXPECT_EQ(34, *cc.maxAge);
+ EXPECT_EQ(34u, *cc.maxAge);
EXPECT_FALSE(cc.mustRevalidate);
cc = http::CacheControl::parse(R"#(,max-age=1)#");
ASSERT_TRUE(bool(cc.maxAge));
- EXPECT_EQ(1, *cc.maxAge);
+ EXPECT_EQ(1u, *cc.maxAge);
EXPECT_FALSE(cc.mustRevalidate);
cc = http::CacheControl::parse(R"#(max-age=-1)#");
@@ -30,12 +30,12 @@ TEST(HTTPHeader, Parsing) {
cc = http::CacheControl::parse(R"#(max-age="34,max-age="22,max-age=28)#");
ASSERT_TRUE(bool(cc.maxAge));
- EXPECT_EQ(28, *cc.maxAge);
+ EXPECT_EQ(28u, *cc.maxAge);
EXPECT_FALSE(cc.mustRevalidate);
cc = http::CacheControl::parse(R"#(max-age=3,max-age="34)#");
ASSERT_TRUE(bool(cc.maxAge));
- EXPECT_EQ(3, *cc.maxAge);
+ EXPECT_EQ(3u, *cc.maxAge);
EXPECT_FALSE(cc.mustRevalidate);
cc = http::CacheControl::parse(R"#(max-age="\",max-age=4,")#");
@@ -44,26 +44,26 @@ TEST(HTTPHeader, Parsing) {
cc = http::CacheControl::parse(R"#(private, max-age=0, no-cache)#");
ASSERT_TRUE(bool(cc.maxAge));
- EXPECT_EQ(0, *cc.maxAge);
+ EXPECT_EQ(0u, *cc.maxAge);
EXPECT_FALSE(cc.mustRevalidate);
cc = http::CacheControl::parse(R"#(max-age=0, no-cache, no-store)#");
ASSERT_TRUE(bool(cc.maxAge));
- EXPECT_EQ(0, *cc.maxAge);
+ EXPECT_EQ(0u, *cc.maxAge);
EXPECT_FALSE(cc.mustRevalidate);
cc = http::CacheControl::parse(R"#(, private , max-bar=3 , no-cache, "\,",,foo=",",,max-age=32)#");
ASSERT_TRUE(bool(cc.maxAge));
- EXPECT_EQ(32, *cc.maxAge);
+ EXPECT_EQ(32u, *cc.maxAge);
EXPECT_FALSE(cc.mustRevalidate);
cc = http::CacheControl::parse(R"#(max-age=3600, must-revalidate)#");
ASSERT_TRUE(bool(cc.maxAge));
- EXPECT_EQ(3600, *cc.maxAge);
+ EXPECT_EQ(3600u, *cc.maxAge);
EXPECT_TRUE(cc.mustRevalidate);
cc = http::CacheControl::parse(R"#(no-cache="Expires,Via",max-age=3600, must-revalidate)#");
ASSERT_TRUE(bool(cc.maxAge));
- EXPECT_EQ(3600, *cc.maxAge);
+ EXPECT_EQ(3600u, *cc.maxAge);
EXPECT_TRUE(cc.mustRevalidate);
}
diff --git a/test/storage/offline.cpp b/test/storage/offline.cpp
index 06789cd828..0faaabc298 100644
--- a/test/storage/offline.cpp
+++ b/test/storage/offline.cpp
@@ -1,5 +1,4 @@
#include <mbgl/storage/offline.hpp>
-#include <mbgl/util/tileset.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <gtest/gtest.h>
@@ -14,49 +13,42 @@ static const LatLngBounds sanFranciscoWrapped =
TEST(OfflineTilePyramidRegionDefinition, TileCoverEmpty) {
OfflineTilePyramidRegionDefinition region("", LatLngBounds::empty(), 0, 20, 1.0);
- Tileset tileset;
- EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, tileset));
+ EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, { 0, 22 }));
}
TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomIntersection) {
OfflineTilePyramidRegionDefinition region("", sanFrancisco, 2, 2, 1.0);
- Tileset tileset;
- tileset.minZoom = 0;
EXPECT_EQ((std::vector<CanonicalTileID>{ { 2, 0, 1 } }),
- region.tileCover(SourceType::Vector, 512, tileset));
+ region.tileCover(SourceType::Vector, 512, { 0, 22 }));
- tileset.minZoom = 3;
- EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, tileset));
+ EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, { 3, 22 }));
}
TEST(OfflineTilePyramidRegionDefinition, TileCoverTileSize) {
OfflineTilePyramidRegionDefinition region("", LatLngBounds::world(), 0, 0, 1.0);
- Tileset tileset;
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
- region.tileCover(SourceType::Vector, 512, tileset));
+ region.tileCover(SourceType::Vector, 512, { 0, 22 }));
EXPECT_EQ((std::vector<CanonicalTileID>{ { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 } }),
- region.tileCover(SourceType::Vector, 256, tileset));
+ region.tileCover(SourceType::Vector, 256, { 0, 22 }));
}
TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomRounding) {
OfflineTilePyramidRegionDefinition region("", sanFrancisco, 0.6, 0.7, 1.0);
- Tileset tileset;
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
- region.tileCover(SourceType::Vector, 512, tileset));
+ region.tileCover(SourceType::Vector, 512, { 0, 22 }));
EXPECT_EQ((std::vector<CanonicalTileID>{ { 1, 0, 0 } }),
- region.tileCover(SourceType::Raster, 512, tileset));
+ region.tileCover(SourceType::Raster, 512, { 0, 22 }));
}
TEST(OfflineTilePyramidRegionDefinition, TileCoverWrapped) {
OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 0, 1.0);
- Tileset tileset;
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
- region.tileCover(SourceType::Vector, 512, tileset));
+ region.tileCover(SourceType::Vector, 512, { 0, 22 }));
}
diff --git a/test/storage/offline_database.cpp b/test/storage/offline_database.cpp
index 0f95e927de..6a3262abf6 100644
--- a/test/storage/offline_database.cpp
+++ b/test/storage/offline_database.cpp
@@ -78,7 +78,7 @@ private:
bool locked = false;
};
-}
+} // namespace
TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Create)) {
using namespace mbgl;
@@ -113,7 +113,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(SchemaVersion)) {
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
- EXPECT_EQ(1ul, flo->count({ EventSeverity::Warning, Event::Database, -1, "Removing existing incompatible offline database" }));
+ EXPECT_EQ(1u, flo->count({ EventSeverity::Warning, Event::Database, -1, "Removing existing incompatible offline database" }));
}
TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Invalid)) {
@@ -129,7 +129,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Invalid)) {
auto observer = Log::removeObserver();
auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
- EXPECT_EQ(1ul, flo->count({ EventSeverity::Warning, Event::Database, -1, "Removing existing incompatible offline database" }));
+ EXPECT_EQ(1u, flo->count({ EventSeverity::Warning, Event::Database, -1, "Removing existing incompatible offline database" }));
}
TEST(OfflineDatabase, PutDoesNotStoreConnectionErrors) {
@@ -169,7 +169,7 @@ TEST(OfflineDatabase, PutResource) {
response.data = std::make_shared<std::string>("first");
auto insertPutResult = db.put(resource, response);
EXPECT_TRUE(insertPutResult.first);
- EXPECT_EQ(5, insertPutResult.second);
+ EXPECT_EQ(5u, insertPutResult.second);
auto insertGetResult = db.get(resource);
EXPECT_EQ(nullptr, insertGetResult->error.get());
@@ -178,7 +178,7 @@ TEST(OfflineDatabase, PutResource) {
response.data = std::make_shared<std::string>("second");
auto updatePutResult = db.put(resource, response);
EXPECT_FALSE(updatePutResult.first);
- EXPECT_EQ(6, updatePutResult.second);
+ EXPECT_EQ(6u, updatePutResult.second);
auto updateGetResult = db.get(resource);
EXPECT_EQ(nullptr, updateGetResult->error.get());
@@ -203,7 +203,7 @@ TEST(OfflineDatabase, PutTile) {
response.data = std::make_shared<std::string>("first");
auto insertPutResult = db.put(resource, response);
EXPECT_TRUE(insertPutResult.first);
- EXPECT_EQ(5, insertPutResult.second);
+ EXPECT_EQ(5u, insertPutResult.second);
auto insertGetResult = db.get(resource);
EXPECT_EQ(nullptr, insertGetResult->error.get());
@@ -212,7 +212,7 @@ TEST(OfflineDatabase, PutTile) {
response.data = std::make_shared<std::string>("second");
auto updatePutResult = db.put(resource, response);
EXPECT_FALSE(updatePutResult.first);
- EXPECT_EQ(6, updatePutResult.second);
+ EXPECT_EQ(6u, updatePutResult.second);
auto updateGetResult = db.get(resource);
EXPECT_EQ(nullptr, updateGetResult->error.get());
@@ -284,7 +284,7 @@ TEST(OfflineDatabase, ListRegions) {
OfflineRegion region = db.createRegion(definition, metadata);
std::vector<OfflineRegion> regions = db.listRegions();
- ASSERT_EQ(1, regions.size());
+ ASSERT_EQ(1u, regions.size());
EXPECT_EQ(region.getID(), regions.at(0).getID());
EXPECT_EQ(definition.styleURL, regions.at(0).getDefinition().styleURL);
EXPECT_EQ(definition.bounds, regions.at(0).getDefinition().bounds);
@@ -327,7 +327,7 @@ TEST(OfflineDatabase, DeleteRegion) {
db.deleteRegion(std::move(region));
- ASSERT_EQ(0, db.listRegions().size());
+ ASSERT_EQ(0u, db.listRegions().size());
}
TEST(OfflineDatabase, CreateRegionInfiniteMaxZoom) {
@@ -391,15 +391,15 @@ TEST(OfflineDatabase, PutReturnsSize) {
Response compressible;
compressible.data = std::make_shared<std::string>(1024, 0);
- EXPECT_EQ(17, db.put(Resource::style("http://example.com/compressible"), compressible).second);
+ EXPECT_EQ(17u, db.put(Resource::style("http://example.com/compressible"), compressible).second);
Response incompressible;
incompressible.data = randomString(1024);
- EXPECT_EQ(1024, db.put(Resource::style("http://example.com/incompressible"), incompressible).second);
+ EXPECT_EQ(1024u, db.put(Resource::style("http://example.com/incompressible"), incompressible).second);
Response noContent;
noContent.noContent = true;
- EXPECT_EQ(0, db.put(Resource::style("http://example.com/noContent"), noContent).second);
+ EXPECT_EQ(0u, db.put(Resource::style("http://example.com/noContent"), noContent).second);
}
TEST(OfflineDatabase, PutEvictsLeastRecentlyUsedResources) {
@@ -458,10 +458,10 @@ TEST(OfflineDatabase, GetRegionCompletedStatus) {
OfflineRegion region = db.createRegion(definition, metadata);
OfflineRegionStatus status1 = db.getRegionCompletedStatus(region.getID());
- EXPECT_EQ(0, status1.completedResourceCount);
- EXPECT_EQ(0, status1.completedResourceSize);
- EXPECT_EQ(0, status1.completedTileCount);
- EXPECT_EQ(0, status1.completedTileSize);
+ EXPECT_EQ(0u, status1.completedResourceCount);
+ EXPECT_EQ(0u, status1.completedResourceSize);
+ EXPECT_EQ(0u, status1.completedTileCount);
+ EXPECT_EQ(0u, status1.completedTileSize);
Response response;
response.data = std::make_shared<std::string>("data");
@@ -469,17 +469,17 @@ TEST(OfflineDatabase, GetRegionCompletedStatus) {
uint64_t styleSize = db.putRegionResource(region.getID(), Resource::style("http://example.com/"), response);
OfflineRegionStatus status2 = db.getRegionCompletedStatus(region.getID());
- EXPECT_EQ(1, status2.completedResourceCount);
+ EXPECT_EQ(1u, status2.completedResourceCount);
EXPECT_EQ(styleSize, status2.completedResourceSize);
- EXPECT_EQ(0, status2.completedTileCount);
- EXPECT_EQ(0, status2.completedTileSize);
+ EXPECT_EQ(0u, status2.completedTileCount);
+ EXPECT_EQ(0u, status2.completedTileSize);
uint64_t tileSize = db.putRegionResource(region.getID(), Resource::tile("http://example.com/", 1.0, 0, 0, 0), response);
OfflineRegionStatus status3 = db.getRegionCompletedStatus(region.getID());
- EXPECT_EQ(2, status3.completedResourceCount);
+ EXPECT_EQ(2u, status3.completedResourceCount);
EXPECT_EQ(styleSize + tileSize, status3.completedResourceSize);
- EXPECT_EQ(1, status3.completedTileCount);
+ EXPECT_EQ(1u, status3.completedTileCount);
EXPECT_EQ(tileSize, status3.completedTileSize);
}
@@ -501,43 +501,43 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) {
response.data = std::make_shared<std::string>("data");
// Count is initially zero.
- EXPECT_EQ(0, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(0u, db.getOfflineMapboxTileCount());
// Count stays the same after putting a non-tile resource.
db.putRegionResource(region1.getID(), Resource::style("http://example.com/"), response);
- EXPECT_EQ(0, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(0u, db.getOfflineMapboxTileCount());
// Count stays the same after putting a non-Mapbox tile.
db.putRegionResource(region1.getID(), nonMapboxTile, response);
- EXPECT_EQ(0, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(0u, db.getOfflineMapboxTileCount());
// Count increases after putting a Mapbox tile not used by another region.
db.putRegionResource(region1.getID(), mapboxTile1, response);
- EXPECT_EQ(1, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(1u, db.getOfflineMapboxTileCount());
// Count stays the same after putting a Mapbox tile used by another region.
db.putRegionResource(region2.getID(), mapboxTile1, response);
- EXPECT_EQ(1, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(1u, db.getOfflineMapboxTileCount());
// Count stays the same after putting a Mapbox tile used by the same region.
db.putRegionResource(region2.getID(), mapboxTile1, response);
- EXPECT_EQ(1, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(1u, db.getOfflineMapboxTileCount());
// Count stays the same after deleting a region when the tile is still used by another region.
db.deleteRegion(std::move(region2));
- EXPECT_EQ(1, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(1u, db.getOfflineMapboxTileCount());
// Count stays the same after the putting a non-offline Mapbox tile.
db.put(mapboxTile2, response);
- EXPECT_EQ(1, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(1u, db.getOfflineMapboxTileCount());
// Count increases after putting a pre-existing, but non-offline Mapbox tile.
db.putRegionResource(region1.getID(), mapboxTile2, response);
- EXPECT_EQ(2, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(2u, db.getOfflineMapboxTileCount());
// Count decreases after deleting a region when the tiles are not used by other regions.
db.deleteRegion(std::move(region1));
- EXPECT_EQ(0, db.getOfflineMapboxTileCount());
+ EXPECT_EQ(0u, db.getOfflineMapboxTileCount());
}
static int databasePageCount(const std::string& path) {
diff --git a/test/storage/offline_download.cpp b/test/storage/offline_download.cpp
index 7ac9b94e81..eb676e3b8d 100644
--- a/test/storage/offline_download.cpp
+++ b/test/storage/offline_download.cpp
@@ -77,12 +77,12 @@ TEST(OfflineDownload, NoSubresources) {
if (!expectsInactiveStatus) {
expectsInactiveStatus = true;
EXPECT_EQ(OfflineRegionDownloadState::Active, status.downloadState);
- EXPECT_EQ(1, status.completedResourceCount);
+ EXPECT_EQ(1u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
} else {
EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState);
- EXPECT_EQ(1, status.completedResourceCount);
+ EXPECT_EQ(1u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
test.loop.stop();
@@ -123,7 +123,7 @@ TEST(OfflineDownload, InlineSource) {
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (status.complete()) {
- EXPECT_EQ(2, status.completedResourceCount);
+ EXPECT_EQ(2u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
test.loop.stop();
@@ -158,7 +158,7 @@ TEST(OfflineDownload, GeoJSONSource) {
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (status.complete()) {
- EXPECT_EQ(2, status.completedResourceCount);
+ EXPECT_EQ(2u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
test.loop.stop();
@@ -217,7 +217,7 @@ TEST(OfflineDownload, Activate) {
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (status.complete()) {
- EXPECT_EQ(261, status.completedResourceCount); // 256 glyphs, 1 tile, 1 style, source, sprite image, and sprite json
+ EXPECT_EQ(261u, status.completedResourceCount); // 256 glyphs, 1 tile, 1 style, source, sprite image, and sprite json
EXPECT_EQ(test.size, status.completedResourceSize);
download.setState(OfflineRegionDownloadState::Inactive);
@@ -226,6 +226,8 @@ TEST(OfflineDownload, Activate) {
EXPECT_EQ(status.requiredResourceCount, computedStatus.requiredResourceCount);
EXPECT_EQ(status.completedResourceCount, computedStatus.completedResourceCount);
EXPECT_EQ(status.completedResourceSize, computedStatus.completedResourceSize);
+ EXPECT_EQ(status.completedTileCount, computedStatus.completedTileCount);
+ EXPECT_EQ(status.completedTileSize, computedStatus.completedTileSize);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
test.loop.stop();
@@ -248,9 +250,9 @@ TEST(OfflineDownload, GetStatusNoResources) {
OfflineRegionStatus status = download.getStatus();
EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState);
- EXPECT_EQ(0, status.completedResourceCount);
- EXPECT_EQ(0, status.completedResourceSize);
- EXPECT_EQ(1, status.requiredResourceCount);
+ EXPECT_EQ(0u, status.completedResourceCount);
+ EXPECT_EQ(0u, status.completedResourceSize);
+ EXPECT_EQ(1u, status.requiredResourceCount);
EXPECT_FALSE(status.requiredResourceCountIsPrecise);
EXPECT_FALSE(status.complete());
}
@@ -270,9 +272,9 @@ TEST(OfflineDownload, GetStatusStyleComplete) {
OfflineRegionStatus status = download.getStatus();
EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState);
- EXPECT_EQ(1, status.completedResourceCount);
+ EXPECT_EQ(1u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
- EXPECT_EQ(260, status.requiredResourceCount);
+ EXPECT_EQ(260u, status.requiredResourceCount);
EXPECT_FALSE(status.requiredResourceCountIsPrecise);
EXPECT_FALSE(status.complete());
}
@@ -296,9 +298,9 @@ TEST(OfflineDownload, GetStatusStyleAndSourceComplete) {
OfflineRegionStatus status = download.getStatus();
EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState);
- EXPECT_EQ(2, status.completedResourceCount);
+ EXPECT_EQ(2u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
- EXPECT_EQ(261, status.requiredResourceCount);
+ EXPECT_EQ(261u, status.requiredResourceCount);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
EXPECT_FALSE(status.complete());
}
@@ -353,7 +355,7 @@ TEST(OfflineDownload, RequestErrorsAreRetried) {
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (status.complete()) {
- EXPECT_EQ(1, status.completedResourceCount);
+ EXPECT_EQ(1u, status.completedResourceCount);
test.loop.stop();
}
};
@@ -391,7 +393,7 @@ TEST(OfflineDownload, TileCountLimitExceededNoTileResponse) {
};
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
- if (!mapboxTileCountLimitExceededCalled) {
+ if (!mapboxTileCountLimitExceededCalled) {
EXPECT_FALSE(status.complete());
EXPECT_EQ(OfflineRegionDownloadState::Active, status.downloadState);
} else {
@@ -422,7 +424,7 @@ TEST(OfflineDownload, TileCountLimitExceededWithTileResponse) {
EXPECT_EQ("http://127.0.0.1:3000/style.json", resource.url);
return test.response("mapbox_source.style.json");
};
-
+
test.fileSource.tileResponse = [&] (const Resource& resource) {
const Resource::TileData& tile = *resource.tileData;
EXPECT_EQ("mapbox://{z}-{x}-{y}.vector.pbf", tile.urlTemplate);
@@ -442,7 +444,7 @@ TEST(OfflineDownload, TileCountLimitExceededWithTileResponse) {
mapboxTileCountLimitExceededCalled = true;
};
- observer->statusChangedFn = [&] (OfflineRegionStatus status) {
+ observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (!mapboxTileCountLimitExceededCalled) {
EXPECT_EQ(OfflineRegionDownloadState::Active, status.downloadState);
} else {
@@ -481,7 +483,7 @@ TEST(OfflineDownload, WithPreviouslyExistingTile) {
observer->statusChangedFn = [&] (OfflineRegionStatus status) {
if (status.complete()) {
- EXPECT_EQ(2, status.completedResourceCount);
+ EXPECT_EQ(2u, status.completedResourceCount);
EXPECT_EQ(test.size, status.completedResourceSize);
EXPECT_TRUE(status.requiredResourceCountIsPrecise);
test.loop.stop();
@@ -543,22 +545,22 @@ TEST(OfflineDownload, ReactivatePreviouslyCompletedDownload) {
test.loop.run();
- ASSERT_EQ(4, statusesAfterReactivate.size());
+ ASSERT_EQ(4u, statusesAfterReactivate.size());
EXPECT_EQ(OfflineRegionDownloadState::Active, statusesAfterReactivate[0].downloadState);
EXPECT_FALSE(statusesAfterReactivate[0].requiredResourceCountIsPrecise);
- EXPECT_EQ(1, statusesAfterReactivate[0].requiredResourceCount);
- EXPECT_EQ(0, statusesAfterReactivate[0].completedResourceCount);
+ EXPECT_EQ(1u, statusesAfterReactivate[0].requiredResourceCount);
+ EXPECT_EQ(0u, statusesAfterReactivate[0].completedResourceCount);
EXPECT_EQ(OfflineRegionDownloadState::Active, statusesAfterReactivate[1].downloadState);
EXPECT_TRUE(statusesAfterReactivate[1].requiredResourceCountIsPrecise);
- EXPECT_EQ(2, statusesAfterReactivate[1].requiredResourceCount);
- EXPECT_EQ(1, statusesAfterReactivate[1].completedResourceCount);
+ EXPECT_EQ(2u, statusesAfterReactivate[1].requiredResourceCount);
+ EXPECT_EQ(1u, statusesAfterReactivate[1].completedResourceCount);
EXPECT_EQ(OfflineRegionDownloadState::Active, statusesAfterReactivate[2].downloadState);
EXPECT_TRUE(statusesAfterReactivate[2].requiredResourceCountIsPrecise);
- EXPECT_EQ(2, statusesAfterReactivate[2].requiredResourceCount);
- EXPECT_EQ(2, statusesAfterReactivate[2].completedResourceCount);
+ EXPECT_EQ(2u, statusesAfterReactivate[2].requiredResourceCount);
+ EXPECT_EQ(2u, statusesAfterReactivate[2].completedResourceCount);
}
TEST(OfflineDownload, Deactivate) {
diff --git a/test/storage/resource.cpp b/test/storage/resource.cpp
index 5d6c3bcbf2..02559f3e84 100644
--- a/test/storage/resource.cpp
+++ b/test/storage/resource.cpp
@@ -36,6 +36,62 @@ TEST(Resource, Tile) {
EXPECT_EQ(1, vectorTile.tileData->x);
EXPECT_EQ(2, vectorTile.tileData->y);
EXPECT_EQ(3, vectorTile.tileData->z);
+
+ Resource quadTile = Resource::tile("http://example.com/{quadkey}.png", 2.0, 0, 0, 1);
+ EXPECT_EQ(Resource::Kind::Tile, quadTile.kind);
+ EXPECT_EQ("http://example.com/0.png", quadTile.url);
+ EXPECT_EQ("http://example.com/{quadkey}.png", quadTile.tileData->urlTemplate);
+ EXPECT_EQ(1, quadTile.tileData->pixelRatio);
+ EXPECT_EQ(0, quadTile.tileData->x);
+ EXPECT_EQ(0, quadTile.tileData->y);
+ EXPECT_EQ(1, quadTile.tileData->z);
+
+ quadTile = Resource::tile("http://example.com/{quadkey}.png", 2.0, 0, 0, 2);
+ EXPECT_EQ(Resource::Kind::Tile, quadTile.kind);
+ EXPECT_EQ("http://example.com/00.png", quadTile.url);
+ EXPECT_EQ("http://example.com/{quadkey}.png", quadTile.tileData->urlTemplate);
+ EXPECT_EQ(1, quadTile.tileData->pixelRatio);
+ EXPECT_EQ(0, quadTile.tileData->x);
+ EXPECT_EQ(0, quadTile.tileData->y);
+ EXPECT_EQ(2, quadTile.tileData->z);
+
+ quadTile = Resource::tile("http://example.com/{quadkey}.png", 2.0, 1, 1, 2);
+ EXPECT_EQ(Resource::Kind::Tile, quadTile.kind);
+ EXPECT_EQ("http://example.com/03.png", quadTile.url);
+ EXPECT_EQ("http://example.com/{quadkey}.png", quadTile.tileData->urlTemplate);
+ EXPECT_EQ(1, quadTile.tileData->pixelRatio);
+ EXPECT_EQ(1, quadTile.tileData->x);
+ EXPECT_EQ(1, quadTile.tileData->y);
+ EXPECT_EQ(2, quadTile.tileData->z);
+
+ quadTile = Resource::tile("http://example.com/{quadkey}.png", 2.0, 22914, 52870, 17);
+ EXPECT_EQ(Resource::Kind::Tile, quadTile.kind);
+ EXPECT_EQ("http://example.com/02301322130000230.png", quadTile.url);
+ EXPECT_EQ("http://example.com/{quadkey}.png", quadTile.tileData->urlTemplate);
+ EXPECT_EQ(1, quadTile.tileData->pixelRatio);
+ EXPECT_EQ(22914, quadTile.tileData->x);
+ EXPECT_EQ(52870, quadTile.tileData->y);
+ EXPECT_EQ(17, quadTile.tileData->z);
+
+ // Test case confirmed by quadkeytools package
+ // https://bitbucket.org/steele/quadkeytools/src/master/test/quadkey.js?fileviewer=file-view-default#quadkey.js-57
+ quadTile = Resource::tile("http://example.com/{quadkey}.png", 2.0, 29, 3, 6);
+ EXPECT_EQ(Resource::Kind::Tile, quadTile.kind);
+ EXPECT_EQ("http://example.com/011123.png", quadTile.url);
+ EXPECT_EQ("http://example.com/{quadkey}.png", quadTile.tileData->urlTemplate);
+ EXPECT_EQ(1, quadTile.tileData->pixelRatio);
+ EXPECT_EQ(29, quadTile.tileData->x);
+ EXPECT_EQ(3, quadTile.tileData->y);
+ EXPECT_EQ(6, quadTile.tileData->z);
+
+ Resource wmsTile = Resource::tile("http://example.com/?bbox={bbox-epsg-3857}", 2.0, 0, 0, 1);
+ EXPECT_EQ(Resource::Kind::Tile, wmsTile.kind);
+ EXPECT_EQ("http://example.com/?bbox=-20037508.342789245,0,0,20037508.342789245", wmsTile.url);
+ EXPECT_EQ("http://example.com/?bbox={bbox-epsg-3857}", wmsTile.tileData->urlTemplate);
+ EXPECT_EQ(1, wmsTile.tileData->pixelRatio);
+ EXPECT_EQ(0, wmsTile.tileData->x);
+ EXPECT_EQ(0, wmsTile.tileData->y);
+ EXPECT_EQ(1, wmsTile.tileData->z);
}
TEST(Resource, Glyphs) {
diff --git a/test/style/filter.cpp b/test/style/filter.cpp
index 6e7f62db1b..b49f424a39 100644
--- a/test/style/filter.cpp
+++ b/test/style/filter.cpp
@@ -1,139 +1,125 @@
#include <mbgl/test/util.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/geometry.hpp>
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/style/parser.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/filter.hpp>
#include <rapidjson/document.h>
-#include <map>
-
using namespace mbgl;
using namespace mbgl::style;
-typedef std::multimap<std::string, mbgl::Value> Properties;
-
-class StubFeature : public GeometryTileFeature {
-public:
- inline StubFeature(const Properties& properties_, FeatureType type_)
- : properties(properties_)
- , type(type_)
- {}
-
- optional<Value> getValue(const std::string &key) const override {
- auto it = properties.find(key);
- if (it == properties.end())
- return optional<Value>();
- return it->second;
- }
-
- FeatureType getType() const override {
- return type;
- }
-
- GeometryCollection getGeometries() const override {
- return GeometryCollection();
- }
-
-private:
- const Properties properties;
- FeatureType type;
-};
-
Filter parse(const char * expression) {
rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
doc.Parse<0>(expression);
- return parseFilter(doc);
+ return *conversion::convert<Filter>(doc);
}
-bool evaluate(const Filter& filter, const Properties& properties, FeatureType type = FeatureType::Unknown) {
- StubFeature feature(properties, type);
- FilterEvaluator evaluator(feature);
- return Filter::visit(filter, evaluator);
+Feature feature(const PropertyMap& properties, const Geometry<double>& geometry = Point<double>()) {
+ Feature result { geometry };
+ result.properties = properties;
+ return result;
}
TEST(Filter, EqualsString) {
Filter f = parse("[\"==\", \"foo\", \"bar\"]");
- ASSERT_TRUE(evaluate(f, {{ "foo", std::string("bar") }}));
- ASSERT_FALSE(evaluate(f, {{ "foo", std::string("baz") }}));
-};
+ ASSERT_TRUE(f(feature({{ "foo", std::string("bar") }})));
+ ASSERT_FALSE(f(feature({{ "foo", std::string("baz") }})));
+}
TEST(Filter, EqualsNumber) {
Filter f = parse("[\"==\", \"foo\", 0]");
- ASSERT_TRUE(evaluate(f, {{ "foo", int64_t(0) }}));
- ASSERT_TRUE(evaluate(f, {{ "foo", uint64_t(0) }}));
- ASSERT_TRUE(evaluate(f, {{ "foo", double(0) }}));
- ASSERT_FALSE(evaluate(f, {{ "foo", int64_t(1) }}));
- ASSERT_FALSE(evaluate(f, {{ "foo", uint64_t(1) }}));
- ASSERT_FALSE(evaluate(f, {{ "foo", double(1) }}));
- ASSERT_FALSE(evaluate(f, {{ "foo", std::string("0") }}));
- ASSERT_FALSE(evaluate(f, {{ "foo", false }}));
- ASSERT_FALSE(evaluate(f, {{ "foo", true }}));
- ASSERT_FALSE(evaluate(f, {{}}));
+ ASSERT_TRUE(f(feature({{ "foo", int64_t(0) }})));
+ ASSERT_TRUE(f(feature({{ "foo", uint64_t(0) }})));
+ ASSERT_TRUE(f(feature({{ "foo", double(0) }})));
+ ASSERT_FALSE(f(feature({{ "foo", int64_t(1) }})));
+ ASSERT_FALSE(f(feature({{ "foo", uint64_t(1) }})));
+ ASSERT_FALSE(f(feature({{ "foo", double(1) }})));
+ ASSERT_FALSE(f(feature({{ "foo", std::string("0") }})));
+ ASSERT_FALSE(f(feature({{ "foo", false }})));
+ ASSERT_FALSE(f(feature({{ "foo", true }})));
+ ASSERT_FALSE(f(feature({{ "foo", nullptr }})));
+ ASSERT_FALSE(f(feature({{}})));
}
TEST(Filter, EqualsType) {
Filter f = parse("[\"==\", \"$type\", \"LineString\"]");
- ASSERT_FALSE(evaluate(f, {{}}, FeatureType::Point));
- ASSERT_TRUE(evaluate(f, {{}}, FeatureType::LineString));
+ ASSERT_FALSE(f(feature({{}}, Point<double>())));
+ ASSERT_TRUE(f(feature({{}}, LineString<double>())));
}
TEST(Filter, InType) {
Filter f = parse("[\"in\", \"$type\", \"LineString\", \"Polygon\"]");
- ASSERT_FALSE(evaluate(f, {{}}, FeatureType::Point));
- ASSERT_TRUE(evaluate(f, {{}}, FeatureType::LineString));
- ASSERT_TRUE(evaluate(f, {{}}, FeatureType::Polygon));
+ ASSERT_FALSE(f(feature({{}}, Point<double>())));
+ ASSERT_TRUE(f(feature({{}}, LineString<double>())));
+ ASSERT_TRUE(f(feature({{}}, Polygon<double>())));
}
TEST(Filter, Any) {
- ASSERT_FALSE(evaluate(parse("[\"any\"]"), {{}}));
- ASSERT_TRUE(evaluate(parse("[\"any\", [\"==\", \"foo\", 1]]"),
- {{ std::string("foo"), int64_t(1) }}));
- ASSERT_FALSE(evaluate(parse("[\"any\", [\"==\", \"foo\", 0]]"),
- {{ std::string("foo"), int64_t(1) }}));
- ASSERT_TRUE(evaluate(parse("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
- {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(parse("[\"any\"]")(feature({{}})));
+ ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 1]]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_FALSE(parse("[\"any\", [\"==\", \"foo\", 0]]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
}
TEST(Filter, All) {
- ASSERT_TRUE(evaluate(parse("[\"all\"]"), {{}}));
- ASSERT_TRUE(evaluate(parse("[\"all\", [\"==\", \"foo\", 1]]"),
- {{ std::string("foo"), int64_t(1) }}));
- ASSERT_FALSE(evaluate(parse("[\"all\", [\"==\", \"foo\", 0]]"),
- {{ std::string("foo"), int64_t(1) }}));
- ASSERT_FALSE(evaluate(parse("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
- {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_TRUE(parse("[\"all\"]")(feature({{}})));
+ ASSERT_TRUE(parse("[\"all\", [\"==\", \"foo\", 1]]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0]]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
}
TEST(Filter, None) {
- ASSERT_TRUE(evaluate(parse("[\"none\"]"), {{}}));
- ASSERT_FALSE(evaluate(parse("[\"none\", [\"==\", \"foo\", 1]]"),
- {{ std::string("foo"), int64_t(1) }}));
- ASSERT_TRUE(evaluate(parse("[\"none\", [\"==\", \"foo\", 0]]"),
- {{ std::string("foo"), int64_t(1) }}));
- ASSERT_FALSE(evaluate(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
- {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_TRUE(parse("[\"none\"]")(feature({{}})));
+ ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 1]]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_TRUE(parse("[\"none\", [\"==\", \"foo\", 0]]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
}
TEST(Filter, Has) {
- ASSERT_TRUE(evaluate(parse("[\"has\", \"foo\"]"),
- {{ std::string("foo"), int64_t(1) }}));
- ASSERT_TRUE(evaluate(parse("[\"has\", \"foo\"]"),
- {{ std::string("foo"), int64_t(0) }}));
- ASSERT_TRUE(evaluate(parse("[\"has\", \"foo\"]"),
- {{ std::string("foo"), false }}));
- ASSERT_FALSE(evaluate(parse("[\"has\", \"foo\"]"),
- {{}}));
+ ASSERT_TRUE(parse("[\"has\", \"foo\"]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_TRUE(parse("[\"has\", \"foo\"]")(
+ feature({{ std::string("foo"), int64_t(0) }})));
+ ASSERT_TRUE(parse("[\"has\", \"foo\"]")(
+ feature({{ std::string("foo"), false }})));
+ ASSERT_FALSE(parse("[\"has\", \"foo\"]")(
+ feature({{}})));
}
TEST(Filter, NotHas) {
- ASSERT_FALSE(evaluate(parse("[\"!has\", \"foo\"]"),
- {{ std::string("foo"), int64_t(1) }}));
- ASSERT_FALSE(evaluate(parse("[\"!has\", \"foo\"]"),
- {{ std::string("foo"), int64_t(0) }}));
- ASSERT_FALSE(evaluate(parse("[\"!has\", \"foo\"]"),
- {{ std::string("foo"), false }}));
- ASSERT_TRUE(evaluate(parse("[\"!has\", \"foo\"]"),
- {{}}));
+ ASSERT_FALSE(parse("[\"!has\", \"foo\"]")(
+ feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_FALSE(parse("[\"!has\", \"foo\"]")(
+ feature({{ std::string("foo"), int64_t(0) }})));
+ ASSERT_FALSE(parse("[\"!has\", \"foo\"]")(
+ feature({{ std::string("foo"), false }})));
+ ASSERT_TRUE(parse("[\"!has\", \"foo\"]")(
+ feature({{}})));
+}
+
+TEST(Filter, ID) {
+ Feature feature1 { Point<double>() };
+ feature1.id = { 1234 };
+
+ ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1));
+ ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1));
+
+ Feature feature2 { Point<double>() };
+ feature2.properties["id"] = { 1234 };
+
+ ASSERT_FALSE(parse("[\"==\", \"$id\", 1234]")(feature2));
}
diff --git a/test/style/source.cpp b/test/style/source.cpp
index ae8fb54d2f..59b2524e17 100644
--- a/test/style/source.cpp
+++ b/test/style/source.cpp
@@ -2,15 +2,19 @@
#include <mbgl/test/stub_file_source.hpp>
#include <mbgl/test/stub_style_observer.hpp>
-#include <mbgl/style/source.hpp>
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/sources/raster_source.hpp>
+#include <mbgl/style/sources/vector_source.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/io.hpp>
+#include <mbgl/util/tileset.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/util/worker.hpp>
-#include <mbgl/gl/texture_pool.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/update_parameters.hpp>
#include <mbgl/style/layers/line_layer.hpp>
@@ -28,7 +32,6 @@ public:
Transform transform;
TransformState transformState;
Worker worker { 1 };
- gl::TexturePool texturePool;
AnnotationManager annotationManager { 1.0 };
style::Style style { fileSource, 1.0 };
@@ -39,7 +42,6 @@ public:
transformState,
worker,
fileSource,
- texturePool,
true,
MapMode::Continuous,
annotationManager,
@@ -78,14 +80,14 @@ TEST(Source, LoadingFail) {
};
test.observer.sourceError = [&] (Source& source, std::exception_ptr error) {
- EXPECT_EQ("url", source.url);
+ EXPECT_EQ("source", source.getID());
EXPECT_EQ("Failed by the test case", util::toString(error));
test.end();
};
- Source source(SourceType::Vector, "source", "url", 512, nullptr, nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
+ VectorSource source("source", "url");
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
test.run();
}
@@ -101,14 +103,14 @@ TEST(Source, LoadingCorrupt) {
};
test.observer.sourceError = [&] (Source& source, std::exception_ptr error) {
- EXPECT_EQ("url", source.url);
+ EXPECT_EQ("source", source.getID());
EXPECT_EQ("0 - Invalid value.", util::toString(error));
test.end();
};
- Source source(SourceType::Vector, "source", "url", 512, nullptr, nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
+ VectorSource source("source", "url");
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
test.run();
}
@@ -123,7 +125,7 @@ TEST(Source, RasterTileEmpty) {
};
test.observer.tileLoaded = [&] (Source& source, const OverscaledTileID&, bool) {
- EXPECT_EQ("source", source.id);
+ EXPECT_EQ("source", source.getID());
test.end();
};
@@ -131,13 +133,13 @@ TEST(Source, RasterTileEmpty) {
FAIL() << "Should never be called";
};
- auto tileset = std::make_unique<Tileset>();
- tileset->tiles = { "tiles" };
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
- Source source(SourceType::Raster, "source", "", 512, std::move(tileset), nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
- source.update(test.updateParameters);
+ RasterSource source("source", tileset, 512);
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
+ source.baseImpl->update(test.updateParameters);
test.run();
}
@@ -152,7 +154,7 @@ TEST(Source, VectorTileEmpty) {
};
test.observer.tileLoaded = [&] (Source& source, const OverscaledTileID&, bool) {
- EXPECT_EQ("source", source.id);
+ EXPECT_EQ("source", source.getID());
test.end();
};
@@ -160,13 +162,13 @@ TEST(Source, VectorTileEmpty) {
FAIL() << "Should never be called";
};
- auto tileset = std::make_unique<Tileset>();
- tileset->tiles = { "tiles" };
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
- Source source(SourceType::Vector, "source", "", 512, std::move(tileset), nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
- source.update(test.updateParameters);
+ VectorSource source("source", tileset);
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
+ source.baseImpl->update(test.updateParameters);
test.run();
}
@@ -183,19 +185,19 @@ TEST(Source, RasterTileFail) {
};
test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) {
- EXPECT_EQ(SourceType::Raster, source.type);
+ EXPECT_EQ(SourceType::Raster, source.baseImpl->type);
EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
EXPECT_EQ("Failed by the test case", util::toString(error));
test.end();
};
- auto tileset = std::make_unique<Tileset>();
- tileset->tiles = { "tiles" };
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
- Source source(SourceType::Raster, "source", "", 512, std::move(tileset), nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
- source.update(test.updateParameters);
+ RasterSource source("source", tileset, 512);
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
+ source.baseImpl->update(test.updateParameters);
test.run();
}
@@ -212,19 +214,19 @@ TEST(Source, VectorTileFail) {
};
test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) {
- EXPECT_EQ(SourceType::Vector, source.type);
+ EXPECT_EQ(SourceType::Vector, source.baseImpl->type);
EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
EXPECT_EQ("Failed by the test case", util::toString(error));
test.end();
};
- auto tileset = std::make_unique<Tileset>();
- tileset->tiles = { "tiles" };
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
- Source source(SourceType::Vector, "source", "", 512, std::move(tileset), nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
- source.update(test.updateParameters);
+ VectorSource source("source", tileset);
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
+ source.baseImpl->update(test.updateParameters);
test.run();
}
@@ -239,20 +241,20 @@ TEST(Source, RasterTileCorrupt) {
};
test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) {
- EXPECT_EQ(source.type, SourceType::Raster);
+ EXPECT_EQ(source.baseImpl->type, SourceType::Raster);
EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
EXPECT_TRUE(bool(error));
// Not asserting on platform-specific error text.
test.end();
};
- auto tileset = std::make_unique<Tileset>();
- tileset->tiles = { "tiles" };
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
- Source source(SourceType::Raster, "source", "", 512, std::move(tileset), nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
- source.update(test.updateParameters);
+ RasterSource source("source", tileset, 512);
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
+ source.baseImpl->update(test.updateParameters);
test.run();
}
@@ -267,24 +269,24 @@ TEST(Source, VectorTileCorrupt) {
};
test.observer.tileError = [&] (Source& source, const OverscaledTileID& tileID, std::exception_ptr error) {
- EXPECT_EQ(source.type, SourceType::Vector);
+ EXPECT_EQ(source.baseImpl->type, SourceType::Vector);
EXPECT_EQ(OverscaledTileID(0, 0, 0), tileID);
EXPECT_EQ(util::toString(error), "unknown pbf field type exception");
test.end();
};
// Need to have at least one layer that uses the source.
- auto layer = std::make_unique<LineLayer>("id");
- layer->setSource("source", "water");
+ auto layer = std::make_unique<LineLayer>("id", "source");
+ layer->setSourceLayer("water");
test.style.addLayer(std::move(layer));
- auto tileset = std::make_unique<Tileset>();
- tileset->tiles = { "tiles" };
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
- Source source(SourceType::Vector, "source", "", 512, std::move(tileset), nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
- source.update(test.updateParameters);
+ VectorSource source("source", tileset);
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
+ source.baseImpl->update(test.updateParameters);
test.run();
}
@@ -305,13 +307,13 @@ TEST(Source, RasterTileCancel) {
FAIL() << "Should never be called";
};
- auto tileset = std::make_unique<Tileset>();
- tileset->tiles = { "tiles" };
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
- Source source(SourceType::Raster, "source", "", 512, std::move(tileset), nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
- source.update(test.updateParameters);
+ RasterSource source("source", tileset, 512);
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
+ source.baseImpl->update(test.updateParameters);
test.run();
}
@@ -332,13 +334,13 @@ TEST(Source, VectorTileCancel) {
FAIL() << "Should never be called";
};
- auto tileset = std::make_unique<Tileset>();
- tileset->tiles = { "tiles" };
+ Tileset tileset;
+ tileset.tiles = { "tiles" };
- Source source(SourceType::Vector, "source", "", 512, std::move(tileset), nullptr);
- source.setObserver(&test.observer);
- source.load(test.fileSource);
- source.update(test.updateParameters);
+ VectorSource source("source", tileset);
+ source.baseImpl->setObserver(&test.observer);
+ source.baseImpl->load(test.fileSource);
+ source.baseImpl->update(test.updateParameters);
test.run();
}
diff --git a/test/style/style.cpp b/test/style/style.cpp
index d530d9cbdd..1681bac1c9 100644
--- a/test/style/style.cpp
+++ b/test/style/style.cpp
@@ -2,7 +2,7 @@
#include <mbgl/test/stub_file_source.hpp>
#include <mbgl/style/style.hpp>
-#include <mbgl/style/source.hpp>
+#include <mbgl/style/source_impl.hpp>
#include <mbgl/util/io.hpp>
using namespace mbgl;
@@ -22,11 +22,11 @@ TEST(Style, UnusedSource) {
Source *usedSource = style.getSource("usedsource");
EXPECT_TRUE(usedSource);
- EXPECT_TRUE(usedSource->isLoaded());
+ EXPECT_TRUE(usedSource->baseImpl->isLoaded());
Source *unusedSource = style.getSource("unusedsource");
EXPECT_TRUE(unusedSource);
- EXPECT_FALSE(unusedSource->isLoaded());
+ EXPECT_FALSE(unusedSource->baseImpl->isLoaded());
}
TEST(Style, UnusedSourceActiveViaClassUpdate) {
@@ -46,7 +46,7 @@ TEST(Style, UnusedSourceActiveViaClassUpdate) {
Source *unusedSource = style.getSource("unusedsource");
EXPECT_TRUE(unusedSource);
- EXPECT_TRUE(unusedSource->isLoaded());
+ EXPECT_TRUE(unusedSource->baseImpl->isLoaded());
// Style classes should be cleared upon new style load.
style.setJSON(util::read_file("test/fixtures/resources/style-unused-sources.json"));
@@ -59,5 +59,5 @@ TEST(Style, UnusedSourceActiveViaClassUpdate) {
unusedSource = style.getSource("unusedsource");
EXPECT_TRUE(unusedSource);
- EXPECT_FALSE(unusedSource->isLoaded());
+ EXPECT_FALSE(unusedSource->baseImpl->isLoaded());
}
diff --git a/test/style/style_layer.cpp b/test/style/style_layer.cpp
index ed8904dabd..9c6a6e924a 100644
--- a/test/style/style_layer.cpp
+++ b/test/style/style_layer.cpp
@@ -1,19 +1,212 @@
#include <mbgl/test/util.hpp>
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/background_layer_impl.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/circle_layer_impl.hpp>
+#include <mbgl/style/layers/custom_layer.hpp>
+#include <mbgl/style/layers/custom_layer_impl.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_layer_impl.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/line_layer_impl.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/raster_layer_impl.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/util/color.hpp>
using namespace mbgl;
using namespace mbgl::style;
-TEST(Layer, Clone) {
- std::unique_ptr<Layer> layer = std::make_unique<BackgroundLayer>("id");
- std::unique_ptr<Layer> clone = layer->baseImpl->clone();
- EXPECT_NE(layer.get(), clone.get());
- EXPECT_TRUE(reinterpret_cast<BackgroundLayer::Impl*>(clone->baseImpl.get()));
-}
+namespace {
-TEST(Layer, CloneCopiesBaseProperties) {
- std::unique_ptr<BackgroundLayer> layer = std::make_unique<BackgroundLayer>("id");
+template <class T, class... Params> void testClone(Params... params) {
+ auto layer = std::make_unique<T>(std::forward<Params>(params)...);
+ auto clone = layer->baseImpl->clone();
+ EXPECT_NE(layer.get(), clone.get());
+ EXPECT_TRUE(reinterpret_cast<typename T::Impl*>(clone->baseImpl.get()));
layer->impl->id = "test";
EXPECT_EQ("test", layer->baseImpl->clone()->getID());
}
+
+const auto color = PropertyValue<Color> {{ 1, 0, 0, 1 }};
+const auto opacity = PropertyValue<float> { 1.0f };
+const auto radius = PropertyValue<float> { 1.0f };
+const auto blur = PropertyValue<float> { 1.0f };
+const auto pattern = PropertyValue<std::string> { "foo" };
+const auto antialias = PropertyValue<bool> { false };
+const auto translate = PropertyValue<std::array<float, 2>> {{{ 0, 0 }}};
+const auto translateAnchor = PropertyValue<TranslateAnchorType> { TranslateAnchorType::Map };
+const auto lineCap = PropertyValue<LineCapType> { LineCapType::Round };
+const auto lineJoin = PropertyValue<LineJoinType> { LineJoinType::Miter };
+const auto miterLimit = PropertyValue<float> { 1.0f };
+const auto roundLimit = PropertyValue<float> { 1.0f };
+const auto width = PropertyValue<float> { 1.0f };
+const auto gapWidth = PropertyValue<float> { 1.0f };
+const auto offset = PropertyValue<float> { 1.0f };
+const auto dashArray = PropertyValue<std::vector<float>> {{}};
+const auto hueRotate = PropertyValue<float> { 1.0f };
+const auto brightness = PropertyValue<float> { 1.0f };
+const auto saturation = PropertyValue<float> { 1.0f };
+const auto contrast = PropertyValue<float> { 1.0f };
+const auto duration = PropertyValue<float> { 1.0f };
+
+} // namespace
+
+TEST(Layer, Clone) {
+ testClone<BackgroundLayer>("background");
+ testClone<CircleLayer>("circle", "source");
+ testClone<CustomLayer>("custom", [](void*){}, [](void*, const CustomLayerRenderParameters&){}, [](void*){}, nullptr),
+ testClone<FillLayer>("fill", "source");
+ testClone<LineLayer>("line", "source");
+ testClone<RasterLayer>("raster", "source");
+ testClone<SymbolLayer>("symbol", "source");
+}
+
+TEST(Layer, BackgroundProperties) {
+ auto layer = std::make_unique<BackgroundLayer>("background");
+ EXPECT_TRUE(layer->is<BackgroundLayer>());
+
+ // Paint properties
+
+ layer->setBackgroundColor(color);
+ EXPECT_EQ(layer->getBackgroundColor().asConstant(), color.asConstant());
+
+ layer->setBackgroundOpacity(opacity);
+ EXPECT_EQ(layer->getBackgroundOpacity().asConstant(), opacity.asConstant());
+
+ layer->setBackgroundPattern(pattern);
+ EXPECT_EQ(layer->getBackgroundPattern().asConstant(), pattern.asConstant());
+}
+
+TEST(Layer, CircleProperties) {
+ auto layer = std::make_unique<CircleLayer>("circle", "source");
+ EXPECT_TRUE(layer->is<CircleLayer>());
+
+ // Paint properties
+
+ layer->setCircleColor(color);
+ EXPECT_EQ(layer->getCircleColor().asConstant(), color.asConstant());
+
+ layer->setCircleOpacity(opacity);
+ EXPECT_EQ(layer->getCircleOpacity().asConstant(), opacity.asConstant());
+
+ layer->setCircleRadius(radius);
+ EXPECT_EQ(layer->getCircleRadius().asConstant(), radius.asConstant());
+
+ layer->setCircleBlur(blur);
+ EXPECT_EQ(layer->getCircleBlur().asConstant(), blur.asConstant());
+
+ layer->setCircleTranslate(translate);
+ EXPECT_EQ(layer->getCircleTranslate().asConstant(), translate.asConstant());
+
+ layer->setCircleTranslateAnchor(translateAnchor);
+ EXPECT_EQ(layer->getCircleTranslateAnchor().asConstant(), translateAnchor.asConstant());
+}
+
+TEST(Layer, FillProperties) {
+ auto layer = std::make_unique<FillLayer>("fill", "source");
+ EXPECT_TRUE(layer->is<FillLayer>());
+
+ // Paint properties
+
+ layer->setFillColor(color);
+ EXPECT_EQ(layer->getFillColor().asConstant(), color.asConstant());
+
+ layer->setFillOutlineColor(color);
+ EXPECT_EQ(layer->getFillOutlineColor().asConstant(), color.asConstant());
+
+ layer->setFillOpacity(opacity);
+ EXPECT_EQ(layer->getFillOpacity().asConstant(), opacity.asConstant());
+
+ layer->setFillPattern(pattern);
+ EXPECT_EQ(layer->getFillPattern().asConstant(), pattern.asConstant());
+
+ layer->setFillAntialias(antialias);
+ EXPECT_EQ(layer->getFillAntialias().asConstant(), antialias.asConstant());
+
+ layer->setFillTranslate(translate);
+ EXPECT_EQ(layer->getFillTranslate().asConstant(), translate.asConstant());
+
+ layer->setFillTranslateAnchor(translateAnchor);
+ EXPECT_EQ(layer->getFillTranslateAnchor().asConstant(), translateAnchor.asConstant());
+}
+
+TEST(Layer, LineProperties) {
+ auto layer = std::make_unique<LineLayer>("line", "source");
+ EXPECT_TRUE(layer->is<LineLayer>());
+
+ // Layout properties
+
+ layer->setLineCap(lineCap);
+ EXPECT_EQ(layer->getLineCap().asConstant(), lineCap.asConstant());
+
+ layer->setLineJoin(lineJoin);
+ EXPECT_EQ(layer->getLineJoin().asConstant(), lineJoin.asConstant());
+
+ layer->setLineMiterLimit(miterLimit);
+ EXPECT_EQ(layer->getLineMiterLimit().asConstant(), miterLimit.asConstant());
+
+ layer->setLineRoundLimit(roundLimit);
+ EXPECT_EQ(layer->getLineRoundLimit().asConstant(), roundLimit.asConstant());
+
+ // Paint properties
+
+ layer->setLineColor(color);
+ EXPECT_EQ(layer->getLineColor().asConstant(), color.asConstant());
+
+ layer->setLineOpacity(opacity);
+ EXPECT_EQ(layer->getLineOpacity().asConstant(), opacity.asConstant());
+
+ layer->setLineTranslate(translate);
+ EXPECT_EQ(layer->getLineTranslate().asConstant(), translate.asConstant());
+
+ layer->setLineTranslateAnchor(translateAnchor);
+ EXPECT_EQ(layer->getLineTranslateAnchor().asConstant(), translateAnchor.asConstant());
+
+ layer->setLineWidth(width);
+ EXPECT_EQ(layer->getLineWidth().asConstant(), width.asConstant());
+
+ layer->setLineGapWidth(gapWidth);
+ EXPECT_EQ(layer->getLineGapWidth().asConstant(), gapWidth.asConstant());
+
+ layer->setLineOffset(offset);
+ EXPECT_EQ(layer->getLineOffset().asConstant(), offset.asConstant());
+
+ layer->setLineBlur(blur);
+ EXPECT_EQ(layer->getLineBlur().asConstant(), blur.asConstant());
+
+ layer->setLineDasharray(dashArray);
+ EXPECT_EQ(layer->getLineDasharray().asConstant(), dashArray.asConstant());
+
+ layer->setLinePattern(pattern);
+ EXPECT_EQ(layer->getLinePattern().asConstant(), pattern.asConstant());
+}
+
+TEST(Layer, RasterProperties) {
+ auto layer = std::make_unique<RasterLayer>("raster", "source");
+ EXPECT_TRUE(layer->is<RasterLayer>());
+
+ // Paint properties
+
+ layer->setRasterOpacity(opacity);
+ EXPECT_EQ(layer->getRasterOpacity().asConstant(), opacity.asConstant());
+
+ layer->setRasterHueRotate(hueRotate);
+ EXPECT_EQ(layer->getRasterHueRotate().asConstant(), hueRotate.asConstant());
+
+ layer->setRasterBrightnessMin(brightness);
+ EXPECT_EQ(layer->getRasterBrightnessMin().asConstant(), brightness.asConstant());
+
+ layer->setRasterBrightnessMax(brightness);
+ EXPECT_EQ(layer->getRasterBrightnessMax().asConstant(), brightness.asConstant());
+
+ layer->setRasterSaturation(saturation);
+ EXPECT_EQ(layer->getRasterSaturation().asConstant(), saturation.asConstant());
+
+ layer->setRasterContrast(contrast);
+ EXPECT_EQ(layer->getRasterContrast().asConstant(), contrast.asConstant());
+
+ layer->setRasterFadeDuration(duration);
+ EXPECT_EQ(layer->getRasterFadeDuration().asConstant(), duration.asConstant());
+}
diff --git a/test/style/style_parser.cpp b/test/style/style_parser.cpp
index 5f730a8373..b7806c11cc 100644
--- a/test/style/style_parser.cpp
+++ b/test/style/style_parser.cpp
@@ -3,6 +3,8 @@
#include <mbgl/style/parser.hpp>
#include <mbgl/util/io.hpp>
+#include <mbgl/util/enum.hpp>
+#include <mbgl/util/tileset.hpp>
#include <rapidjson/document.h>
@@ -26,7 +28,7 @@ TEST_P(StyleParserTest, ParseStyle) {
ASSERT_FALSE(infoDoc.HasParseError());
ASSERT_TRUE(infoDoc.IsObject());
- FixtureLogObserver* observer = new FixtureLogObserver();
+ auto observer = new FixtureLogObserver();
Log::setObserver(std::unique_ptr<Log::Observer>(observer));
style::Parser parser;
@@ -46,8 +48,8 @@ TEST_P(StyleParserTest, ParseStyle) {
const uint32_t count = js_entry[rapidjson::SizeType(0)].GetUint();
const FixtureLogObserver::LogMessage message {
- EventSeverityClass(js_entry[rapidjson::SizeType(1)].GetString()),
- EventClass(js_entry[rapidjson::SizeType(2)].GetString()),
+ *Enum<EventSeverity>::toEnum(js_entry[rapidjson::SizeType(1)].GetString()),
+ *Enum<Event>::toEnum(js_entry[rapidjson::SizeType(2)].GetString()),
int64_t(-1),
js_entry[rapidjson::SizeType(3)].GetString()
};
@@ -61,7 +63,7 @@ TEST_P(StyleParserTest, ParseStyle) {
std::cerr << "Unchecked Log Messages (" << base << "/" << name << "): " << std::endl << unchecked;
}
- ASSERT_EQ(0ul, unchecked.size());
+ ASSERT_EQ(0u, unchecked.size());
}
}
@@ -81,45 +83,15 @@ INSTANTIATE_TEST_CASE_P(StyleParser, StyleParserTest, ::testing::ValuesIn([] {
closedir(dir);
}
- EXPECT_GT(names.size(), 0ul);
+ EXPECT_GT(names.size(), 0u);
return names;
}()));
-TEST(StyleParser, ParseTileJSONRaster) {
- auto result = style::parseTileJSON(
- util::read_file("test/fixtures/style_parser/tilejson.raster.json"),
- "mapbox://mapbox.satellite",
- SourceType::Raster,
- 256);
-
- EXPECT_EQ(0, result->minZoom);
- EXPECT_EQ(15, result->maxZoom);
- EXPECT_EQ("attribution", result->attribution);
-#if !defined(__ANDROID__) && !defined(__APPLE__)
- EXPECT_EQ("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.webp", result->tiles[0]);
-#else
- EXPECT_EQ("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", result->tiles[0]);
-#endif
-}
-
-TEST(StyleParser, ParseTileJSONVector) {
- auto result = style::parseTileJSON(
- util::read_file("test/fixtures/style_parser/tilejson.vector.json"),
- "mapbox://mapbox.streets",
- SourceType::Vector,
- 256);
-
- EXPECT_EQ(0, result->minZoom);
- EXPECT_EQ(15, result->maxZoom);
- EXPECT_EQ("attribution", result->attribution);
- EXPECT_EQ("mapbox://tiles/mapbox.streets/{z}/{x}/{y}.vector.pbf", result->tiles[0]);
-}
-
TEST(StyleParser, FontStacks) {
style::Parser parser;
parser.parse(util::read_file("test/fixtures/style_parser/font_stacks.json"));
auto result = parser.fontStacks();
- ASSERT_EQ(3, result.size());
+ ASSERT_EQ(3u, result.size());
ASSERT_EQ(FontStack({"a"}), result[0]);
ASSERT_EQ(FontStack({"a", "b"}), result[1]);
ASSERT_EQ(FontStack({"a", "b", "c"}), result[2]);
diff --git a/test/style/tile_source.cpp b/test/style/tile_source.cpp
new file mode 100644
index 0000000000..35d037a049
--- /dev/null
+++ b/test/style/tile_source.cpp
@@ -0,0 +1,37 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/style/tile_source_impl.hpp>
+#include <mbgl/util/io.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+TEST(TileSourceImpl, ParseTileJSONRaster) {
+ auto result = TileSourceImpl::parseTileJSON(
+ util::read_file("test/fixtures/style_parser/tilejson.raster.json"),
+ "mapbox://mapbox.satellite",
+ SourceType::Raster,
+ 256);
+
+ EXPECT_EQ(0, result.zoomRange.min);
+ EXPECT_EQ(15, result.zoomRange.max);
+ EXPECT_EQ("attribution", result.attribution);
+#if !defined(__ANDROID__) && !defined(__APPLE__)
+ EXPECT_EQ("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.webp", result.tiles[0]);
+#else
+ EXPECT_EQ("mapbox://tiles/mapbox.satellite/{z}/{x}/{y}{ratio}.png", result.tiles[0]);
+#endif
+}
+
+TEST(TileSourceImpl, ParseTileJSONVector) {
+ auto result = TileSourceImpl::parseTileJSON(
+ util::read_file("test/fixtures/style_parser/tilejson.vector.json"),
+ "mapbox://mapbox.streets",
+ SourceType::Vector,
+ 256);
+
+ EXPECT_EQ(0, result.zoomRange.min);
+ EXPECT_EQ(15, result.zoomRange.max);
+ EXPECT_EQ("attribution", result.attribution);
+ EXPECT_EQ("mapbox://tiles/mapbox.streets/{z}/{x}/{y}.vector.pbf", result.tiles[0]);
+}
diff --git a/test/test.gypi b/test/test.gypi
index b7e7b5c32b..82bd02bd83 100644
--- a/test/test.gypi
+++ b/test/test.gypi
@@ -22,6 +22,7 @@
'util/geo.cpp',
'util/image.cpp',
'util/mapbox.cpp',
+ 'util/memory.cpp',
'util/merge_lines.cpp',
'util/run_loop.cpp',
'util/number_conversions.cpp',
@@ -32,6 +33,7 @@
'util/timer.cpp',
'util/token.cpp',
'util/work_queue.cpp',
+ 'util/projection.cpp',
'algorithm/covered_by_children.cpp',
'algorithm/generate_clip_ids.cpp',
@@ -55,7 +57,9 @@
'math/minmax.cpp',
'math/clamp.cpp',
- 'tile/geometry_tile.cpp',
+ 'text/quads.cpp',
+
+ 'tile/geometry_tile_data.cpp',
'tile/tile_id.cpp',
'storage/offline.cpp',
@@ -72,6 +76,7 @@
'style/source.cpp',
'style/style.cpp',
'style/style_layer.cpp',
+ 'style/tile_source.cpp',
'style/filter.cpp',
'style/functions.cpp',
'style/style_parser.cpp',
@@ -98,6 +103,8 @@
'<@(boost_cflags)',
'<@(sqlite_cflags)',
'<@(geojsonvt_cflags)',
+ '<@(supercluster_cflags)',
+ '<@(kdbush_cflags)',
'<@(rapidjson_cflags)',
'<@(pixelmatch_cflags)',
'<@(earcut_cflags)',
@@ -108,8 +115,7 @@
],
'libraries': [
'<@(gtest_static_libs)',
- '<@(sqlite_static_libs)',
- '<@(geojsonvt_static_libs)',
+ '<@(sqlite_static_libs)'
],
},
diff --git a/test/text/quads.cpp b/test/text/quads.cpp
new file mode 100644
index 0000000000..2dd6a68cad
--- /dev/null
+++ b/test/text/quads.cpp
@@ -0,0 +1,265 @@
+#include <mbgl/geometry/anchor.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/test/util.hpp>
+#include <mbgl/text/quads.hpp>
+#include <mbgl/text/shaping.hpp>
+#include <mbgl/text/glyph.hpp>
+#include <mbgl/style/layers/symbol_layer_properties.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+TEST(getIconQuads, normal) {
+ SymbolLayoutProperties layout;
+ Anchor anchor(2.0, 3.0, 0.0, 0.5f, 0);
+ SpriteAtlasElement image = {
+ Rect<uint16_t>( 0, 0, 15, 11 ),
+ std::shared_ptr<const SpriteImage>(),
+ 1.0f
+ };
+ PositionedIcon shapedIcon(image, -5.0, 6.0, -7.0, 8.0);
+ GeometryCoordinates line;
+ Shaping shapedText;
+
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads.size(), 1u);
+ ASSERT_EQ(quads[0].anchorPoint.x, 2);
+ ASSERT_EQ(quads[0].anchorPoint.y, 3);
+ ASSERT_EQ(quads[0].tl.x, -8);
+ ASSERT_EQ(quads[0].tl.y, -6);
+ ASSERT_EQ(quads[0].tr.x, 7);
+ ASSERT_EQ(quads[0].tr.y, -6);
+ ASSERT_EQ(quads[0].bl.x, -8);
+ ASSERT_EQ(quads[0].bl.y, 5);
+ ASSERT_EQ(quads[0].br.x, 7);
+ ASSERT_EQ(quads[0].br.y, 5);
+ ASSERT_EQ(quads[0].anchorAngle, 0.0f);
+ ASSERT_EQ(quads[0].glyphAngle, 0.0f);
+ ASSERT_EQ(quads[0].minScale, 0.5f);
+}
+
+TEST(getIconQuads, style) {
+ Anchor anchor(0.0, 0.0, 0.0, 0.5f, 0);
+ SpriteAtlasElement image = {
+ Rect<uint16_t>( 0, 0, 20, 20 ),
+ std::shared_ptr<const SpriteImage>(),
+ 1.0f
+ };
+ PositionedIcon shapedIcon(image, -10.0, 10.0, -10.0, 10.0);
+ GeometryCoordinates line;
+ Shaping shapedText;
+ shapedText.top = -10.0f;
+ shapedText.bottom = 30.0f;
+ shapedText.left = -60.0f;
+ shapedText.right = 20.0f;
+ shapedText.positionedGlyphs.emplace_back(PositionedGlyph(32, 0.0f, 0.0f));
+
+ // none
+ {
+ SymbolLayoutProperties layout;
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads.size(), 1u);
+ ASSERT_EQ(quads[0].anchorPoint.x, 0);
+ ASSERT_EQ(quads[0].anchorPoint.y, 0);
+ ASSERT_EQ(quads[0].tl.x, -11);
+ ASSERT_EQ(quads[0].tl.y, -11);
+ ASSERT_EQ(quads[0].tr.x, 9);
+ ASSERT_EQ(quads[0].tr.y, -11);
+ ASSERT_EQ(quads[0].bl.x, -11);
+ ASSERT_EQ(quads[0].bl.y, 9);
+ ASSERT_EQ(quads[0].br.x, 9);
+ ASSERT_EQ(quads[0].br.y, 9);
+ ASSERT_EQ(quads[0].anchorAngle, 0.0f);
+ ASSERT_EQ(quads[0].glyphAngle, 0.0f);
+ ASSERT_EQ(quads[0].minScale, 0.5f);
+ }
+
+ // width
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(24.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Width);
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -60);
+ ASSERT_EQ(quads[0].tl.y, 0);
+ ASSERT_EQ(quads[0].tr.x, 20);
+ ASSERT_EQ(quads[0].tr.y, 0);
+ ASSERT_EQ(quads[0].bl.x, -60);
+ ASSERT_EQ(quads[0].bl.y, 20);
+ ASSERT_EQ(quads[0].br.x, 20);
+ ASSERT_EQ(quads[0].br.y, 20);
+ }
+
+ // width x textSize
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(12.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Width);
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -30);
+ ASSERT_EQ(quads[0].tl.y, -5);
+ ASSERT_EQ(quads[0].tr.x, 10);
+ ASSERT_EQ(quads[0].tr.y, -5);
+ ASSERT_EQ(quads[0].bl.x, -30);
+ ASSERT_EQ(quads[0].bl.y, 15);
+ ASSERT_EQ(quads[0].br.x, 10);
+ ASSERT_EQ(quads[0].br.y, 15);
+ }
+
+ // width x textSize + padding
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(12.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Width);
+ layout.iconTextFitPadding.value[0] = 5.0f;
+ layout.iconTextFitPadding.value[1] = 10.0f;
+ layout.iconTextFitPadding.value[2] = 5.0f;
+ layout.iconTextFitPadding.value[3] = 10.0f;
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -40);
+ ASSERT_EQ(quads[0].tl.y, -10);
+ ASSERT_EQ(quads[0].tr.x, 20);
+ ASSERT_EQ(quads[0].tr.y, -10);
+ ASSERT_EQ(quads[0].bl.x, -40);
+ ASSERT_EQ(quads[0].bl.y, 20);
+ ASSERT_EQ(quads[0].br.x, 20);
+ ASSERT_EQ(quads[0].br.y, 20);
+ }
+
+ // height
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(24.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Height);
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -30);
+ ASSERT_EQ(quads[0].tl.y, -10);
+ ASSERT_EQ(quads[0].tr.x, -10);
+ ASSERT_EQ(quads[0].tr.y, -10);
+ ASSERT_EQ(quads[0].bl.x, -30);
+ ASSERT_EQ(quads[0].bl.y, 30);
+ ASSERT_EQ(quads[0].br.x, -10);
+ ASSERT_EQ(quads[0].br.y, 30);
+ }
+
+ // height x textSize
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(12.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Height);
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -20);
+ ASSERT_EQ(quads[0].tl.y, -5);
+ ASSERT_EQ(quads[0].tr.x, 0);
+ ASSERT_EQ(quads[0].tr.y, -5);
+ ASSERT_EQ(quads[0].bl.x, -20);
+ ASSERT_EQ(quads[0].bl.y, 15);
+ ASSERT_EQ(quads[0].br.x, 0);
+ ASSERT_EQ(quads[0].br.y, 15);
+ }
+
+ // height x textSize + padding
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(12.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Height);
+ layout.iconTextFitPadding.value[0] = 5.0f;
+ layout.iconTextFitPadding.value[1] = 10.0f;
+ layout.iconTextFitPadding.value[2] = 5.0f;
+ layout.iconTextFitPadding.value[3] = 10.0f;
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -30);
+ ASSERT_EQ(quads[0].tl.y, -10);
+ ASSERT_EQ(quads[0].tr.x, 10);
+ ASSERT_EQ(quads[0].tr.y, -10);
+ ASSERT_EQ(quads[0].bl.x, -30);
+ ASSERT_EQ(quads[0].bl.y, 20);
+ ASSERT_EQ(quads[0].br.x, 10);
+ ASSERT_EQ(quads[0].br.y, 20);
+ }
+
+ // both
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(24.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Both);
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -60);
+ ASSERT_EQ(quads[0].tl.y, -10);
+ ASSERT_EQ(quads[0].tr.x, 20);
+ ASSERT_EQ(quads[0].tr.y, -10);
+ ASSERT_EQ(quads[0].bl.x, -60);
+ ASSERT_EQ(quads[0].bl.y, 30);
+ ASSERT_EQ(quads[0].br.x, 20);
+ ASSERT_EQ(quads[0].br.y, 30);
+ }
+
+ // both x textSize
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(12.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Both);
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -30);
+ ASSERT_EQ(quads[0].tl.y, -5);
+ ASSERT_EQ(quads[0].tr.x, 10);
+ ASSERT_EQ(quads[0].tr.y, -5);
+ ASSERT_EQ(quads[0].bl.x, -30);
+ ASSERT_EQ(quads[0].bl.y, 15);
+ ASSERT_EQ(quads[0].br.x, 10);
+ ASSERT_EQ(quads[0].br.y, 15);
+ }
+
+ // both x textSize + padding
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(12.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Both);
+ layout.iconTextFitPadding.value[0] = 5.0f;
+ layout.iconTextFitPadding.value[1] = 10.0f;
+ layout.iconTextFitPadding.value[2] = 5.0f;
+ layout.iconTextFitPadding.value[3] = 10.0f;
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -40);
+ ASSERT_EQ(quads[0].tl.y, -10);
+ ASSERT_EQ(quads[0].tr.x, 20);
+ ASSERT_EQ(quads[0].tr.y, -10);
+ ASSERT_EQ(quads[0].bl.x, -40);
+ ASSERT_EQ(quads[0].bl.y, 20);
+ ASSERT_EQ(quads[0].br.x, 20);
+ ASSERT_EQ(quads[0].br.y, 20);
+ }
+
+ // both x textSize + padding t/r/b/l
+ {
+ SymbolLayoutProperties layout;
+ layout.textSize = LayoutProperty<float>(12.0f);
+ layout.iconTextFit = LayoutProperty<IconTextFitType>(IconTextFitType::Both);
+ layout.iconTextFitPadding.value[0] = 0.0f;
+ layout.iconTextFitPadding.value[1] = 5.0f;
+ layout.iconTextFitPadding.value[2] = 10.0f;
+ layout.iconTextFitPadding.value[3] = 15.0f;
+ SymbolQuads quads = getIconQuads(anchor, shapedIcon, line, layout, false, shapedText);
+
+ ASSERT_EQ(quads[0].tl.x, -45);
+ ASSERT_EQ(quads[0].tl.y, -5);
+ ASSERT_EQ(quads[0].tr.x, 15);
+ ASSERT_EQ(quads[0].tr.y, -5);
+ ASSERT_EQ(quads[0].bl.x, -45);
+ ASSERT_EQ(quads[0].bl.y, 25);
+ ASSERT_EQ(quads[0].br.x, 15);
+ ASSERT_EQ(quads[0].br.y, 25);
+ }
+}
+
diff --git a/test/tile/geometry_tile.cpp b/test/tile/geometry_tile_data.cpp
index c94b20cee6..6e118d6fd5 100644
--- a/test/tile/geometry_tile.cpp
+++ b/test/tile/geometry_tile_data.cpp
@@ -1,32 +1,32 @@
#include <mbgl/test/util.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
using namespace mbgl;
-TEST(GeometryTile, classifyRings1) {
+TEST(GeometryTileData, classifyRings1) {
std::vector<GeometryCollection> polygons = classifyRings({
{ {0, 0}, {0, 40}, {40, 40}, {40, 0}, {0, 0} }
});
// output: 1 polygon
- ASSERT_EQ(polygons.size(), 1);
+ ASSERT_EQ(polygons.size(), 1u);
// output: polygon 1 has 1 exterior
- ASSERT_EQ(polygons[0].size(), 1);
+ ASSERT_EQ(polygons[0].size(), 1u);
}
-TEST(GeometryTile, classifyRings2) {
+TEST(GeometryTileData, classifyRings2) {
std::vector<GeometryCollection> polygons = classifyRings({
{ {0, 0}, {0, 40}, {40, 40}, {40, 0}, {0, 0} },
{ {10, 10}, {20, 10}, {20, 20}, {10, 10} }
});
// output: 1 polygon
- ASSERT_EQ(polygons.size(), 1);
+ ASSERT_EQ(polygons.size(), 1u);
// output: polygon 1 has 1 exterior, 1 interior
- ASSERT_EQ(polygons[0].size(), 2);
+ ASSERT_EQ(polygons[0].size(), 2u);
}
-TEST(GeometryTile, limitHoles1) {
+TEST(GeometryTileData, limitHoles1) {
GeometryCollection polygon = {
{ {0, 0}, {0, 40}, {40, 40}, {40, 0}, {0, 0} },
{ {30, 30}, {32, 30}, {32, 32}, {30, 30} },
@@ -36,14 +36,14 @@ TEST(GeometryTile, limitHoles1) {
limitHoles(polygon, 1);
// output: polygon 1 has 1 exterior, 1 interior
- ASSERT_EQ(polygon.size(), 2);
+ ASSERT_EQ(polygon.size(), 2u);
// ensure we've kept the right rings (ones with largest areas)
ASSERT_EQ(polygon[0][0].x, 0);
ASSERT_EQ(polygon[1][0].x, 10);
}
-TEST(GeometryTile, limitHoles2) {
+TEST(GeometryTileData, limitHoles2) {
GeometryCollection polygon = {
{ {0, 0}, {0, 40}, {40, 40}, {40, 0}, {0, 0} },
{ {10, 10}, {20, 10}, {20, 20}, {10, 10} },
@@ -53,7 +53,7 @@ TEST(GeometryTile, limitHoles2) {
limitHoles(polygon, 1);
// output: polygon 1 has 1 exterior, 1 interior
- ASSERT_EQ(polygon.size(), 2);
+ ASSERT_EQ(polygon.size(), 2u);
// ensure we've kept the right rings (ones with largest areas)
ASSERT_EQ(polygon[0][0].x, 0);
diff --git a/test/tile/tile_id.cpp b/test/tile/tile_id.cpp
index e5a1403d20..1ef19fea0e 100644
--- a/test/tile/tile_id.cpp
+++ b/test/tile/tile_id.cpp
@@ -6,9 +6,9 @@ using namespace mbgl;
TEST(TileID, Canonical) {
CanonicalTileID id_0_0_0(0, 0, 0);
- EXPECT_EQ(0, id_0_0_0.z);
- EXPECT_EQ(0, id_0_0_0.x);
- EXPECT_EQ(0, id_0_0_0.y);
+ EXPECT_EQ(0u, id_0_0_0.z);
+ EXPECT_EQ(0u, id_0_0_0.x);
+ EXPECT_EQ(0u, id_0_0_0.y);
EXPECT_TRUE(CanonicalTileID(4, 2, 3) == CanonicalTileID(4, 2, 3));
EXPECT_FALSE(CanonicalTileID(4, 2, 3) != CanonicalTileID(4, 2, 3));
@@ -157,11 +157,11 @@ TEST(TileID, Overscaled) {
EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(7, { 4, 2, 3 }));
EXPECT_FALSE(OverscaledTileID(7, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3));
- EXPECT_EQ(8, OverscaledTileID(7, { 4, 2, 3 }).overscaleFactor());
- EXPECT_EQ(4, OverscaledTileID(6, { 4, 2, 3 }).overscaleFactor());
- EXPECT_EQ(2, OverscaledTileID(5, { 4, 2, 3 }).overscaleFactor());
- EXPECT_EQ(1, OverscaledTileID(4, { 4, 2, 3 }).overscaleFactor());
- EXPECT_EQ(2147483648, OverscaledTileID(31, { 0, 0, 0 }).overscaleFactor());
+ EXPECT_EQ(8u, OverscaledTileID(7, { 4, 2, 3 }).overscaleFactor());
+ EXPECT_EQ(4u, OverscaledTileID(6, { 4, 2, 3 }).overscaleFactor());
+ EXPECT_EQ(2u, OverscaledTileID(5, { 4, 2, 3 }).overscaleFactor());
+ EXPECT_EQ(1u, OverscaledTileID(4, { 4, 2, 3 }).overscaleFactor());
+ EXPECT_EQ(2147483648u, OverscaledTileID(31, { 0, 0, 0 }).overscaleFactor());
EXPECT_EQ(OverscaledTileID(0, { 0, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(0));
EXPECT_EQ(OverscaledTileID(1, { 1, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(1));
@@ -214,58 +214,58 @@ TEST(TileID, Overscaled) {
TEST(TileID, Unwrapped) {
UnwrappedTileID id_0_0_0(0, 0, 0);
- EXPECT_EQ(0, id_0_0_0.canonical.z);
- EXPECT_EQ(0, id_0_0_0.canonical.x);
- EXPECT_EQ(0, id_0_0_0.canonical.y);
+ EXPECT_EQ(0u, id_0_0_0.canonical.z);
+ EXPECT_EQ(0u, id_0_0_0.canonical.x);
+ EXPECT_EQ(0u, id_0_0_0.canonical.y);
EXPECT_EQ(0, id_0_0_0.wrap);
UnwrappedTileID id_0_1_0(0, 1, 0);
- EXPECT_EQ(0, id_0_1_0.canonical.z);
- EXPECT_EQ(0, id_0_1_0.canonical.x);
- EXPECT_EQ(0, id_0_1_0.canonical.y);
+ EXPECT_EQ(0u, id_0_1_0.canonical.z);
+ EXPECT_EQ(0u, id_0_1_0.canonical.x);
+ EXPECT_EQ(0u, id_0_1_0.canonical.y);
EXPECT_EQ(1, id_0_1_0.wrap);
UnwrappedTileID id_0_4_0(0, 4, 0);
- EXPECT_EQ(0, id_0_4_0.canonical.z);
- EXPECT_EQ(0, id_0_4_0.canonical.x);
- EXPECT_EQ(0, id_0_4_0.canonical.y);
+ EXPECT_EQ(0u, id_0_4_0.canonical.z);
+ EXPECT_EQ(0u, id_0_4_0.canonical.x);
+ EXPECT_EQ(0u, id_0_4_0.canonical.y);
EXPECT_EQ(4, id_0_4_0.wrap);
UnwrappedTileID id_0_n1_0(0, -1, 0);
- EXPECT_EQ(0, id_0_n1_0.canonical.z);
- EXPECT_EQ(0, id_0_n1_0.canonical.x);
- EXPECT_EQ(0, id_0_n1_0.canonical.y);
+ EXPECT_EQ(0u, id_0_n1_0.canonical.z);
+ EXPECT_EQ(0u, id_0_n1_0.canonical.x);
+ EXPECT_EQ(0u, id_0_n1_0.canonical.y);
EXPECT_EQ(-1, id_0_n1_0.wrap);
UnwrappedTileID id_0_0_1(0, 0, 1);
- EXPECT_EQ(0, id_0_0_1.canonical.z);
- EXPECT_EQ(0, id_0_0_1.canonical.x);
- EXPECT_EQ(0, id_0_0_1.canonical.y);
+ EXPECT_EQ(0u, id_0_0_1.canonical.z);
+ EXPECT_EQ(0u, id_0_0_1.canonical.x);
+ EXPECT_EQ(0u, id_0_0_1.canonical.y);
EXPECT_EQ(0, id_0_0_1.wrap);
UnwrappedTileID id_0_0_n1(0, 0, -1);
- EXPECT_EQ(0, id_0_0_n1.canonical.z);
- EXPECT_EQ(0, id_0_0_n1.canonical.x);
- EXPECT_EQ(0, id_0_0_n1.canonical.y);
+ EXPECT_EQ(0u, id_0_0_n1.canonical.z);
+ EXPECT_EQ(0u, id_0_0_n1.canonical.x);
+ EXPECT_EQ(0u, id_0_0_n1.canonical.y);
EXPECT_EQ(0, id_0_0_n1.wrap);
UnwrappedTileID id_18_262143_0(18, 262143, 0);
- EXPECT_EQ(18, id_18_262143_0.canonical.z);
- EXPECT_EQ(262143, id_18_262143_0.canonical.x);
- EXPECT_EQ(0, id_18_262143_0.canonical.y);
+ EXPECT_EQ(18u, id_18_262143_0.canonical.z);
+ EXPECT_EQ(262143u, id_18_262143_0.canonical.x);
+ EXPECT_EQ(0u, id_18_262143_0.canonical.y);
EXPECT_EQ(0, id_18_262143_0.wrap);
UnwrappedTileID id_18_262144_0(18, 262144, 0);
- EXPECT_EQ(18, id_18_262144_0.canonical.z);
- EXPECT_EQ(0, id_18_262144_0.canonical.x);
- EXPECT_EQ(0, id_18_262144_0.canonical.y);
+ EXPECT_EQ(18u, id_18_262144_0.canonical.z);
+ EXPECT_EQ(0u, id_18_262144_0.canonical.x);
+ EXPECT_EQ(0u, id_18_262144_0.canonical.y);
EXPECT_EQ(1, id_18_262144_0.wrap);
UnwrappedTileID a(2, 4, 3);
UnwrappedTileID b(a);
- EXPECT_EQ(2, b.canonical.z);
- EXPECT_EQ(0, b.canonical.x);
- EXPECT_EQ(3, b.canonical.y);
+ EXPECT_EQ(2u, b.canonical.z);
+ EXPECT_EQ(0u, b.canonical.x);
+ EXPECT_EQ(3u, b.canonical.y);
EXPECT_EQ(1, b.wrap);
EXPECT_TRUE(UnwrappedTileID(0, 0, 0) < UnwrappedTileID(1, 0, 0));
diff --git a/test/util/async_task.cpp b/test/util/async_task.cpp
index ad65bfad78..78dc79dd19 100644
--- a/test/util/async_task.cpp
+++ b/test/util/async_task.cpp
@@ -49,7 +49,7 @@ TEST(AsyncTask, RequestCoalescing) {
loop.runOnce();
- EXPECT_EQ(count, 1);
+ EXPECT_EQ(count, 1u);
}
TEST(AsyncTask, DestroyShouldNotRunQueue) {
@@ -61,7 +61,7 @@ TEST(AsyncTask, DestroyShouldNotRunQueue) {
async->send();
async.reset();
- EXPECT_EQ(count, 0);
+ EXPECT_EQ(count, 0u);
}
TEST(AsyncTask, DestroyAfterSignaling) {
@@ -114,7 +114,7 @@ TEST(AsyncTask, RequestCoalescingMultithreaded) {
loop.runOnce();
- EXPECT_EQ(count, 1);
+ EXPECT_EQ(count, 1u);
}
TEST(AsyncTask, ThreadSafety) {
@@ -149,5 +149,5 @@ TEST(AsyncTask, ThreadSafety) {
// We expect here more than 1 but 1 would also be
// a valid result, although very unlikely (I hope).
- EXPECT_GT(count, 0);
+ EXPECT_GT(count, 0u);
}
diff --git a/test/util/image.cpp b/test/util/image.cpp
index 67a79cf9dd..b2814e66da 100644
--- a/test/util/image.cpp
+++ b/test/util/image.cpp
@@ -68,21 +68,21 @@ TEST(Image, PNGReadProfileAlpha) {
TEST(Image, PNGTile) {
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.png"));
- EXPECT_EQ(256, image.width);
- EXPECT_EQ(256, image.height);
+ EXPECT_EQ(256u, image.width);
+ EXPECT_EQ(256u, image.height);
}
TEST(Image, JPEGTile) {
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.jpeg"));
- EXPECT_EQ(256, image.width);
- EXPECT_EQ(256, image.height);
+ EXPECT_EQ(256u, image.width);
+ EXPECT_EQ(256u, image.height);
}
#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(QT_IMAGE_DECODERS)
TEST(Image, WebPTile) {
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.webp"));
- EXPECT_EQ(256, image.width);
- EXPECT_EQ(256, image.height);
+ EXPECT_EQ(256u, image.width);
+ EXPECT_EQ(256u, image.height);
}
#endif // !defined(__ANDROID__) && !defined(__APPLE__) && !defined(QT_IMAGE_DECODERS)
diff --git a/test/util/mapbox.cpp b/test/util/mapbox.cpp
index 35c478287c..9341b695f3 100644
--- a/test/util/mapbox.cpp
+++ b/test/util/mapbox.cpp
@@ -7,7 +7,6 @@
using namespace mbgl;
-
TEST(Mapbox, SourceURL) {
EXPECT_EQ(
"https://api.mapbox.com/v4/user.map.json?access_token=key&secure",
diff --git a/test/util/memory.cpp b/test/util/memory.cpp
new file mode 100644
index 0000000000..bc20200916
--- /dev/null
+++ b/test/util/memory.cpp
@@ -0,0 +1,228 @@
+#include <mbgl/test/stub_file_source.hpp>
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/platform/default/headless_display.hpp>
+#include <mbgl/platform/default/headless_view.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace mbgl;
+using namespace std::literals::string_literals;
+
+long getRSS() {
+ auto statm = util::read_file("/proc/self/statm");
+
+ std::vector<std::string> stats;
+ std::istringstream stream(statm);
+
+ std::copy(std::istream_iterator<std::string>(stream),
+ std::istream_iterator<std::string>(),
+ std::back_inserter(stats));
+
+ return std::stol(stats[1]) * getpagesize();
+}
+
+bool isUsingJemalloc() {
+ const char* preload = getenv("LD_PRELOAD");
+
+ if (preload) {
+ return std::string(preload).find("libjemalloc.so") != std::string::npos;
+ } else {
+ return false;
+ }
+}
+
+class MemoryTest {
+public:
+ MemoryTest() {
+ fileSource.styleResponse = [&](const Resource& res) { return response("style_" + getType(res) + ".json");};
+ fileSource.tileResponse = [&](const Resource& res) { return response(getType(res) + ".tile"); };
+ fileSource.sourceResponse = [&](const Resource& res) { return response("source_" + getType(res) + ".json"); };
+ fileSource.glyphsResponse = [&](const Resource&) { return response("glyphs.pbf"); };
+ fileSource.spriteJSONResponse = [&](const Resource&) { return response("sprite.json"); };
+ fileSource.spriteImageResponse = [&](const Resource&) { return response("sprite.png"); };
+ }
+
+ util::RunLoop runLoop;
+ std::shared_ptr<HeadlessDisplay> display { std::make_shared<mbgl::HeadlessDisplay>() };
+ HeadlessView view { display, 2 };
+ StubFileSource fileSource;
+
+private:
+ Response response(const std::string& path) {
+ Response result;
+
+ auto it = cache.find(path);
+ if (it != cache.end()) {
+ result.data = it->second;
+ } else {
+ auto data = std::make_shared<std::string>(
+ util::read_file("test/fixtures/resources/"s + path));
+
+ cache.insert(it, std::make_pair(path, data));
+ result.data = data;
+ }
+
+ return result;
+ }
+
+ std::string getType(const Resource& res) {
+ if (res.url.find("satellite") != std::string::npos) {
+ return "raster";
+ } else {
+ return "vector";
+ }
+ };
+
+ std::unordered_map<std::string, std::shared_ptr<std::string>> cache;
+};
+
+TEST(Memory, Vector) {
+ MemoryTest test;
+
+ Map map(test.view, test.fileSource, MapMode::Still);
+ map.setZoom(16); // more map features
+ map.setStyleURL("mapbox://streets");
+
+ test::render(map);
+}
+
+TEST(Memory, Raster) {
+ MemoryTest test;
+
+ Map map(test.view, test.fileSource, MapMode::Still);
+ map.setStyleURL("mapbox://satellite");
+
+ test::render(map);
+}
+
+// This test will render 3 map objects alternating
+// between a raster and a vector style. Memory is
+// expected to grow between the renderings due to
+// fragmentation. A good allocator for Mapbox GL
+// Native will keep memory growth within acceptable
+// levels and stable in the long run.
+TEST(Memory, Fragmentation) {
+ if (!isUsingJemalloc()) {
+ return;
+ }
+
+ MemoryTest test;
+
+ Map map1(test.view, test.fileSource, MapMode::Still);
+ Map map2(test.view, test.fileSource, MapMode::Still);
+ Map map3(test.view, test.fileSource, MapMode::Still);
+
+ map1.setZoom(16);
+ map2.setZoom(16);
+ map3.setZoom(16);
+
+ auto renderMap = [&] {
+ map1.setStyleURL("mapbox://satellite");
+ test::render(map1);
+
+ map2.setStyleURL("mapbox://satellite");
+ test::render(map2);
+
+ map3.setStyleURL("mapbox://satellite");
+ test::render(map3);
+
+ map1.setStyleURL("mapbox://streets");
+ test::render(map1);
+
+ map2.setStyleURL("mapbox://streets");
+ test::render(map2);
+
+ map3.setStyleURL("mapbox://streets");
+ test::render(map3);
+ };
+
+ // Warm up buffers and cache.
+ for (unsigned i = 0; i < 5; ++i) {
+ renderMap();
+ }
+
+ long lastRSS = getRSS();
+ long memoryFragments = 0;
+
+ for (unsigned i = 0; i < 20; ++i) {
+ renderMap();
+
+ long currentRSS = getRSS();
+
+ memoryFragments += currentRSS - lastRSS;
+ lastRSS = currentRSS;
+ }
+
+ ASSERT_LT(memoryFragments, 10 * 1024 * 1024) << "\
+ Abnormal memory growth detected.";
+}
+
+// This test will measure the size of a Map object
+// after rendering a raster and a vector style. The
+// idea is to try to keep the memory footprint within
+// reasonable limits, so this test acts more like a
+// safeguard.
+TEST(Memory, Footprint) {
+ if (!isUsingJemalloc()) {
+ return;
+ }
+
+ MemoryTest test;
+
+ auto renderMap = [](Map* map, const char* style){
+ map->setZoom(16);
+
+ map->setStyleURL(style);
+ test::render(*map);
+ };
+
+ // Warm up buffers and cache.
+ for (unsigned i = 0; i < 10; ++i) {
+ Map map(test.view, test.fileSource, MapMode::Still);
+ renderMap(&map, "mapbox://streets");
+ renderMap(&map, "mapbox://satellite");
+ };
+
+ // Process close callbacks, mostly needed by
+ // libuv runloop.
+ test.runLoop.runOnce();
+
+ std::vector<std::unique_ptr<Map>> maps;
+ unsigned runs = 15;
+
+ long vectorInitialRSS = getRSS();
+ for (unsigned i = 0; i < runs; ++i) {
+ auto vector = std::make_unique<Map>(test.view, test.fileSource, MapMode::Still);
+ renderMap(vector.get(), "mapbox://streets");
+ maps.push_back(std::move(vector));
+ };
+
+ double vectorFootprint = (getRSS() - vectorInitialRSS) / double(runs);
+
+ long rasterInitialRSS = getRSS();
+ for (unsigned i = 0; i < runs; ++i) {
+ auto raster = std::make_unique<Map>(test.view, test.fileSource, MapMode::Still);
+ renderMap(raster.get(), "mapbox://satellite");
+ maps.push_back(std::move(raster));
+ };
+
+ double rasterFootprint = (getRSS() - rasterInitialRSS) / double(runs);
+
+ ASSERT_LT(vectorFootprint, 65 * 1024 * 1024) << "\
+ mbgl::Map footprint over 65MB for vector styles.";
+
+ ASSERT_LT(rasterFootprint, 25 * 1024 * 1024) << "\
+ mbgl::Map footprint over 25MB for raster styles.";
+}
diff --git a/test/util/projection.cpp b/test/util/projection.cpp
new file mode 100644
index 0000000000..5efba380b3
--- /dev/null
+++ b/test/util/projection.cpp
@@ -0,0 +1,68 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/projection.hpp>
+
+#include <limits>
+
+using namespace mbgl;
+
+TEST(Projection, MetersPerPixelAtLatitude) {
+ double zoom = 0;
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom), 78271.516964020484);
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(-util::LATITUDE_MAX, zoom), 6752.2284729446501);
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(util::LATITUDE_MAX, zoom),
+ Projection::getMetersPerPixelAtLatitude(-util::LATITUDE_MAX, zoom));
+
+ zoom = 20;
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom), 0.074645535434742435);
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(-util::LATITUDE_MAX, zoom), 0.0064394268731543065);
+
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(std::numeric_limits<double>::lowest(), zoom),
+ Projection::getMetersPerPixelAtLatitude(std::numeric_limits<double>::max(), zoom));
+
+ zoom = std::numeric_limits<double>::min();
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom),
+ Projection::getMetersPerPixelAtLatitude(0, util::MIN_ZOOM));
+
+ zoom = std::numeric_limits<double>::lowest();
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom),
+ Projection::getMetersPerPixelAtLatitude(0, util::MIN_ZOOM));
+
+ zoom = std::numeric_limits<double>::max();
+ EXPECT_DOUBLE_EQ(Projection::getMetersPerPixelAtLatitude(0, zoom),
+ Projection::getMetersPerPixelAtLatitude(0, util::MAX_ZOOM));
+}
+
+TEST(Projection, ProjectedMeters) {
+ const auto southWest = LatLng { -util::LATITUDE_MAX, -util::LONGITUDE_MAX };
+ const auto northEast = LatLng { util::LATITUDE_MAX, util::LONGITUDE_MAX };
+
+ auto latLng = LatLng {};
+ auto projectedMeters = Projection::projectedMetersForLatLng(latLng);
+ EXPECT_EQ(projectedMeters.northing, projectedMeters.easting);
+ EXPECT_EQ(latLng, Projection::latLngForProjectedMeters(projectedMeters));
+
+ latLng = LatLng { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
+ projectedMeters = Projection::projectedMetersForLatLng(latLng);
+ EXPECT_EQ(projectedMeters, Projection::projectedMetersForLatLng(southWest));
+ EXPECT_DOUBLE_EQ(projectedMeters.northing, -20037508.342789274);
+ EXPECT_DOUBLE_EQ(projectedMeters.easting, -20037508.342789244);
+
+ latLng = LatLng { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
+ projectedMeters = Projection::projectedMetersForLatLng(latLng);
+ EXPECT_EQ(projectedMeters, Projection::projectedMetersForLatLng(northEast));
+ EXPECT_DOUBLE_EQ(projectedMeters.northing, -Projection::projectedMetersForLatLng(southWest).northing);
+ EXPECT_DOUBLE_EQ(projectedMeters.easting, -Projection::projectedMetersForLatLng(southWest).easting);
+
+ projectedMeters = ProjectedMeters { std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest() };
+ latLng = Projection::latLngForProjectedMeters(projectedMeters);
+ EXPECT_EQ(latLng.latitude, -util::LATITUDE_MAX);
+ EXPECT_EQ(latLng.longitude, -util::LONGITUDE_MAX);
+
+ projectedMeters = ProjectedMeters { std::numeric_limits<double>::max(), std::numeric_limits<double>::max() };
+ latLng = Projection::latLngForProjectedMeters(projectedMeters);
+ EXPECT_EQ(latLng.latitude, util::LATITUDE_MAX);
+ EXPECT_EQ(latLng.longitude, util::LONGITUDE_MAX);
+}
diff --git a/test/util/text_conversions.cpp b/test/util/text_conversions.cpp
index 29f02e988a..19e30d9f3d 100644
--- a/test/util/text_conversions.cpp
+++ b/test/util/text_conversions.cpp
@@ -14,8 +14,8 @@ TEST(TextConversions, to_upper) {
EXPECT_EQ(std::string("MASSE"), platform::uppercase("maße")); // DE
EXPECT_EQ(std::string("WEISSKOPFSEEADLER"), platform::uppercase("weißkopfseeadler")); // DE
+ EXPECT_EQ(std::string("BÊNÇÃO"), platform::uppercase("bênção")); // PT
EXPECT_EQ(std::string("AZƏRBAYCAN"), platform::uppercase("Azərbaycan")); // AZ
-
EXPECT_EQ(std::string("ὈΔΥΣΣΕΎΣ"), platform::uppercase("Ὀδυσσεύς")); // GR
}
@@ -29,7 +29,14 @@ TEST(TextConversions, to_lower) {
EXPECT_EQ(std::string("masse"), platform::lowercase("MASSE")); // DE
EXPECT_EQ(std::string("weisskopfseeadler"), platform::lowercase("weiSSkopfseeadler")); // DE
+ EXPECT_EQ(std::string("bênção"), platform::lowercase("BÊNÇÃO")); // PT
EXPECT_EQ(std::string("azərbaycan"), platform::lowercase("AZƏRBAYCAN")); // AZ
+
+#if defined(__QT__)
+ // https://bugreports.qt.io/browse/QTBUG-17337
+ EXPECT_NE(std::string("ὀδυσσεύς"), platform::lowercase("ὈΔΥΣΣΕΎΣ")); // GR
+#else
EXPECT_EQ(std::string("ὀδυσσεύς"), platform::lowercase("ὈΔΥΣΣΕΎΣ")); // GR
+#endif
}
diff --git a/test/util/thread_local.cpp b/test/util/thread_local.cpp
index 074af2c5d0..d2ec7f70a5 100644
--- a/test/util/thread_local.cpp
+++ b/test/util/thread_local.cpp
@@ -80,8 +80,8 @@ TEST(ThreadLocalStorage, AutoReclaim) {
unsigned counter = 0;
- DtorCounter* dtorCounter1 = new DtorCounter{ &counter };
- DtorCounter* dtorCounter2 = new DtorCounter{ &counter };
+ auto dtorCounter1 = new DtorCounter{ &counter };
+ auto dtorCounter2 = new DtorCounter{ &counter };
ThreadContext context = {"Test"};
@@ -91,5 +91,5 @@ TEST(ThreadLocalStorage, AutoReclaim) {
thread1.reset();
thread2.reset();
- EXPECT_EQ(counter, 2);
+ EXPECT_EQ(counter, 2u);
}