diff options
408 files changed, 25140 insertions, 5259 deletions
diff --git a/.tx/config b/.tx/config index 272fe45b16..6ac5784884 100644 --- a/.tx/config +++ b/.tx/config @@ -46,7 +46,7 @@ type = STRINGS [mapbox-gl-native.stringsxml-android] file_filter = platform/android/MapboxGLAndroidSDK/src/main/res/values-<lang>/strings.xml -lang_map = pt_PT: pt-rPT +lang_map = pt_PT: pt-rPT, zh_CN: zh-rCN source_file = platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml source_lang = en type = ANDROID diff --git a/CMakeLists.txt b/CMakeLists.txt index b6b86d9a8d..b007b541b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ if(WITH_COVERAGE) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage") endif(WITH_COVERAGE) -set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebugInfo Sanitize) +set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo Sanitize) # Compiler configuration set(CMAKE_CXX_EXTENSIONS OFF) @@ -92,7 +92,6 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os -DNDEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O3 -DNDEBUG") set(CMAKE_CXX_FLAGS_SANITIZE "${CMAKE_CXX_FLAGS_SANITIZE} -O1 -g -fno-omit-frame-pointer -fno-optimize-sibling-calls") - if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unknown-warning-option") elseif(CMAKE_COMPILER_IS_GNUCXX) @@ -100,6 +99,9 @@ elseif(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") endif() +set(CMAKE_SHARED_LINKER_FLAGS_SANITIZE "${CMAKE_SHARED_LINKER_FLAGS}") +set(CMAKE_EXE_LINKER_FLAGS_SANITIZE "${CMAKE_EXE_LINKER_FLAGS}") + # Technique from https://crascit.com/2016/04/09/using-ccache-with-cmake/ find_program(CCACHE_PROGRAM ccache) if(CCACHE_PROGRAM) @@ -146,16 +148,13 @@ if (COMMAND mbgl_filesource) include(cmake/filesource.cmake) endif() -include(cmake/core-files.cmake) include(cmake/core.cmake) if(COMMAND mbgl_platform_test) - include(cmake/test-files.cmake) include(cmake/test.cmake) endif() if(COMMAND mbgl_platform_benchmark) - include(cmake/benchmark-files.cmake) include(cmake/benchmark.cmake) endif() @@ -176,8 +175,10 @@ if(WITH_NODEJS AND COMMAND mbgl_platform_node) endif() if(CMAKE_GENERATOR STREQUAL "Xcode") - write_xcconfig_target_properties( - mbgl-core - mbgl-filesource + set_xcconfig_target_properties(mbgl-core) + set_xcconfig_target_properties(mbgl-filesource) + file(GENERATE + OUTPUT "${CMAKE_BINARY_DIR}/config.xcconfig" + INPUT "${CMAKE_SOURCE_DIR}/scripts/config.xcconfig.in" ) endif() @@ -506,6 +506,7 @@ test-node-recycle-map: node npm test npm run test-render -- --recycle-map --shuffle npm run test-query + npm run test-expressions #### Android targets ########################################################### diff --git a/benchmark/function/camera_function.benchmark.cpp b/benchmark/function/camera_function.benchmark.cpp index 51d2e22293..0ce7d7c763 100644 --- a/benchmark/function/camera_function.benchmark.cpp +++ b/benchmark/function/camera_function.benchmark.cpp @@ -1,9 +1,9 @@ #include <benchmark/benchmark.h> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/function.hpp> #include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion_impl.hpp> using namespace mbgl; using namespace mbgl::style; diff --git a/benchmark/function/composite_function.benchmark.cpp b/benchmark/function/composite_function.benchmark.cpp index f16254641f..8064d548fa 100644 --- a/benchmark/function/composite_function.benchmark.cpp +++ b/benchmark/function/composite_function.benchmark.cpp @@ -2,10 +2,10 @@ #include <mbgl/benchmark/stub_geometry_tile_feature.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/function.hpp> #include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion_impl.hpp> using namespace mbgl; using namespace mbgl::style; diff --git a/benchmark/function/source_function.benchmark.cpp b/benchmark/function/source_function.benchmark.cpp index 2164b2a418..b75cc143d2 100644 --- a/benchmark/function/source_function.benchmark.cpp +++ b/benchmark/function/source_function.benchmark.cpp @@ -2,9 +2,9 @@ #include <mbgl/benchmark/stub_geometry_tile_feature.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion_impl.hpp> using namespace mbgl; using namespace mbgl::style; diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp index 4e39237138..1ae2b0f2bc 100644 --- a/benchmark/parse/filter.benchmark.cpp +++ b/benchmark/parse/filter.benchmark.cpp @@ -1,9 +1,9 @@ #include <benchmark/benchmark.h> #include <mbgl/style/filter.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/filter.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/benchmark/stub_geometry_tile_feature.hpp> diff --git a/bin/offline.cpp b/bin/offline.cpp index 8b42ab7b72..2da47a4476 100644 --- a/bin/offline.cpp +++ b/bin/offline.cpp @@ -1,6 +1,7 @@ #include <mbgl/util/default_styles.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/util/string.hpp> +#include <mbgl/util/geojson.hpp> #include <mbgl/storage/default_file_source.hpp> @@ -11,9 +12,47 @@ #include <csignal> #include <atomic> #include <sstream> +#include <fstream> using namespace std::literals::chrono_literals; +std::string readFile(const std::string& fileName) { + std::ifstream stream(fileName.c_str()); + if (!stream.good()) { + throw std::runtime_error("Cannot read file: " + fileName); + } + + std::stringstream buffer; + buffer << stream.rdbuf(); + stream.close(); + + return buffer.str(); +} + +mapbox::geometry::geometry<double> parseGeometry(const std::string& json) { + using namespace mapbox::geojson; + auto geojson = parse(json); + return geojson.match( + [](const geometry& geom) { + return geom; + }, + [](const feature& feature) { + return feature.geometry; + }, + [](const feature_collection& featureCollection) { + if (featureCollection.size() < 1) { + throw std::runtime_error("No features in feature collection"); + } + geometry_collection geometries; + + for (auto feature : featureCollection) { + geometries.push_back(feature.geometry); + } + + return geometries; + }); +} + int main(int argc, char *argv[]) { args::ArgumentParser argumentParser("Mapbox GL offline tool"); args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"}); @@ -22,11 +61,19 @@ int main(int argc, char *argv[]) { args::ValueFlag<std::string> styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"}); args::ValueFlag<std::string> outputValue(argumentParser, "file", "Output database file name", {'o', "output"}); args::ValueFlag<std::string> apiBaseValue(argumentParser, "URL", "API Base URL", {'a', "apiBaseURL"}); - - args::ValueFlag<double> northValue(argumentParser, "degrees", "North latitude", {"north"}); - args::ValueFlag<double> westValue(argumentParser, "degrees", "West longitude", {"west"}); - args::ValueFlag<double> southValue(argumentParser, "degrees", "South latitude", {"south"}); - args::ValueFlag<double> eastValue(argumentParser, "degrees", "East longitude", {"east"}); + + // LatLngBounds + args::Group latLngBoundsGroup(argumentParser, "LatLng bounds:", args::Group::Validators::AllOrNone); + args::ValueFlag<double> northValue(latLngBoundsGroup, "degrees", "North latitude", {"north"}); + args::ValueFlag<double> westValue(latLngBoundsGroup, "degrees", "West longitude", {"west"}); + args::ValueFlag<double> southValue(latLngBoundsGroup, "degrees", "South latitude", {"south"}); + args::ValueFlag<double> eastValue(latLngBoundsGroup, "degrees", "East longitude", {"east"}); + + // Geometry + args::Group geoJSONGroup(argumentParser, "GeoJson geometry:", args::Group::Validators::AllOrNone); + args::ValueFlag<std::string> geometryValue(geoJSONGroup, "file", "GeoJSON file containing the region geometry", {"geojson"}); + + args::ValueFlag<double> minZoomValue(argumentParser, "number", "Min zoom level", {"minZoom"}); args::ValueFlag<double> maxZoomValue(argumentParser, "number", "Max zoom level", {"maxZoom"}); args::ValueFlag<double> pixelRatioValue(argumentParser, "number", "Pixel ratio", {"pixelRatio"}); @@ -48,23 +95,39 @@ int main(int argc, char *argv[]) { std::string style = styleValue ? args::get(styleValue) : mbgl::util::default_styles::streets.url; - // Bay area - const double north = northValue ? args::get(northValue) : 37.2; - const double west = westValue ? args::get(westValue) : -122.8; - const double south = southValue ? args::get(southValue) : 38.1; - const double east = eastValue ? args::get(eastValue) : -121.7; - const double minZoom = minZoomValue ? args::get(minZoomValue) : 0.0; const double maxZoom = maxZoomValue ? args::get(maxZoomValue) : 15.0; const double pixelRatio = pixelRatioValue ? args::get(pixelRatioValue) : 1.0; const std::string output = outputValue ? args::get(outputValue) : "offline.db"; + + using namespace mbgl; + + OfflineRegionDefinition definition = [&]() { + if (geometryValue) { + try { + std::string json = readFile(geometryValue.Get()); + auto geometry = parseGeometry(json); + return OfflineRegionDefinition{ OfflineGeometryRegionDefinition(style, geometry, minZoom, maxZoom, pixelRatio) }; + } catch(std::runtime_error e) { + std::cerr << "Could not parse geojson file " << geometryValue.Get() << ": " << e.what() << std::endl; + exit(1); + } + } else { + // Bay area + const double north = northValue ? args::get(northValue) : 37.2; + const double west = westValue ? args::get(westValue) : -122.8; + const double south = southValue ? args::get(southValue) : 38.1; + const double east = eastValue ? args::get(eastValue) : -121.7; + LatLngBounds boundingBox = LatLngBounds::hull(LatLng(north, west), LatLng(south, east)); + return OfflineRegionDefinition{ OfflineTilePyramidRegionDefinition(style, boundingBox, minZoom, maxZoom, pixelRatio) }; + } + }(); const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN"); const std::string token = tokenValue ? args::get(tokenValue) : (tokenEnv ? tokenEnv : std::string()); const std::string apiBaseURL = apiBaseValue ? args::get(apiBaseValue) : mbgl::util::API_BASE_URL; - using namespace mbgl; util::RunLoop loop; DefaultFileSource fileSource(output, "."); @@ -73,8 +136,7 @@ int main(int argc, char *argv[]) { fileSource.setAccessToken(token); fileSource.setAPIBaseURL(apiBaseURL); - LatLngBounds boundingBox = LatLngBounds::hull(LatLng(north, west), LatLng(south, east)); - OfflineTilePyramidRegionDefinition definition(style, boundingBox, minZoom, maxZoom, pixelRatio); + OfflineRegionMetadata metadata; class Observer : public OfflineRegionObserver { @@ -136,9 +198,9 @@ int main(int argc, char *argv[]) { std::signal(SIGINT, [] (int) { stop(); }); - fileSource.createOfflineRegion(definition, metadata, [&] (std::exception_ptr error, optional<OfflineRegion> region_) { - if (error) { - std::cerr << "Error creating region: " << util::toString(error) << std::endl; + fileSource.createOfflineRegion(definition, metadata, [&] (mbgl::expected<OfflineRegion, std::exception_ptr> region_) { + if (!region_) { + std::cerr << "Error creating region: " << util::toString(region_.error()) << std::endl; loop.stop(); exit(1); } else { diff --git a/circle.yml b/circle.yml index 6c4724600e..bf903a2f36 100644 --- a/circle.yml +++ b/circle.yml @@ -110,14 +110,14 @@ step-library: - &save-mason_packages-cache save_cache: name: Save mason_packages cache - key: 'mason_packages/v1/{{ arch }}/{{ checksum "cmake/mason-dependencies.cmake" }}' + key: 'mason_packages/v3/{{ arch }}/{{ checksum "cmake/mason-dependencies.cmake" }}' paths: [ "mason_packages/.binaries" ] - &restore-mason_packages-cache restore_cache: name: Restore mason_packages cache keys: - - 'mason_packages/v1/{{ arch }}/{{ checksum "cmake/mason-dependencies.cmake" }}' - - 'mason_packages/v1/{{ arch }}' + - 'mason_packages/v3/{{ arch }}/{{ checksum "cmake/mason-dependencies.cmake" }}' + - 'mason_packages/v3/{{ arch }}' - &save-ccache save_cache: name: Save ccache @@ -426,6 +426,12 @@ jobs: --test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/debug/MapboxGLAndroidSDKTestApp-debug-androidTest.apk \ --device-ids sailfish --os-version-ids 26 --locales en --orientations portrait --timeout 20m fi + - run: + name: gzip debugable .so files + command: | + gzip platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/debug/obj/armeabi-v7a/libmapbox-gl.so + - store_artifacts: + path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/debug/obj/armeabi-v7a/libmapbox-gl.so.gz - store_artifacts: path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug destination: . @@ -491,9 +497,24 @@ jobs: - *save-mason_packages-cache - *save-ccache - *save-gradle-cache + - run: + name: gzip debugable .so files + command: | + gzip platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/arm64-v8a/libmapbox-gl.so && \ + gzip platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/armeabi-v7a/libmapbox-gl.so && \ + gzip platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/x86/libmapbox-gl.so && \ + gzip platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/x86_64/libmapbox-gl.so - store_artifacts: path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/release destination: . + - store_artifacts: + path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/arm64-v8a/libmapbox-gl.so.gz + - store_artifacts: + path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/armeabi-v7a/libmapbox-gl.so.gz + - store_artifacts: + path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/x86/libmapbox-gl.so.gz + - store_artifacts: + path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/x86_64/libmapbox-gl.so.gz - run: name: Record size command: platform/android/scripts/metrics.sh diff --git a/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake deleted file mode 100644 index 21547eb9c4..0000000000 --- a/cmake/benchmark-files.cmake +++ /dev/null @@ -1,28 +0,0 @@ -# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js - -set(MBGL_BENCHMARK_FILES - # api - benchmark/api/query.benchmark.cpp - benchmark/api/render.benchmark.cpp - - # benchmark - benchmark/include/mbgl/benchmark.hpp - benchmark/src/main.cpp - benchmark/src/mbgl/benchmark/benchmark.cpp - benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp - - # function - benchmark/function/camera_function.benchmark.cpp - benchmark/function/composite_function.benchmark.cpp - benchmark/function/source_function.benchmark.cpp - - # parse - benchmark/parse/filter.benchmark.cpp - benchmark/parse/tile_mask.benchmark.cpp - benchmark/parse/vector_tile.benchmark.cpp - - # util - benchmark/util/dtoa.benchmark.cpp - benchmark/util/tilecover.benchmark.cpp - -) diff --git a/cmake/benchmark-files.txt b/cmake/benchmark-files.txt new file mode 100644 index 0000000000..16224e9cad --- /dev/null +++ b/cmake/benchmark-files.txt @@ -0,0 +1,26 @@ +# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js + +# api +benchmark/api/query.benchmark.cpp +benchmark/api/render.benchmark.cpp + +# benchmark +benchmark/include/mbgl/benchmark.hpp +benchmark/src/main.cpp +benchmark/src/mbgl/benchmark/benchmark.cpp +benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp + +# function +benchmark/function/camera_function.benchmark.cpp +benchmark/function/composite_function.benchmark.cpp +benchmark/function/source_function.benchmark.cpp + +# parse +benchmark/parse/filter.benchmark.cpp +benchmark/parse/tile_mask.benchmark.cpp +benchmark/parse/vector_tile.benchmark.cpp + +# util +benchmark/util/dtoa.benchmark.cpp +benchmark/util/tilecover.benchmark.cpp + diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake index 98fa4dac63..5cac8cc8b6 100644 --- a/cmake/benchmark.cmake +++ b/cmake/benchmark.cmake @@ -1,6 +1,5 @@ -add_executable(mbgl-benchmark - ${MBGL_BENCHMARK_FILES} -) +load_sources_list(MBGL_BENCHMARK_FILES cmake/benchmark-files.txt) +add_executable(mbgl-benchmark ${MBGL_BENCHMARK_FILES}) target_include_directories(mbgl-benchmark PRIVATE src diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake deleted file mode 100644 index addacc174d..0000000000 --- a/cmake/core-files.cmake +++ /dev/null @@ -1,765 +0,0 @@ -# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js - -set(MBGL_CORE_FILES - # actor - include/mbgl/actor/actor.hpp - include/mbgl/actor/actor_ref.hpp - include/mbgl/actor/aspiring_actor.hpp - include/mbgl/actor/established_actor.hpp - include/mbgl/actor/mailbox.hpp - include/mbgl/actor/message.hpp - include/mbgl/actor/scheduler.hpp - src/mbgl/actor/mailbox.cpp - src/mbgl/actor/scheduler.cpp - - # algorithm - src/mbgl/algorithm/covered_by_children.hpp - src/mbgl/algorithm/generate_clip_ids.cpp - src/mbgl/algorithm/generate_clip_ids.hpp - src/mbgl/algorithm/generate_clip_ids_impl.hpp - src/mbgl/algorithm/update_renderables.hpp - src/mbgl/algorithm/update_tile_masks.hpp - - # annotation - include/mbgl/annotation/annotation.hpp - src/mbgl/annotation/annotation_manager.cpp - src/mbgl/annotation/annotation_manager.hpp - src/mbgl/annotation/annotation_source.cpp - src/mbgl/annotation/annotation_source.hpp - src/mbgl/annotation/annotation_tile.cpp - src/mbgl/annotation/annotation_tile.hpp - src/mbgl/annotation/fill_annotation_impl.cpp - src/mbgl/annotation/fill_annotation_impl.hpp - src/mbgl/annotation/line_annotation_impl.cpp - src/mbgl/annotation/line_annotation_impl.hpp - src/mbgl/annotation/render_annotation_source.cpp - src/mbgl/annotation/render_annotation_source.hpp - src/mbgl/annotation/shape_annotation_impl.cpp - src/mbgl/annotation/shape_annotation_impl.hpp - src/mbgl/annotation/symbol_annotation_impl.cpp - src/mbgl/annotation/symbol_annotation_impl.hpp - - # csscolorparser - src/csscolorparser/csscolorparser.cpp - src/csscolorparser/csscolorparser.hpp - - # geometry - src/mbgl/geometry/anchor.hpp - src/mbgl/geometry/debug_font_data.hpp - src/mbgl/geometry/dem_data.cpp - src/mbgl/geometry/dem_data.hpp - src/mbgl/geometry/feature_index.cpp - src/mbgl/geometry/feature_index.hpp - src/mbgl/geometry/line_atlas.cpp - src/mbgl/geometry/line_atlas.hpp - - # gl - src/mbgl/gl/attribute.cpp - src/mbgl/gl/attribute.hpp - src/mbgl/gl/color_mode.cpp - src/mbgl/gl/color_mode.hpp - src/mbgl/gl/context.cpp - src/mbgl/gl/context.hpp - src/mbgl/gl/debugging.cpp - src/mbgl/gl/debugging.hpp - src/mbgl/gl/debugging_extension.cpp - src/mbgl/gl/debugging_extension.hpp - src/mbgl/gl/depth_mode.cpp - src/mbgl/gl/depth_mode.hpp - src/mbgl/gl/draw_mode.hpp - src/mbgl/gl/extension.hpp - src/mbgl/gl/features.hpp - src/mbgl/gl/framebuffer.hpp - src/mbgl/gl/gl.cpp - src/mbgl/gl/gl.hpp - src/mbgl/gl/index_buffer.hpp - src/mbgl/gl/object.cpp - src/mbgl/gl/object.hpp - src/mbgl/gl/primitives.hpp - src/mbgl/gl/program.hpp - src/mbgl/gl/program_binary_extension.hpp - src/mbgl/gl/renderbuffer.hpp - src/mbgl/gl/state.hpp - src/mbgl/gl/stencil_mode.cpp - src/mbgl/gl/stencil_mode.hpp - src/mbgl/gl/texture.hpp - src/mbgl/gl/types.hpp - src/mbgl/gl/uniform.cpp - src/mbgl/gl/uniform.hpp - src/mbgl/gl/value.cpp - src/mbgl/gl/value.hpp - src/mbgl/gl/vertex_array.cpp - src/mbgl/gl/vertex_array.hpp - src/mbgl/gl/vertex_array_extension.hpp - src/mbgl/gl/vertex_buffer.hpp - - # layout - src/mbgl/layout/clip_lines.cpp - src/mbgl/layout/clip_lines.hpp - src/mbgl/layout/merge_lines.cpp - src/mbgl/layout/merge_lines.hpp - src/mbgl/layout/symbol_feature.hpp - src/mbgl/layout/symbol_instance.cpp - src/mbgl/layout/symbol_instance.hpp - src/mbgl/layout/symbol_layout.cpp - src/mbgl/layout/symbol_layout.hpp - src/mbgl/layout/symbol_projection.cpp - src/mbgl/layout/symbol_projection.hpp - - # map - include/mbgl/map/camera.hpp - include/mbgl/map/change.hpp - include/mbgl/map/map.hpp - include/mbgl/map/map_observer.hpp - include/mbgl/map/mode.hpp - src/mbgl/map/map.cpp - src/mbgl/map/transform.cpp - src/mbgl/map/transform.hpp - src/mbgl/map/transform_state.cpp - src/mbgl/map/transform_state.hpp - src/mbgl/map/zoom_history.hpp - - # math - include/mbgl/math/clamp.hpp - include/mbgl/math/log2.hpp - include/mbgl/math/minmax.hpp - include/mbgl/math/wrap.hpp - src/mbgl/math/log2.cpp - - # parsedate - src/parsedate/parsedate.cpp - src/parsedate/parsedate.hpp - - # programs - src/mbgl/programs/attributes.hpp - src/mbgl/programs/background_program.cpp - src/mbgl/programs/background_program.hpp - src/mbgl/programs/binary_program.cpp - src/mbgl/programs/binary_program.hpp - src/mbgl/programs/circle_program.cpp - src/mbgl/programs/circle_program.hpp - src/mbgl/programs/clipping_mask_program.hpp - src/mbgl/programs/collision_box_program.cpp - src/mbgl/programs/collision_box_program.hpp - src/mbgl/programs/debug_program.hpp - src/mbgl/programs/extrusion_texture_program.cpp - src/mbgl/programs/extrusion_texture_program.hpp - src/mbgl/programs/fill_extrusion_program.cpp - src/mbgl/programs/fill_extrusion_program.hpp - src/mbgl/programs/fill_program.cpp - src/mbgl/programs/fill_program.hpp - src/mbgl/programs/heatmap_program.cpp - src/mbgl/programs/heatmap_program.hpp - src/mbgl/programs/heatmap_texture_program.cpp - src/mbgl/programs/heatmap_texture_program.hpp - src/mbgl/programs/hillshade_prepare_program.cpp - src/mbgl/programs/hillshade_prepare_program.hpp - src/mbgl/programs/hillshade_program.cpp - src/mbgl/programs/hillshade_program.hpp - src/mbgl/programs/line_program.cpp - src/mbgl/programs/line_program.hpp - src/mbgl/programs/program.hpp - src/mbgl/programs/program_parameters.cpp - src/mbgl/programs/program_parameters.hpp - src/mbgl/programs/programs.hpp - src/mbgl/programs/raster_program.cpp - src/mbgl/programs/raster_program.hpp - src/mbgl/programs/segment.hpp - src/mbgl/programs/symbol_program.cpp - src/mbgl/programs/symbol_program.hpp - src/mbgl/programs/uniforms.hpp - - # renderer - include/mbgl/renderer/backend_scope.hpp - include/mbgl/renderer/mode.hpp - include/mbgl/renderer/query.hpp - include/mbgl/renderer/renderer.hpp - include/mbgl/renderer/renderer_backend.hpp - include/mbgl/renderer/renderer_frontend.hpp - include/mbgl/renderer/renderer_observer.hpp - src/mbgl/renderer/backend_scope.cpp - src/mbgl/renderer/bucket.hpp - src/mbgl/renderer/bucket_parameters.cpp - src/mbgl/renderer/bucket_parameters.hpp - src/mbgl/renderer/cross_faded_property_evaluator.cpp - src/mbgl/renderer/cross_faded_property_evaluator.hpp - src/mbgl/renderer/data_driven_property_evaluator.hpp - src/mbgl/renderer/group_by_layout.cpp - src/mbgl/renderer/group_by_layout.hpp - src/mbgl/renderer/image_atlas.cpp - src/mbgl/renderer/image_atlas.hpp - src/mbgl/renderer/image_manager.cpp - src/mbgl/renderer/image_manager.hpp - src/mbgl/renderer/paint_parameters.cpp - src/mbgl/renderer/paint_parameters.hpp - src/mbgl/renderer/paint_property_binder.hpp - src/mbgl/renderer/paint_property_statistics.hpp - src/mbgl/renderer/possibly_evaluated_property_value.hpp - src/mbgl/renderer/property_evaluation_parameters.hpp - src/mbgl/renderer/property_evaluator.hpp - src/mbgl/renderer/render_layer.cpp - src/mbgl/renderer/render_layer.hpp - src/mbgl/renderer/render_light.cpp - src/mbgl/renderer/render_light.hpp - src/mbgl/renderer/render_pass.hpp - src/mbgl/renderer/render_source.cpp - src/mbgl/renderer/render_source.hpp - src/mbgl/renderer/render_source_observer.hpp - src/mbgl/renderer/render_static_data.cpp - src/mbgl/renderer/render_static_data.hpp - src/mbgl/renderer/render_tile.cpp - src/mbgl/renderer/render_tile.hpp - src/mbgl/renderer/renderer.cpp - src/mbgl/renderer/renderer_backend.cpp - src/mbgl/renderer/renderer_impl.cpp - src/mbgl/renderer/renderer_impl.hpp - src/mbgl/renderer/style_diff.cpp - src/mbgl/renderer/style_diff.hpp - src/mbgl/renderer/tile_mask.hpp - src/mbgl/renderer/tile_parameters.hpp - src/mbgl/renderer/tile_pyramid.cpp - src/mbgl/renderer/tile_pyramid.hpp - src/mbgl/renderer/transition_parameters.hpp - src/mbgl/renderer/update_parameters.hpp - - # renderer/buckets - src/mbgl/renderer/buckets/circle_bucket.cpp - src/mbgl/renderer/buckets/circle_bucket.hpp - src/mbgl/renderer/buckets/debug_bucket.cpp - src/mbgl/renderer/buckets/debug_bucket.hpp - src/mbgl/renderer/buckets/fill_bucket.cpp - src/mbgl/renderer/buckets/fill_bucket.hpp - src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp - src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp - src/mbgl/renderer/buckets/heatmap_bucket.cpp - src/mbgl/renderer/buckets/heatmap_bucket.hpp - src/mbgl/renderer/buckets/hillshade_bucket.cpp - src/mbgl/renderer/buckets/hillshade_bucket.hpp - src/mbgl/renderer/buckets/line_bucket.cpp - src/mbgl/renderer/buckets/line_bucket.hpp - src/mbgl/renderer/buckets/raster_bucket.cpp - src/mbgl/renderer/buckets/raster_bucket.hpp - src/mbgl/renderer/buckets/symbol_bucket.cpp - src/mbgl/renderer/buckets/symbol_bucket.hpp - - # renderer/layers - src/mbgl/renderer/layers/render_background_layer.cpp - src/mbgl/renderer/layers/render_background_layer.hpp - src/mbgl/renderer/layers/render_circle_layer.cpp - src/mbgl/renderer/layers/render_circle_layer.hpp - src/mbgl/renderer/layers/render_custom_layer.cpp - src/mbgl/renderer/layers/render_custom_layer.hpp - src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp - src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp - src/mbgl/renderer/layers/render_fill_layer.cpp - src/mbgl/renderer/layers/render_fill_layer.hpp - src/mbgl/renderer/layers/render_heatmap_layer.cpp - src/mbgl/renderer/layers/render_heatmap_layer.hpp - src/mbgl/renderer/layers/render_hillshade_layer.cpp - src/mbgl/renderer/layers/render_hillshade_layer.hpp - src/mbgl/renderer/layers/render_line_layer.cpp - src/mbgl/renderer/layers/render_line_layer.hpp - src/mbgl/renderer/layers/render_raster_layer.cpp - src/mbgl/renderer/layers/render_raster_layer.hpp - src/mbgl/renderer/layers/render_symbol_layer.cpp - src/mbgl/renderer/layers/render_symbol_layer.hpp - - # renderer/sources - src/mbgl/renderer/sources/render_custom_geometry_source.cpp - src/mbgl/renderer/sources/render_custom_geometry_source.hpp - src/mbgl/renderer/sources/render_geojson_source.cpp - src/mbgl/renderer/sources/render_geojson_source.hpp - src/mbgl/renderer/sources/render_image_source.cpp - src/mbgl/renderer/sources/render_image_source.hpp - src/mbgl/renderer/sources/render_raster_dem_source.cpp - src/mbgl/renderer/sources/render_raster_dem_source.hpp - src/mbgl/renderer/sources/render_raster_source.cpp - src/mbgl/renderer/sources/render_raster_source.hpp - src/mbgl/renderer/sources/render_vector_source.cpp - src/mbgl/renderer/sources/render_vector_source.hpp - - # shaders - src/mbgl/shaders/background.cpp - src/mbgl/shaders/background.hpp - src/mbgl/shaders/background_pattern.cpp - src/mbgl/shaders/background_pattern.hpp - src/mbgl/shaders/circle.cpp - src/mbgl/shaders/circle.hpp - src/mbgl/shaders/clipping_mask.cpp - src/mbgl/shaders/clipping_mask.hpp - src/mbgl/shaders/collision_box.cpp - src/mbgl/shaders/collision_box.hpp - src/mbgl/shaders/collision_circle.cpp - src/mbgl/shaders/collision_circle.hpp - src/mbgl/shaders/debug.cpp - src/mbgl/shaders/debug.hpp - src/mbgl/shaders/extrusion_texture.cpp - src/mbgl/shaders/extrusion_texture.hpp - src/mbgl/shaders/fill.cpp - src/mbgl/shaders/fill.hpp - src/mbgl/shaders/fill_extrusion.cpp - src/mbgl/shaders/fill_extrusion.hpp - src/mbgl/shaders/fill_extrusion_pattern.cpp - src/mbgl/shaders/fill_extrusion_pattern.hpp - src/mbgl/shaders/fill_outline.cpp - src/mbgl/shaders/fill_outline.hpp - src/mbgl/shaders/fill_outline_pattern.cpp - src/mbgl/shaders/fill_outline_pattern.hpp - src/mbgl/shaders/fill_pattern.cpp - src/mbgl/shaders/fill_pattern.hpp - src/mbgl/shaders/heatmap.cpp - src/mbgl/shaders/heatmap.hpp - src/mbgl/shaders/heatmap_texture.cpp - src/mbgl/shaders/heatmap_texture.hpp - src/mbgl/shaders/hillshade.cpp - src/mbgl/shaders/hillshade.hpp - src/mbgl/shaders/hillshade_prepare.cpp - src/mbgl/shaders/hillshade_prepare.hpp - src/mbgl/shaders/line.cpp - src/mbgl/shaders/line.hpp - src/mbgl/shaders/line_pattern.cpp - src/mbgl/shaders/line_pattern.hpp - src/mbgl/shaders/line_sdf.cpp - src/mbgl/shaders/line_sdf.hpp - src/mbgl/shaders/preludes.cpp - src/mbgl/shaders/preludes.hpp - src/mbgl/shaders/raster.cpp - src/mbgl/shaders/raster.hpp - src/mbgl/shaders/shaders.cpp - src/mbgl/shaders/shaders.hpp - src/mbgl/shaders/source.cpp - src/mbgl/shaders/source.hpp - src/mbgl/shaders/symbol_icon.cpp - src/mbgl/shaders/symbol_icon.hpp - src/mbgl/shaders/symbol_sdf.cpp - src/mbgl/shaders/symbol_sdf.hpp - - # sprite - src/mbgl/sprite/sprite_loader.cpp - src/mbgl/sprite/sprite_loader.hpp - src/mbgl/sprite/sprite_loader_observer.hpp - src/mbgl/sprite/sprite_loader_worker.cpp - src/mbgl/sprite/sprite_loader_worker.hpp - src/mbgl/sprite/sprite_parser.cpp - src/mbgl/sprite/sprite_parser.hpp - - # storage - include/mbgl/storage/default_file_source.hpp - include/mbgl/storage/file_source.hpp - include/mbgl/storage/network_status.hpp - include/mbgl/storage/offline.hpp - include/mbgl/storage/online_file_source.hpp - include/mbgl/storage/resource.hpp - include/mbgl/storage/resource_transform.hpp - include/mbgl/storage/response.hpp - src/mbgl/storage/asset_file_source.hpp - src/mbgl/storage/http_file_source.hpp - src/mbgl/storage/local_file_source.hpp - src/mbgl/storage/network_status.cpp - src/mbgl/storage/resource.cpp - src/mbgl/storage/resource_transform.cpp - src/mbgl/storage/response.cpp - - # style - include/mbgl/style/color_ramp_property_value.hpp - include/mbgl/style/conversion.hpp - include/mbgl/style/filter.hpp - include/mbgl/style/image.hpp - include/mbgl/style/layer.hpp - include/mbgl/style/layer_type.hpp - include/mbgl/style/light.hpp - include/mbgl/style/position.hpp - include/mbgl/style/property_expression.hpp - include/mbgl/style/property_value.hpp - include/mbgl/style/source.hpp - include/mbgl/style/style.hpp - include/mbgl/style/transition_options.hpp - include/mbgl/style/types.hpp - include/mbgl/style/undefined.hpp - src/mbgl/style/collection.hpp - src/mbgl/style/custom_tile_loader.cpp - src/mbgl/style/custom_tile_loader.hpp - src/mbgl/style/filter.cpp - src/mbgl/style/image.cpp - src/mbgl/style/image_impl.cpp - src/mbgl/style/image_impl.hpp - src/mbgl/style/layer.cpp - src/mbgl/style/layer_impl.cpp - src/mbgl/style/layer_impl.hpp - src/mbgl/style/layer_observer.hpp - src/mbgl/style/layout_property.hpp - src/mbgl/style/light.cpp - src/mbgl/style/light_impl.cpp - src/mbgl/style/light_impl.hpp - src/mbgl/style/light_observer.hpp - src/mbgl/style/observer.hpp - src/mbgl/style/paint_property.hpp - src/mbgl/style/parser.cpp - src/mbgl/style/parser.hpp - src/mbgl/style/properties.hpp - src/mbgl/style/rapidjson_conversion.hpp - src/mbgl/style/source.cpp - src/mbgl/style/source_impl.cpp - src/mbgl/style/source_impl.hpp - src/mbgl/style/source_observer.hpp - src/mbgl/style/style.cpp - src/mbgl/style/style_impl.cpp - src/mbgl/style/style_impl.hpp - src/mbgl/style/types.cpp - - # style/conversion - include/mbgl/style/conversion/color_ramp_property_value.hpp - include/mbgl/style/conversion/constant.hpp - include/mbgl/style/conversion/coordinate.hpp - include/mbgl/style/conversion/custom_geometry_source_options.hpp - include/mbgl/style/conversion/filter.hpp - include/mbgl/style/conversion/function.hpp - include/mbgl/style/conversion/geojson.hpp - include/mbgl/style/conversion/geojson_options.hpp - include/mbgl/style/conversion/get_json_type.hpp - include/mbgl/style/conversion/layer.hpp - include/mbgl/style/conversion/light.hpp - include/mbgl/style/conversion/position.hpp - include/mbgl/style/conversion/property_value.hpp - include/mbgl/style/conversion/source.hpp - include/mbgl/style/conversion/tileset.hpp - include/mbgl/style/conversion/transition_options.hpp - src/mbgl/style/conversion/color_ramp_property_value.cpp - src/mbgl/style/conversion/constant.cpp - src/mbgl/style/conversion/coordinate.cpp - src/mbgl/style/conversion/custom_geometry_source_options.cpp - src/mbgl/style/conversion/filter.cpp - src/mbgl/style/conversion/function.cpp - src/mbgl/style/conversion/geojson.cpp - src/mbgl/style/conversion/geojson_options.cpp - src/mbgl/style/conversion/get_json_type.cpp - src/mbgl/style/conversion/json.hpp - src/mbgl/style/conversion/layer.cpp - src/mbgl/style/conversion/light.cpp - src/mbgl/style/conversion/make_property_setters.hpp - src/mbgl/style/conversion/position.cpp - src/mbgl/style/conversion/property_setter.hpp - src/mbgl/style/conversion/source.cpp - src/mbgl/style/conversion/stringify.hpp - src/mbgl/style/conversion/tileset.cpp - src/mbgl/style/conversion/transition_options.cpp - - # style/expression - include/mbgl/style/expression/array_assertion.hpp - include/mbgl/style/expression/assertion.hpp - include/mbgl/style/expression/at.hpp - include/mbgl/style/expression/boolean_operator.hpp - include/mbgl/style/expression/case.hpp - include/mbgl/style/expression/check_subtype.hpp - include/mbgl/style/expression/coalesce.hpp - include/mbgl/style/expression/coercion.hpp - include/mbgl/style/expression/collator.hpp - include/mbgl/style/expression/collator_expression.hpp - include/mbgl/style/expression/comparison.hpp - include/mbgl/style/expression/compound_expression.hpp - include/mbgl/style/expression/dsl.hpp - include/mbgl/style/expression/error.hpp - include/mbgl/style/expression/expression.hpp - include/mbgl/style/expression/find_zoom_curve.hpp - include/mbgl/style/expression/get_covering_stops.hpp - include/mbgl/style/expression/interpolate.hpp - include/mbgl/style/expression/interpolator.hpp - include/mbgl/style/expression/is_constant.hpp - include/mbgl/style/expression/is_expression.hpp - include/mbgl/style/expression/length.hpp - include/mbgl/style/expression/let.hpp - include/mbgl/style/expression/literal.hpp - include/mbgl/style/expression/match.hpp - include/mbgl/style/expression/parsing_context.hpp - include/mbgl/style/expression/step.hpp - include/mbgl/style/expression/type.hpp - include/mbgl/style/expression/value.hpp - src/mbgl/style/expression/array_assertion.cpp - src/mbgl/style/expression/assertion.cpp - src/mbgl/style/expression/at.cpp - src/mbgl/style/expression/boolean_operator.cpp - src/mbgl/style/expression/case.cpp - src/mbgl/style/expression/check_subtype.cpp - src/mbgl/style/expression/coalesce.cpp - src/mbgl/style/expression/coercion.cpp - src/mbgl/style/expression/collator_expression.cpp - src/mbgl/style/expression/comparison.cpp - src/mbgl/style/expression/compound_expression.cpp - src/mbgl/style/expression/dsl.cpp - src/mbgl/style/expression/expression.cpp - src/mbgl/style/expression/find_zoom_curve.cpp - src/mbgl/style/expression/get_covering_stops.cpp - src/mbgl/style/expression/interpolate.cpp - src/mbgl/style/expression/is_constant.cpp - src/mbgl/style/expression/is_expression.cpp - src/mbgl/style/expression/length.cpp - src/mbgl/style/expression/let.cpp - src/mbgl/style/expression/literal.cpp - src/mbgl/style/expression/match.cpp - src/mbgl/style/expression/parsing_context.cpp - src/mbgl/style/expression/step.cpp - src/mbgl/style/expression/util.cpp - src/mbgl/style/expression/util.hpp - src/mbgl/style/expression/value.cpp - - # style/layers - include/mbgl/style/layers/background_layer.hpp - include/mbgl/style/layers/circle_layer.hpp - include/mbgl/style/layers/custom_layer.hpp - include/mbgl/style/layers/fill_extrusion_layer.hpp - include/mbgl/style/layers/fill_layer.hpp - include/mbgl/style/layers/heatmap_layer.hpp - include/mbgl/style/layers/hillshade_layer.hpp - include/mbgl/style/layers/line_layer.hpp - include/mbgl/style/layers/raster_layer.hpp - include/mbgl/style/layers/symbol_layer.hpp - src/mbgl/style/layers/background_layer.cpp - src/mbgl/style/layers/background_layer_impl.cpp - src/mbgl/style/layers/background_layer_impl.hpp - src/mbgl/style/layers/background_layer_properties.cpp - src/mbgl/style/layers/background_layer_properties.hpp - src/mbgl/style/layers/circle_layer.cpp - src/mbgl/style/layers/circle_layer_impl.cpp - src/mbgl/style/layers/circle_layer_impl.hpp - src/mbgl/style/layers/circle_layer_properties.cpp - src/mbgl/style/layers/circle_layer_properties.hpp - src/mbgl/style/layers/custom_layer.cpp - src/mbgl/style/layers/custom_layer_impl.cpp - src/mbgl/style/layers/custom_layer_impl.hpp - src/mbgl/style/layers/fill_extrusion_layer.cpp - src/mbgl/style/layers/fill_extrusion_layer_impl.cpp - src/mbgl/style/layers/fill_extrusion_layer_impl.hpp - src/mbgl/style/layers/fill_extrusion_layer_properties.cpp - src/mbgl/style/layers/fill_extrusion_layer_properties.hpp - src/mbgl/style/layers/fill_layer.cpp - src/mbgl/style/layers/fill_layer_impl.cpp - src/mbgl/style/layers/fill_layer_impl.hpp - src/mbgl/style/layers/fill_layer_properties.cpp - src/mbgl/style/layers/fill_layer_properties.hpp - src/mbgl/style/layers/heatmap_layer.cpp - src/mbgl/style/layers/heatmap_layer_impl.cpp - src/mbgl/style/layers/heatmap_layer_impl.hpp - src/mbgl/style/layers/heatmap_layer_properties.cpp - src/mbgl/style/layers/heatmap_layer_properties.hpp - src/mbgl/style/layers/hillshade_layer.cpp - src/mbgl/style/layers/hillshade_layer_impl.cpp - src/mbgl/style/layers/hillshade_layer_impl.hpp - src/mbgl/style/layers/hillshade_layer_properties.cpp - src/mbgl/style/layers/hillshade_layer_properties.hpp - src/mbgl/style/layers/line_layer.cpp - src/mbgl/style/layers/line_layer_impl.cpp - src/mbgl/style/layers/line_layer_impl.hpp - src/mbgl/style/layers/line_layer_properties.cpp - src/mbgl/style/layers/line_layer_properties.hpp - src/mbgl/style/layers/raster_layer.cpp - src/mbgl/style/layers/raster_layer_impl.cpp - src/mbgl/style/layers/raster_layer_impl.hpp - src/mbgl/style/layers/raster_layer_properties.cpp - src/mbgl/style/layers/raster_layer_properties.hpp - src/mbgl/style/layers/symbol_layer.cpp - src/mbgl/style/layers/symbol_layer_impl.cpp - src/mbgl/style/layers/symbol_layer_impl.hpp - src/mbgl/style/layers/symbol_layer_properties.cpp - src/mbgl/style/layers/symbol_layer_properties.hpp - - # style/sources - include/mbgl/style/sources/custom_geometry_source.hpp - include/mbgl/style/sources/geojson_source.hpp - include/mbgl/style/sources/image_source.hpp - include/mbgl/style/sources/raster_dem_source.hpp - include/mbgl/style/sources/raster_source.hpp - include/mbgl/style/sources/vector_source.hpp - src/mbgl/style/sources/custom_geometry_source.cpp - src/mbgl/style/sources/custom_geometry_source_impl.cpp - src/mbgl/style/sources/custom_geometry_source_impl.hpp - src/mbgl/style/sources/geojson_source.cpp - src/mbgl/style/sources/geojson_source_impl.cpp - src/mbgl/style/sources/geojson_source_impl.hpp - src/mbgl/style/sources/image_source.cpp - src/mbgl/style/sources/image_source_impl.cpp - src/mbgl/style/sources/image_source_impl.hpp - src/mbgl/style/sources/raster_dem_source.cpp - src/mbgl/style/sources/raster_source.cpp - src/mbgl/style/sources/raster_source_impl.cpp - src/mbgl/style/sources/raster_source_impl.hpp - src/mbgl/style/sources/vector_source.cpp - src/mbgl/style/sources/vector_source_impl.cpp - src/mbgl/style/sources/vector_source_impl.hpp - - # text - src/mbgl/text/bidi.hpp - src/mbgl/text/check_max_angle.cpp - src/mbgl/text/check_max_angle.hpp - src/mbgl/text/collision_feature.cpp - src/mbgl/text/collision_feature.hpp - src/mbgl/text/collision_index.cpp - src/mbgl/text/collision_index.hpp - src/mbgl/text/cross_tile_symbol_index.cpp - src/mbgl/text/cross_tile_symbol_index.hpp - src/mbgl/text/get_anchors.cpp - src/mbgl/text/get_anchors.hpp - src/mbgl/text/glyph.cpp - src/mbgl/text/glyph.hpp - src/mbgl/text/glyph_atlas.cpp - src/mbgl/text/glyph_atlas.hpp - src/mbgl/text/glyph_manager.cpp - src/mbgl/text/glyph_manager.hpp - src/mbgl/text/glyph_manager_observer.hpp - src/mbgl/text/glyph_pbf.cpp - src/mbgl/text/glyph_pbf.hpp - src/mbgl/text/glyph_range.hpp - src/mbgl/text/language_tag.cpp - src/mbgl/text/language_tag.hpp - src/mbgl/text/local_glyph_rasterizer.hpp - src/mbgl/text/placement.cpp - src/mbgl/text/placement.hpp - src/mbgl/text/quads.cpp - src/mbgl/text/quads.hpp - src/mbgl/text/shaping.cpp - src/mbgl/text/shaping.hpp - - # tile - include/mbgl/tile/tile_id.hpp - include/mbgl/tile/tile_necessity.hpp - src/mbgl/tile/custom_geometry_tile.cpp - src/mbgl/tile/custom_geometry_tile.hpp - src/mbgl/tile/geojson_tile.cpp - src/mbgl/tile/geojson_tile.hpp - src/mbgl/tile/geojson_tile_data.hpp - src/mbgl/tile/geometry_tile.cpp - src/mbgl/tile/geometry_tile.hpp - src/mbgl/tile/geometry_tile_data.cpp - src/mbgl/tile/geometry_tile_data.hpp - src/mbgl/tile/geometry_tile_worker.cpp - src/mbgl/tile/geometry_tile_worker.hpp - src/mbgl/tile/raster_dem_tile.cpp - src/mbgl/tile/raster_dem_tile.hpp - src/mbgl/tile/raster_dem_tile_worker.cpp - src/mbgl/tile/raster_dem_tile_worker.hpp - src/mbgl/tile/raster_tile.cpp - src/mbgl/tile/raster_tile.hpp - src/mbgl/tile/raster_tile_worker.cpp - src/mbgl/tile/raster_tile_worker.hpp - src/mbgl/tile/tile.cpp - src/mbgl/tile/tile.hpp - src/mbgl/tile/tile_cache.cpp - src/mbgl/tile/tile_cache.hpp - src/mbgl/tile/tile_id_hash.cpp - src/mbgl/tile/tile_id_io.cpp - src/mbgl/tile/tile_loader.hpp - src/mbgl/tile/tile_loader_impl.hpp - src/mbgl/tile/tile_observer.hpp - src/mbgl/tile/vector_tile.cpp - src/mbgl/tile/vector_tile.hpp - src/mbgl/tile/vector_tile_data.cpp - src/mbgl/tile/vector_tile_data.hpp - - # util - include/mbgl/util/async_request.hpp - include/mbgl/util/async_task.hpp - include/mbgl/util/char_array_buffer.hpp - include/mbgl/util/chrono.hpp - include/mbgl/util/color.hpp - include/mbgl/util/compression.hpp - include/mbgl/util/constants.hpp - include/mbgl/util/convert.hpp - include/mbgl/util/enum.hpp - include/mbgl/util/event.hpp - include/mbgl/util/exception.hpp - include/mbgl/util/feature.hpp - include/mbgl/util/font_stack.hpp - include/mbgl/util/geo.hpp - include/mbgl/util/geojson.hpp - include/mbgl/util/geometry.hpp - include/mbgl/util/ignore.hpp - include/mbgl/util/image.hpp - include/mbgl/util/immutable.hpp - include/mbgl/util/indexed_tuple.hpp - include/mbgl/util/interpolate.hpp - include/mbgl/util/logging.hpp - include/mbgl/util/noncopyable.hpp - include/mbgl/util/optional.hpp - include/mbgl/util/platform.hpp - include/mbgl/util/premultiply.hpp - include/mbgl/util/projection.hpp - include/mbgl/util/range.hpp - include/mbgl/util/run_loop.hpp - include/mbgl/util/size.hpp - include/mbgl/util/string.hpp - include/mbgl/util/thread.hpp - include/mbgl/util/tileset.hpp - include/mbgl/util/timer.hpp - include/mbgl/util/traits.hpp - include/mbgl/util/tuple.hpp - include/mbgl/util/type_list.hpp - include/mbgl/util/unique_any.hpp - include/mbgl/util/unitbezier.hpp - include/mbgl/util/util.hpp - include/mbgl/util/variant.hpp - include/mbgl/util/work_request.hpp - include/mbgl/util/work_task.hpp - include/mbgl/util/work_task_impl.hpp - src/mbgl/util/chrono.cpp - src/mbgl/util/clip_id.cpp - src/mbgl/util/clip_id.hpp - src/mbgl/util/color.cpp - src/mbgl/util/compression.cpp - src/mbgl/util/constants.cpp - src/mbgl/util/convert.cpp - src/mbgl/util/dtoa.cpp - src/mbgl/util/dtoa.hpp - src/mbgl/util/event.cpp - src/mbgl/util/font_stack.cpp - src/mbgl/util/geo.cpp - src/mbgl/util/geojson_impl.cpp - src/mbgl/util/grid_index.cpp - src/mbgl/util/grid_index.hpp - src/mbgl/util/http_header.cpp - src/mbgl/util/http_header.hpp - src/mbgl/util/http_timeout.cpp - src/mbgl/util/http_timeout.hpp - src/mbgl/util/i18n.cpp - src/mbgl/util/i18n.hpp - src/mbgl/util/interpolate.cpp - src/mbgl/util/intersection_tests.cpp - src/mbgl/util/intersection_tests.hpp - src/mbgl/util/io.cpp - src/mbgl/util/io.hpp - src/mbgl/util/logging.cpp - src/mbgl/util/longest_common_subsequence.hpp - src/mbgl/util/mapbox.cpp - src/mbgl/util/mapbox.hpp - src/mbgl/util/mat2.cpp - src/mbgl/util/mat2.hpp - src/mbgl/util/mat3.cpp - src/mbgl/util/mat3.hpp - src/mbgl/util/mat4.cpp - src/mbgl/util/mat4.hpp - src/mbgl/util/math.hpp - src/mbgl/util/offscreen_texture.cpp - src/mbgl/util/offscreen_texture.hpp - src/mbgl/util/premultiply.cpp - src/mbgl/util/rapidjson.hpp - src/mbgl/util/rect.hpp - src/mbgl/util/std.hpp - src/mbgl/util/stopwatch.cpp - src/mbgl/util/stopwatch.hpp - src/mbgl/util/string.cpp - src/mbgl/util/thread_local.hpp - src/mbgl/util/tile_coordinate.hpp - src/mbgl/util/tile_cover.cpp - src/mbgl/util/tile_cover.hpp - src/mbgl/util/tile_cover_impl.cpp - src/mbgl/util/tile_cover_impl.hpp - src/mbgl/util/tile_range.hpp - src/mbgl/util/tiny_sdf.cpp - src/mbgl/util/tiny_sdf.hpp - src/mbgl/util/token.hpp - src/mbgl/util/url.cpp - src/mbgl/util/url.hpp - src/mbgl/util/utf.hpp - src/mbgl/util/version.cpp - src/mbgl/util/version.hpp - src/mbgl/util/work_request.cpp - -) diff --git a/cmake/core-files.txt b/cmake/core-files.txt new file mode 100644 index 0000000000..be80bf677a --- /dev/null +++ b/cmake/core-files.txt @@ -0,0 +1,765 @@ +# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js + +# actor +include/mbgl/actor/actor.hpp +include/mbgl/actor/actor_ref.hpp +include/mbgl/actor/aspiring_actor.hpp +include/mbgl/actor/established_actor.hpp +include/mbgl/actor/mailbox.hpp +include/mbgl/actor/message.hpp +include/mbgl/actor/scheduler.hpp +src/mbgl/actor/mailbox.cpp +src/mbgl/actor/scheduler.cpp + +# algorithm +src/mbgl/algorithm/covered_by_children.hpp +src/mbgl/algorithm/generate_clip_ids.cpp +src/mbgl/algorithm/generate_clip_ids.hpp +src/mbgl/algorithm/generate_clip_ids_impl.hpp +src/mbgl/algorithm/update_renderables.hpp +src/mbgl/algorithm/update_tile_masks.hpp + +# annotation +include/mbgl/annotation/annotation.hpp +src/mbgl/annotation/annotation_manager.cpp +src/mbgl/annotation/annotation_manager.hpp +src/mbgl/annotation/annotation_source.cpp +src/mbgl/annotation/annotation_source.hpp +src/mbgl/annotation/annotation_tile.cpp +src/mbgl/annotation/annotation_tile.hpp +src/mbgl/annotation/fill_annotation_impl.cpp +src/mbgl/annotation/fill_annotation_impl.hpp +src/mbgl/annotation/line_annotation_impl.cpp +src/mbgl/annotation/line_annotation_impl.hpp +src/mbgl/annotation/render_annotation_source.cpp +src/mbgl/annotation/render_annotation_source.hpp +src/mbgl/annotation/shape_annotation_impl.cpp +src/mbgl/annotation/shape_annotation_impl.hpp +src/mbgl/annotation/symbol_annotation_impl.cpp +src/mbgl/annotation/symbol_annotation_impl.hpp + +# csscolorparser +src/csscolorparser/csscolorparser.cpp +src/csscolorparser/csscolorparser.hpp + +# geometry +src/mbgl/geometry/anchor.hpp +src/mbgl/geometry/debug_font_data.hpp +src/mbgl/geometry/dem_data.cpp +src/mbgl/geometry/dem_data.hpp +src/mbgl/geometry/feature_index.cpp +src/mbgl/geometry/feature_index.hpp +src/mbgl/geometry/line_atlas.cpp +src/mbgl/geometry/line_atlas.hpp + +# gl +src/mbgl/gl/attribute.cpp +src/mbgl/gl/attribute.hpp +src/mbgl/gl/color_mode.cpp +src/mbgl/gl/color_mode.hpp +src/mbgl/gl/context.cpp +src/mbgl/gl/context.hpp +src/mbgl/gl/debugging.cpp +src/mbgl/gl/debugging.hpp +src/mbgl/gl/debugging_extension.cpp +src/mbgl/gl/debugging_extension.hpp +src/mbgl/gl/depth_mode.cpp +src/mbgl/gl/depth_mode.hpp +src/mbgl/gl/draw_mode.hpp +src/mbgl/gl/extension.hpp +src/mbgl/gl/features.hpp +src/mbgl/gl/framebuffer.hpp +src/mbgl/gl/gl.cpp +src/mbgl/gl/gl.hpp +src/mbgl/gl/index_buffer.hpp +src/mbgl/gl/object.cpp +src/mbgl/gl/object.hpp +src/mbgl/gl/primitives.hpp +src/mbgl/gl/program.hpp +src/mbgl/gl/program_binary_extension.hpp +src/mbgl/gl/renderbuffer.hpp +src/mbgl/gl/state.hpp +src/mbgl/gl/stencil_mode.cpp +src/mbgl/gl/stencil_mode.hpp +src/mbgl/gl/texture.hpp +src/mbgl/gl/types.hpp +src/mbgl/gl/uniform.cpp +src/mbgl/gl/uniform.hpp +src/mbgl/gl/value.cpp +src/mbgl/gl/value.hpp +src/mbgl/gl/vertex_array.cpp +src/mbgl/gl/vertex_array.hpp +src/mbgl/gl/vertex_array_extension.hpp +src/mbgl/gl/vertex_buffer.hpp + +# layout +src/mbgl/layout/clip_lines.cpp +src/mbgl/layout/clip_lines.hpp +src/mbgl/layout/merge_lines.cpp +src/mbgl/layout/merge_lines.hpp +src/mbgl/layout/symbol_feature.hpp +src/mbgl/layout/symbol_instance.cpp +src/mbgl/layout/symbol_instance.hpp +src/mbgl/layout/symbol_layout.cpp +src/mbgl/layout/symbol_layout.hpp +src/mbgl/layout/symbol_projection.cpp +src/mbgl/layout/symbol_projection.hpp + +# map +include/mbgl/map/camera.hpp +include/mbgl/map/change.hpp +include/mbgl/map/map.hpp +include/mbgl/map/map_observer.hpp +include/mbgl/map/mode.hpp +src/mbgl/map/map.cpp +src/mbgl/map/transform.cpp +src/mbgl/map/transform.hpp +src/mbgl/map/transform_state.cpp +src/mbgl/map/transform_state.hpp +src/mbgl/map/zoom_history.hpp + +# math +include/mbgl/math/clamp.hpp +include/mbgl/math/log2.hpp +include/mbgl/math/minmax.hpp +include/mbgl/math/wrap.hpp +src/mbgl/math/log2.cpp + +# parsedate +src/parsedate/parsedate.cpp +src/parsedate/parsedate.hpp + +# programs +src/mbgl/programs/attributes.hpp +src/mbgl/programs/background_program.cpp +src/mbgl/programs/background_program.hpp +src/mbgl/programs/binary_program.cpp +src/mbgl/programs/binary_program.hpp +src/mbgl/programs/circle_program.cpp +src/mbgl/programs/circle_program.hpp +src/mbgl/programs/clipping_mask_program.hpp +src/mbgl/programs/collision_box_program.cpp +src/mbgl/programs/collision_box_program.hpp +src/mbgl/programs/debug_program.hpp +src/mbgl/programs/extrusion_texture_program.cpp +src/mbgl/programs/extrusion_texture_program.hpp +src/mbgl/programs/fill_extrusion_program.cpp +src/mbgl/programs/fill_extrusion_program.hpp +src/mbgl/programs/fill_program.cpp +src/mbgl/programs/fill_program.hpp +src/mbgl/programs/heatmap_program.cpp +src/mbgl/programs/heatmap_program.hpp +src/mbgl/programs/heatmap_texture_program.cpp +src/mbgl/programs/heatmap_texture_program.hpp +src/mbgl/programs/hillshade_prepare_program.cpp +src/mbgl/programs/hillshade_prepare_program.hpp +src/mbgl/programs/hillshade_program.cpp +src/mbgl/programs/hillshade_program.hpp +src/mbgl/programs/line_program.cpp +src/mbgl/programs/line_program.hpp +src/mbgl/programs/program.hpp +src/mbgl/programs/program_parameters.cpp +src/mbgl/programs/program_parameters.hpp +src/mbgl/programs/programs.hpp +src/mbgl/programs/raster_program.cpp +src/mbgl/programs/raster_program.hpp +src/mbgl/programs/segment.hpp +src/mbgl/programs/symbol_program.cpp +src/mbgl/programs/symbol_program.hpp +src/mbgl/programs/uniforms.hpp + +# renderer +include/mbgl/renderer/backend_scope.hpp +include/mbgl/renderer/mode.hpp +include/mbgl/renderer/query.hpp +include/mbgl/renderer/renderer.hpp +include/mbgl/renderer/renderer_backend.hpp +include/mbgl/renderer/renderer_frontend.hpp +include/mbgl/renderer/renderer_observer.hpp +src/mbgl/renderer/backend_scope.cpp +src/mbgl/renderer/bucket.hpp +src/mbgl/renderer/bucket_parameters.cpp +src/mbgl/renderer/bucket_parameters.hpp +src/mbgl/renderer/cross_faded_property_evaluator.cpp +src/mbgl/renderer/cross_faded_property_evaluator.hpp +src/mbgl/renderer/data_driven_property_evaluator.hpp +src/mbgl/renderer/group_by_layout.cpp +src/mbgl/renderer/group_by_layout.hpp +src/mbgl/renderer/image_atlas.cpp +src/mbgl/renderer/image_atlas.hpp +src/mbgl/renderer/image_manager.cpp +src/mbgl/renderer/image_manager.hpp +src/mbgl/renderer/paint_parameters.cpp +src/mbgl/renderer/paint_parameters.hpp +src/mbgl/renderer/paint_property_binder.hpp +src/mbgl/renderer/paint_property_statistics.hpp +src/mbgl/renderer/possibly_evaluated_property_value.hpp +src/mbgl/renderer/property_evaluation_parameters.hpp +src/mbgl/renderer/property_evaluator.hpp +src/mbgl/renderer/render_layer.cpp +src/mbgl/renderer/render_layer.hpp +src/mbgl/renderer/render_light.cpp +src/mbgl/renderer/render_light.hpp +src/mbgl/renderer/render_pass.hpp +src/mbgl/renderer/render_source.cpp +src/mbgl/renderer/render_source.hpp +src/mbgl/renderer/render_source_observer.hpp +src/mbgl/renderer/render_static_data.cpp +src/mbgl/renderer/render_static_data.hpp +src/mbgl/renderer/render_tile.cpp +src/mbgl/renderer/render_tile.hpp +src/mbgl/renderer/renderer.cpp +src/mbgl/renderer/renderer_backend.cpp +src/mbgl/renderer/renderer_impl.cpp +src/mbgl/renderer/renderer_impl.hpp +src/mbgl/renderer/style_diff.cpp +src/mbgl/renderer/style_diff.hpp +src/mbgl/renderer/tile_mask.hpp +src/mbgl/renderer/tile_parameters.hpp +src/mbgl/renderer/tile_pyramid.cpp +src/mbgl/renderer/tile_pyramid.hpp +src/mbgl/renderer/transition_parameters.hpp +src/mbgl/renderer/update_parameters.hpp + +# renderer/buckets +src/mbgl/renderer/buckets/circle_bucket.cpp +src/mbgl/renderer/buckets/circle_bucket.hpp +src/mbgl/renderer/buckets/debug_bucket.cpp +src/mbgl/renderer/buckets/debug_bucket.hpp +src/mbgl/renderer/buckets/fill_bucket.cpp +src/mbgl/renderer/buckets/fill_bucket.hpp +src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp +src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp +src/mbgl/renderer/buckets/heatmap_bucket.cpp +src/mbgl/renderer/buckets/heatmap_bucket.hpp +src/mbgl/renderer/buckets/hillshade_bucket.cpp +src/mbgl/renderer/buckets/hillshade_bucket.hpp +src/mbgl/renderer/buckets/line_bucket.cpp +src/mbgl/renderer/buckets/line_bucket.hpp +src/mbgl/renderer/buckets/raster_bucket.cpp +src/mbgl/renderer/buckets/raster_bucket.hpp +src/mbgl/renderer/buckets/symbol_bucket.cpp +src/mbgl/renderer/buckets/symbol_bucket.hpp + +# renderer/layers +src/mbgl/renderer/layers/render_background_layer.cpp +src/mbgl/renderer/layers/render_background_layer.hpp +src/mbgl/renderer/layers/render_circle_layer.cpp +src/mbgl/renderer/layers/render_circle_layer.hpp +src/mbgl/renderer/layers/render_custom_layer.cpp +src/mbgl/renderer/layers/render_custom_layer.hpp +src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp +src/mbgl/renderer/layers/render_fill_layer.cpp +src/mbgl/renderer/layers/render_fill_layer.hpp +src/mbgl/renderer/layers/render_heatmap_layer.cpp +src/mbgl/renderer/layers/render_heatmap_layer.hpp +src/mbgl/renderer/layers/render_hillshade_layer.cpp +src/mbgl/renderer/layers/render_hillshade_layer.hpp +src/mbgl/renderer/layers/render_line_layer.cpp +src/mbgl/renderer/layers/render_line_layer.hpp +src/mbgl/renderer/layers/render_raster_layer.cpp +src/mbgl/renderer/layers/render_raster_layer.hpp +src/mbgl/renderer/layers/render_symbol_layer.cpp +src/mbgl/renderer/layers/render_symbol_layer.hpp + +# renderer/sources +src/mbgl/renderer/sources/render_custom_geometry_source.cpp +src/mbgl/renderer/sources/render_custom_geometry_source.hpp +src/mbgl/renderer/sources/render_geojson_source.cpp +src/mbgl/renderer/sources/render_geojson_source.hpp +src/mbgl/renderer/sources/render_image_source.cpp +src/mbgl/renderer/sources/render_image_source.hpp +src/mbgl/renderer/sources/render_raster_dem_source.cpp +src/mbgl/renderer/sources/render_raster_dem_source.hpp +src/mbgl/renderer/sources/render_raster_source.cpp +src/mbgl/renderer/sources/render_raster_source.hpp +src/mbgl/renderer/sources/render_vector_source.cpp +src/mbgl/renderer/sources/render_vector_source.hpp + +# shaders +src/mbgl/shaders/background.cpp +src/mbgl/shaders/background.hpp +src/mbgl/shaders/background_pattern.cpp +src/mbgl/shaders/background_pattern.hpp +src/mbgl/shaders/circle.cpp +src/mbgl/shaders/circle.hpp +src/mbgl/shaders/clipping_mask.cpp +src/mbgl/shaders/clipping_mask.hpp +src/mbgl/shaders/collision_box.cpp +src/mbgl/shaders/collision_box.hpp +src/mbgl/shaders/collision_circle.cpp +src/mbgl/shaders/collision_circle.hpp +src/mbgl/shaders/debug.cpp +src/mbgl/shaders/debug.hpp +src/mbgl/shaders/extrusion_texture.cpp +src/mbgl/shaders/extrusion_texture.hpp +src/mbgl/shaders/fill.cpp +src/mbgl/shaders/fill.hpp +src/mbgl/shaders/fill_extrusion.cpp +src/mbgl/shaders/fill_extrusion.hpp +src/mbgl/shaders/fill_extrusion_pattern.cpp +src/mbgl/shaders/fill_extrusion_pattern.hpp +src/mbgl/shaders/fill_outline.cpp +src/mbgl/shaders/fill_outline.hpp +src/mbgl/shaders/fill_outline_pattern.cpp +src/mbgl/shaders/fill_outline_pattern.hpp +src/mbgl/shaders/fill_pattern.cpp +src/mbgl/shaders/fill_pattern.hpp +src/mbgl/shaders/heatmap.cpp +src/mbgl/shaders/heatmap.hpp +src/mbgl/shaders/heatmap_texture.cpp +src/mbgl/shaders/heatmap_texture.hpp +src/mbgl/shaders/hillshade.cpp +src/mbgl/shaders/hillshade.hpp +src/mbgl/shaders/hillshade_prepare.cpp +src/mbgl/shaders/hillshade_prepare.hpp +src/mbgl/shaders/line.cpp +src/mbgl/shaders/line.hpp +src/mbgl/shaders/line_pattern.cpp +src/mbgl/shaders/line_pattern.hpp +src/mbgl/shaders/line_sdf.cpp +src/mbgl/shaders/line_sdf.hpp +src/mbgl/shaders/preludes.cpp +src/mbgl/shaders/preludes.hpp +src/mbgl/shaders/raster.cpp +src/mbgl/shaders/raster.hpp +src/mbgl/shaders/shaders.cpp +src/mbgl/shaders/shaders.hpp +src/mbgl/shaders/source.cpp +src/mbgl/shaders/source.hpp +src/mbgl/shaders/symbol_icon.cpp +src/mbgl/shaders/symbol_icon.hpp +src/mbgl/shaders/symbol_sdf.cpp +src/mbgl/shaders/symbol_sdf.hpp + +# sprite +src/mbgl/sprite/sprite_loader.cpp +src/mbgl/sprite/sprite_loader.hpp +src/mbgl/sprite/sprite_loader_observer.hpp +src/mbgl/sprite/sprite_loader_worker.cpp +src/mbgl/sprite/sprite_loader_worker.hpp +src/mbgl/sprite/sprite_parser.cpp +src/mbgl/sprite/sprite_parser.hpp + +# storage +include/mbgl/storage/default_file_source.hpp +include/mbgl/storage/file_source.hpp +include/mbgl/storage/network_status.hpp +include/mbgl/storage/offline.hpp +include/mbgl/storage/online_file_source.hpp +include/mbgl/storage/resource.hpp +include/mbgl/storage/resource_transform.hpp +include/mbgl/storage/response.hpp +src/mbgl/storage/asset_file_source.hpp +src/mbgl/storage/http_file_source.hpp +src/mbgl/storage/local_file_source.hpp +src/mbgl/storage/network_status.cpp +src/mbgl/storage/resource.cpp +src/mbgl/storage/resource_transform.cpp +src/mbgl/storage/response.cpp + +# style +include/mbgl/style/color_ramp_property_value.hpp +include/mbgl/style/conversion.hpp +include/mbgl/style/conversion_impl.hpp +include/mbgl/style/filter.hpp +include/mbgl/style/image.hpp +include/mbgl/style/layer.hpp +include/mbgl/style/layer_type.hpp +include/mbgl/style/light.hpp +include/mbgl/style/position.hpp +include/mbgl/style/property_expression.hpp +include/mbgl/style/property_value.hpp +include/mbgl/style/source.hpp +include/mbgl/style/style.hpp +include/mbgl/style/transition_options.hpp +include/mbgl/style/types.hpp +include/mbgl/style/undefined.hpp +src/mbgl/style/collection.hpp +src/mbgl/style/custom_tile_loader.cpp +src/mbgl/style/custom_tile_loader.hpp +src/mbgl/style/filter.cpp +src/mbgl/style/image.cpp +src/mbgl/style/image_impl.cpp +src/mbgl/style/image_impl.hpp +src/mbgl/style/layer.cpp +src/mbgl/style/layer_impl.cpp +src/mbgl/style/layer_impl.hpp +src/mbgl/style/layer_observer.hpp +src/mbgl/style/layout_property.hpp +src/mbgl/style/light.cpp +src/mbgl/style/light_impl.cpp +src/mbgl/style/light_impl.hpp +src/mbgl/style/light_observer.hpp +src/mbgl/style/observer.hpp +src/mbgl/style/paint_property.hpp +src/mbgl/style/parser.cpp +src/mbgl/style/parser.hpp +src/mbgl/style/properties.hpp +src/mbgl/style/rapidjson_conversion.hpp +src/mbgl/style/source.cpp +src/mbgl/style/source_impl.cpp +src/mbgl/style/source_impl.hpp +src/mbgl/style/source_observer.hpp +src/mbgl/style/style.cpp +src/mbgl/style/style_impl.cpp +src/mbgl/style/style_impl.hpp +src/mbgl/style/types.cpp + +# style/conversion +include/mbgl/style/conversion/color_ramp_property_value.hpp +include/mbgl/style/conversion/constant.hpp +include/mbgl/style/conversion/coordinate.hpp +include/mbgl/style/conversion/custom_geometry_source_options.hpp +include/mbgl/style/conversion/filter.hpp +include/mbgl/style/conversion/function.hpp +include/mbgl/style/conversion/geojson.hpp +include/mbgl/style/conversion/geojson_options.hpp +include/mbgl/style/conversion/get_json_type.hpp +include/mbgl/style/conversion/layer.hpp +include/mbgl/style/conversion/light.hpp +include/mbgl/style/conversion/position.hpp +include/mbgl/style/conversion/property_value.hpp +include/mbgl/style/conversion/source.hpp +include/mbgl/style/conversion/tileset.hpp +include/mbgl/style/conversion/transition_options.hpp +src/mbgl/style/conversion/color_ramp_property_value.cpp +src/mbgl/style/conversion/constant.cpp +src/mbgl/style/conversion/coordinate.cpp +src/mbgl/style/conversion/custom_geometry_source_options.cpp +src/mbgl/style/conversion/filter.cpp +src/mbgl/style/conversion/function.cpp +src/mbgl/style/conversion/geojson.cpp +src/mbgl/style/conversion/geojson_options.cpp +src/mbgl/style/conversion/get_json_type.cpp +src/mbgl/style/conversion/json.hpp +src/mbgl/style/conversion/layer.cpp +src/mbgl/style/conversion/light.cpp +src/mbgl/style/conversion/position.cpp +src/mbgl/style/conversion/property_value.cpp +src/mbgl/style/conversion/source.cpp +src/mbgl/style/conversion/stringify.hpp +src/mbgl/style/conversion/tileset.cpp +src/mbgl/style/conversion/transition_options.cpp + +# style/expression +include/mbgl/style/expression/array_assertion.hpp +include/mbgl/style/expression/assertion.hpp +include/mbgl/style/expression/at.hpp +include/mbgl/style/expression/boolean_operator.hpp +include/mbgl/style/expression/case.hpp +include/mbgl/style/expression/check_subtype.hpp +include/mbgl/style/expression/coalesce.hpp +include/mbgl/style/expression/coercion.hpp +include/mbgl/style/expression/collator.hpp +include/mbgl/style/expression/collator_expression.hpp +include/mbgl/style/expression/comparison.hpp +include/mbgl/style/expression/compound_expression.hpp +include/mbgl/style/expression/dsl.hpp +include/mbgl/style/expression/error.hpp +include/mbgl/style/expression/expression.hpp +include/mbgl/style/expression/find_zoom_curve.hpp +include/mbgl/style/expression/get_covering_stops.hpp +include/mbgl/style/expression/interpolate.hpp +include/mbgl/style/expression/interpolator.hpp +include/mbgl/style/expression/is_constant.hpp +include/mbgl/style/expression/is_expression.hpp +include/mbgl/style/expression/length.hpp +include/mbgl/style/expression/let.hpp +include/mbgl/style/expression/literal.hpp +include/mbgl/style/expression/match.hpp +include/mbgl/style/expression/parsing_context.hpp +include/mbgl/style/expression/step.hpp +include/mbgl/style/expression/type.hpp +include/mbgl/style/expression/value.hpp +src/mbgl/style/expression/array_assertion.cpp +src/mbgl/style/expression/assertion.cpp +src/mbgl/style/expression/at.cpp +src/mbgl/style/expression/boolean_operator.cpp +src/mbgl/style/expression/case.cpp +src/mbgl/style/expression/check_subtype.cpp +src/mbgl/style/expression/coalesce.cpp +src/mbgl/style/expression/coercion.cpp +src/mbgl/style/expression/collator_expression.cpp +src/mbgl/style/expression/comparison.cpp +src/mbgl/style/expression/compound_expression.cpp +src/mbgl/style/expression/dsl.cpp +src/mbgl/style/expression/expression.cpp +src/mbgl/style/expression/find_zoom_curve.cpp +src/mbgl/style/expression/get_covering_stops.cpp +src/mbgl/style/expression/interpolate.cpp +src/mbgl/style/expression/is_constant.cpp +src/mbgl/style/expression/is_expression.cpp +src/mbgl/style/expression/length.cpp +src/mbgl/style/expression/let.cpp +src/mbgl/style/expression/literal.cpp +src/mbgl/style/expression/match.cpp +src/mbgl/style/expression/parsing_context.cpp +src/mbgl/style/expression/step.cpp +src/mbgl/style/expression/util.cpp +src/mbgl/style/expression/util.hpp +src/mbgl/style/expression/value.cpp + +# style/layers +include/mbgl/style/layers/background_layer.hpp +include/mbgl/style/layers/circle_layer.hpp +include/mbgl/style/layers/custom_layer.hpp +include/mbgl/style/layers/fill_extrusion_layer.hpp +include/mbgl/style/layers/fill_layer.hpp +include/mbgl/style/layers/heatmap_layer.hpp +include/mbgl/style/layers/hillshade_layer.hpp +include/mbgl/style/layers/line_layer.hpp +include/mbgl/style/layers/raster_layer.hpp +include/mbgl/style/layers/symbol_layer.hpp +src/mbgl/style/layers/background_layer.cpp +src/mbgl/style/layers/background_layer_impl.cpp +src/mbgl/style/layers/background_layer_impl.hpp +src/mbgl/style/layers/background_layer_properties.cpp +src/mbgl/style/layers/background_layer_properties.hpp +src/mbgl/style/layers/circle_layer.cpp +src/mbgl/style/layers/circle_layer_impl.cpp +src/mbgl/style/layers/circle_layer_impl.hpp +src/mbgl/style/layers/circle_layer_properties.cpp +src/mbgl/style/layers/circle_layer_properties.hpp +src/mbgl/style/layers/custom_layer.cpp +src/mbgl/style/layers/custom_layer_impl.cpp +src/mbgl/style/layers/custom_layer_impl.hpp +src/mbgl/style/layers/fill_extrusion_layer.cpp +src/mbgl/style/layers/fill_extrusion_layer_impl.cpp +src/mbgl/style/layers/fill_extrusion_layer_impl.hpp +src/mbgl/style/layers/fill_extrusion_layer_properties.cpp +src/mbgl/style/layers/fill_extrusion_layer_properties.hpp +src/mbgl/style/layers/fill_layer.cpp +src/mbgl/style/layers/fill_layer_impl.cpp +src/mbgl/style/layers/fill_layer_impl.hpp +src/mbgl/style/layers/fill_layer_properties.cpp +src/mbgl/style/layers/fill_layer_properties.hpp +src/mbgl/style/layers/heatmap_layer.cpp +src/mbgl/style/layers/heatmap_layer_impl.cpp +src/mbgl/style/layers/heatmap_layer_impl.hpp +src/mbgl/style/layers/heatmap_layer_properties.cpp +src/mbgl/style/layers/heatmap_layer_properties.hpp +src/mbgl/style/layers/hillshade_layer.cpp +src/mbgl/style/layers/hillshade_layer_impl.cpp +src/mbgl/style/layers/hillshade_layer_impl.hpp +src/mbgl/style/layers/hillshade_layer_properties.cpp +src/mbgl/style/layers/hillshade_layer_properties.hpp +src/mbgl/style/layers/line_layer.cpp +src/mbgl/style/layers/line_layer_impl.cpp +src/mbgl/style/layers/line_layer_impl.hpp +src/mbgl/style/layers/line_layer_properties.cpp +src/mbgl/style/layers/line_layer_properties.hpp +src/mbgl/style/layers/raster_layer.cpp +src/mbgl/style/layers/raster_layer_impl.cpp +src/mbgl/style/layers/raster_layer_impl.hpp +src/mbgl/style/layers/raster_layer_properties.cpp +src/mbgl/style/layers/raster_layer_properties.hpp +src/mbgl/style/layers/symbol_layer.cpp +src/mbgl/style/layers/symbol_layer_impl.cpp +src/mbgl/style/layers/symbol_layer_impl.hpp +src/mbgl/style/layers/symbol_layer_properties.cpp +src/mbgl/style/layers/symbol_layer_properties.hpp + +# style/sources +include/mbgl/style/sources/custom_geometry_source.hpp +include/mbgl/style/sources/geojson_source.hpp +include/mbgl/style/sources/image_source.hpp +include/mbgl/style/sources/raster_dem_source.hpp +include/mbgl/style/sources/raster_source.hpp +include/mbgl/style/sources/vector_source.hpp +src/mbgl/style/sources/custom_geometry_source.cpp +src/mbgl/style/sources/custom_geometry_source_impl.cpp +src/mbgl/style/sources/custom_geometry_source_impl.hpp +src/mbgl/style/sources/geojson_source.cpp +src/mbgl/style/sources/geojson_source_impl.cpp +src/mbgl/style/sources/geojson_source_impl.hpp +src/mbgl/style/sources/image_source.cpp +src/mbgl/style/sources/image_source_impl.cpp +src/mbgl/style/sources/image_source_impl.hpp +src/mbgl/style/sources/raster_dem_source.cpp +src/mbgl/style/sources/raster_source.cpp +src/mbgl/style/sources/raster_source_impl.cpp +src/mbgl/style/sources/raster_source_impl.hpp +src/mbgl/style/sources/vector_source.cpp +src/mbgl/style/sources/vector_source_impl.cpp +src/mbgl/style/sources/vector_source_impl.hpp + +# text +src/mbgl/text/bidi.hpp +src/mbgl/text/check_max_angle.cpp +src/mbgl/text/check_max_angle.hpp +src/mbgl/text/collision_feature.cpp +src/mbgl/text/collision_feature.hpp +src/mbgl/text/collision_index.cpp +src/mbgl/text/collision_index.hpp +src/mbgl/text/cross_tile_symbol_index.cpp +src/mbgl/text/cross_tile_symbol_index.hpp +src/mbgl/text/get_anchors.cpp +src/mbgl/text/get_anchors.hpp +src/mbgl/text/glyph.cpp +src/mbgl/text/glyph.hpp +src/mbgl/text/glyph_atlas.cpp +src/mbgl/text/glyph_atlas.hpp +src/mbgl/text/glyph_manager.cpp +src/mbgl/text/glyph_manager.hpp +src/mbgl/text/glyph_manager_observer.hpp +src/mbgl/text/glyph_pbf.cpp +src/mbgl/text/glyph_pbf.hpp +src/mbgl/text/glyph_range.hpp +src/mbgl/text/language_tag.cpp +src/mbgl/text/language_tag.hpp +src/mbgl/text/local_glyph_rasterizer.hpp +src/mbgl/text/placement.cpp +src/mbgl/text/placement.hpp +src/mbgl/text/quads.cpp +src/mbgl/text/quads.hpp +src/mbgl/text/shaping.cpp +src/mbgl/text/shaping.hpp + +# tile +include/mbgl/tile/tile_id.hpp +include/mbgl/tile/tile_necessity.hpp +src/mbgl/tile/custom_geometry_tile.cpp +src/mbgl/tile/custom_geometry_tile.hpp +src/mbgl/tile/geojson_tile.cpp +src/mbgl/tile/geojson_tile.hpp +src/mbgl/tile/geojson_tile_data.hpp +src/mbgl/tile/geometry_tile.cpp +src/mbgl/tile/geometry_tile.hpp +src/mbgl/tile/geometry_tile_data.cpp +src/mbgl/tile/geometry_tile_data.hpp +src/mbgl/tile/geometry_tile_worker.cpp +src/mbgl/tile/geometry_tile_worker.hpp +src/mbgl/tile/raster_dem_tile.cpp +src/mbgl/tile/raster_dem_tile.hpp +src/mbgl/tile/raster_dem_tile_worker.cpp +src/mbgl/tile/raster_dem_tile_worker.hpp +src/mbgl/tile/raster_tile.cpp +src/mbgl/tile/raster_tile.hpp +src/mbgl/tile/raster_tile_worker.cpp +src/mbgl/tile/raster_tile_worker.hpp +src/mbgl/tile/tile.cpp +src/mbgl/tile/tile.hpp +src/mbgl/tile/tile_cache.cpp +src/mbgl/tile/tile_cache.hpp +src/mbgl/tile/tile_id_hash.cpp +src/mbgl/tile/tile_id_io.cpp +src/mbgl/tile/tile_loader.hpp +src/mbgl/tile/tile_loader_impl.hpp +src/mbgl/tile/tile_observer.hpp +src/mbgl/tile/vector_tile.cpp +src/mbgl/tile/vector_tile.hpp +src/mbgl/tile/vector_tile_data.cpp +src/mbgl/tile/vector_tile_data.hpp + +# util +include/mbgl/util/async_request.hpp +include/mbgl/util/async_task.hpp +include/mbgl/util/char_array_buffer.hpp +include/mbgl/util/chrono.hpp +include/mbgl/util/color.hpp +include/mbgl/util/compression.hpp +include/mbgl/util/constants.hpp +include/mbgl/util/convert.hpp +include/mbgl/util/enum.hpp +include/mbgl/util/event.hpp +include/mbgl/util/exception.hpp +include/mbgl/util/expected.hpp +include/mbgl/util/feature.hpp +include/mbgl/util/font_stack.hpp +include/mbgl/util/geo.hpp +include/mbgl/util/geojson.hpp +include/mbgl/util/geometry.hpp +include/mbgl/util/ignore.hpp +include/mbgl/util/image.hpp +include/mbgl/util/immutable.hpp +include/mbgl/util/indexed_tuple.hpp +include/mbgl/util/interpolate.hpp +include/mbgl/util/logging.hpp +include/mbgl/util/noncopyable.hpp +include/mbgl/util/optional.hpp +include/mbgl/util/peer.hpp +include/mbgl/util/platform.hpp +include/mbgl/util/premultiply.hpp +include/mbgl/util/projection.hpp +include/mbgl/util/range.hpp +include/mbgl/util/run_loop.hpp +include/mbgl/util/size.hpp +include/mbgl/util/string.hpp +include/mbgl/util/thread.hpp +include/mbgl/util/tileset.hpp +include/mbgl/util/timer.hpp +include/mbgl/util/traits.hpp +include/mbgl/util/tuple.hpp +include/mbgl/util/type_list.hpp +include/mbgl/util/unitbezier.hpp +include/mbgl/util/util.hpp +include/mbgl/util/variant.hpp +include/mbgl/util/work_request.hpp +include/mbgl/util/work_task.hpp +include/mbgl/util/work_task_impl.hpp +src/mbgl/util/chrono.cpp +src/mbgl/util/clip_id.cpp +src/mbgl/util/clip_id.hpp +src/mbgl/util/color.cpp +src/mbgl/util/compression.cpp +src/mbgl/util/constants.cpp +src/mbgl/util/convert.cpp +src/mbgl/util/dtoa.cpp +src/mbgl/util/dtoa.hpp +src/mbgl/util/event.cpp +src/mbgl/util/fnv_hash.hpp +src/mbgl/util/font_stack.cpp +src/mbgl/util/geo.cpp +src/mbgl/util/geojson_impl.cpp +src/mbgl/util/grid_index.cpp +src/mbgl/util/grid_index.hpp +src/mbgl/util/http_header.cpp +src/mbgl/util/http_header.hpp +src/mbgl/util/http_timeout.cpp +src/mbgl/util/http_timeout.hpp +src/mbgl/util/i18n.cpp +src/mbgl/util/i18n.hpp +src/mbgl/util/interpolate.cpp +src/mbgl/util/intersection_tests.cpp +src/mbgl/util/intersection_tests.hpp +src/mbgl/util/io.cpp +src/mbgl/util/io.hpp +src/mbgl/util/logging.cpp +src/mbgl/util/longest_common_subsequence.hpp +src/mbgl/util/mapbox.cpp +src/mbgl/util/mapbox.hpp +src/mbgl/util/mat2.cpp +src/mbgl/util/mat2.hpp +src/mbgl/util/mat3.cpp +src/mbgl/util/mat3.hpp +src/mbgl/util/mat4.cpp +src/mbgl/util/mat4.hpp +src/mbgl/util/math.hpp +src/mbgl/util/offscreen_texture.cpp +src/mbgl/util/offscreen_texture.hpp +src/mbgl/util/premultiply.cpp +src/mbgl/util/rapidjson.hpp +src/mbgl/util/rect.hpp +src/mbgl/util/std.hpp +src/mbgl/util/stopwatch.cpp +src/mbgl/util/stopwatch.hpp +src/mbgl/util/string.cpp +src/mbgl/util/thread_local.hpp +src/mbgl/util/tile_coordinate.hpp +src/mbgl/util/tile_cover.cpp +src/mbgl/util/tile_cover.hpp +src/mbgl/util/tile_cover_impl.cpp +src/mbgl/util/tile_cover_impl.hpp +src/mbgl/util/tile_range.hpp +src/mbgl/util/tiny_sdf.cpp +src/mbgl/util/tiny_sdf.hpp +src/mbgl/util/token.hpp +src/mbgl/util/url.cpp +src/mbgl/util/url.hpp +src/mbgl/util/utf.hpp +src/mbgl/util/version.cpp +src/mbgl/util/version.hpp +src/mbgl/util/work_request.cpp + diff --git a/cmake/core.cmake b/cmake/core.cmake index 436c767182..d52f7ba32f 100644 --- a/cmake/core.cmake +++ b/cmake/core.cmake @@ -1,6 +1,5 @@ -add_library(mbgl-core STATIC - ${MBGL_CORE_FILES} -) +load_sources_list(MBGL_CORE_FILES cmake/core-files.txt) +add_library(mbgl-core STATIC ${MBGL_CORE_FILES}) target_include_directories(mbgl-core PUBLIC include diff --git a/cmake/files.cmake.ejs b/cmake/files.txt.ejs index 57126509ed..6c210347f6 100644 --- a/cmake/files.cmake.ejs +++ b/cmake/files.txt.ejs @@ -1,13 +1,10 @@ <% - const name = locals.name; const groups = locals.groups; -%> # This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js -set(MBGL_<%- snakeCaseUpper(name) %>_FILES <% for (const key of Object.keys(groups).sort()) { -%> - # <%- key %> - <%- groups[key].sort().join('\n ') %> +# <%- key %> +<%- groups[key].sort().join('\n') %> <% } -%> -) diff --git a/cmake/filesource-files.txt b/cmake/filesource-files.txt new file mode 100644 index 0000000000..9e806d9e54 --- /dev/null +++ b/cmake/filesource-files.txt @@ -0,0 +1,24 @@ +# File source +include/mbgl/storage/default_file_source.hpp +platform/default/default_file_source.cpp +platform/default/mbgl/storage/file_source_request.hpp +platform/default/file_source_request.cpp +include/mbgl/storage/online_file_source.hpp +platform/default/online_file_source.cpp +src/mbgl/storage/http_file_source.hpp +src/mbgl/storage/asset_file_source.hpp +platform/default/asset_file_source.cpp +src/mbgl/storage/local_file_source.hpp +platform/default/local_file_source.cpp + +# Offline +include/mbgl/storage/offline.hpp +platform/default/mbgl/storage/offline.cpp +platform/default/mbgl/storage/offline_database.hpp +platform/default/mbgl/storage/offline_database.cpp +platform/default/mbgl/storage/offline_download.hpp +platform/default/mbgl/storage/offline_download.cpp +platform/default/mbgl/storage/offline_schema.hpp + +# Database +platform/default/sqlite3.hpp diff --git a/cmake/filesource.cmake b/cmake/filesource.cmake index ccd2192f39..6486a4af32 100644 --- a/cmake/filesource.cmake +++ b/cmake/filesource.cmake @@ -1,29 +1,7 @@ -add_library(mbgl-filesource STATIC - # File source - include/mbgl/storage/default_file_source.hpp - platform/default/default_file_source.cpp - platform/default/mbgl/storage/file_source_request.hpp - platform/default/file_source_request.cpp - include/mbgl/storage/online_file_source.hpp - platform/default/online_file_source.cpp - src/mbgl/storage/http_file_source.hpp - src/mbgl/storage/asset_file_source.hpp - platform/default/asset_file_source.cpp - src/mbgl/storage/local_file_source.hpp - platform/default/local_file_source.cpp - - # Offline - include/mbgl/storage/offline.hpp - platform/default/mbgl/storage/offline.cpp - platform/default/mbgl/storage/offline_database.hpp - platform/default/mbgl/storage/offline_database.cpp - platform/default/mbgl/storage/offline_download.hpp - platform/default/mbgl/storage/offline_download.cpp - platform/default/mbgl/storage/offline_schema.hpp - - # Database - platform/default/sqlite3.hpp -) +add_vendor_target(expected INTERFACE) + +load_sources_list(MBGL_FILESOURCE_FILES cmake/filesource-files.txt) +add_library(mbgl-filesource STATIC ${MBGL_FILESOURCE_FILES}) target_add_mason_package(mbgl-filesource PUBLIC geometry) target_add_mason_package(mbgl-filesource PUBLIC variant) @@ -39,6 +17,7 @@ target_include_directories(mbgl-filesource target_link_libraries(mbgl-filesource PUBLIC mbgl-core + PUBLIC expected ) mbgl_filesource() diff --git a/cmake/mason-dependencies.cmake b/cmake/mason-dependencies.cmake index d4e0378804..826dcf2e45 100644 --- a/cmake/mason-dependencies.cmake +++ b/cmake/mason-dependencies.cmake @@ -5,7 +5,7 @@ mason_use(variant VERSION 1.1.4 HEADER_ONLY) mason_use(unique_resource VERSION cba309e HEADER_ONLY) mason_use(rapidjson VERSION 1.1.0 HEADER_ONLY) mason_use(boost VERSION 1.65.1 HEADER_ONLY) -mason_use(geojsonvt VERSION 6.5.1 HEADER_ONLY) +mason_use(geojsonvt VERSION 6.6.0 HEADER_ONLY) mason_use(supercluster VERSION 0.2.2 HEADER_ONLY) mason_use(kdbush VERSION 0.1.1-1 HEADER_ONLY) mason_use(earcut VERSION 0.12.4 HEADER_ONLY) @@ -20,13 +20,13 @@ mason_use(vector-tile VERSION 1.0.2 HEADER_ONLY) if(MBGL_PLATFORM STREQUAL "android") mason_use(jni.hpp VERSION 3.0.0 HEADER_ONLY) - mason_use(sqlite VERSION 3.14.2) + mason_use(sqlite VERSION 3.24.0-min-size) mason_use(icu VERSION 58.1-min-size) elseif(MBGL_PLATFORM STREQUAL "ios") mason_use(icu VERSION 58.1-min-size) elseif(MBGL_PLATFORM STREQUAL "linux") mason_use(glfw VERSION 2018-06-27-0be4f3f) - mason_use(sqlite VERSION 3.14.2) + mason_use(sqlite VERSION 3.24.0-min-size) mason_use(libuv VERSION 1.9.1) mason_use(libpng VERSION 1.6.25) mason_use(libjpeg-turbo VERSION 1.5.0) diff --git a/cmake/mason.cmake b/cmake/mason.cmake index 6116067080..76d02b95b5 100644 --- a/cmake/mason.cmake +++ b/cmake/mason.cmake @@ -24,11 +24,11 @@ function(mason_detect_platform) # Android Studio only passes ANDROID_ABI, but we need to adjust that to the Mason if(MASON_PLATFORM STREQUAL "android" AND NOT MASON_PLATFORM_VERSION) if (ANDROID_ABI STREQUAL "armeabi-v7a") - set(MASON_PLATFORM_VERSION "arm-v7-9" PARENT_SCOPE) + set(MASON_PLATFORM_VERSION "arm-v7-14" PARENT_SCOPE) elseif (ANDROID_ABI STREQUAL "arm64-v8a") set(MASON_PLATFORM_VERSION "arm-v8-21" PARENT_SCOPE) elseif (ANDROID_ABI STREQUAL "x86") - set(MASON_PLATFORM_VERSION "x86-9" PARENT_SCOPE) + set(MASON_PLATFORM_VERSION "x86-14" PARENT_SCOPE) elseif (ANDROID_ABI STREQUAL "x86_64") set(MASON_PLATFORM_VERSION "x86-64-21" PARENT_SCOPE) else() @@ -84,17 +84,15 @@ function(mason_use _PACKAGE) set(_URL "${MASON_REPOSITORY}/${_SLUG}.tar.gz") message("[Mason] Downloading package ${_URL}...") - set(_FAILED) - set(_ERROR) - # Note: some CMake versions are compiled without SSL support + set(_STATUS) get_filename_component(_CACHE_DIR "${_CACHE_PATH}" DIRECTORY) file(MAKE_DIRECTORY "${_CACHE_DIR}") - execute_process( - COMMAND curl --retry 3 -s -f -S -L "${_URL}" -o "${_CACHE_PATH}.tmp" - RESULT_VARIABLE _FAILED - ERROR_VARIABLE _ERROR) - if(_FAILED) - message(FATAL_ERROR "[Mason] Failed to download ${_URL}: ${_ERROR}") + file(DOWNLOAD "${_URL}" "${_CACHE_PATH}.tmp" STATUS _STATUS TLS_VERIFY ON) + + list(GET _STATUS 0 _STATUS_CODE) + list(GET _STATUS 1 _STATUS_STRING) + if(NOT _STATUS_CODE EQUAL 0) + message(FATAL_ERROR "[Mason] Failed to download ${_URL}: ${_STATUS_STRING}") else() # We downloaded to a temporary file to prevent half-finished downloads file(RENAME "${_CACHE_PATH}.tmp" "${_CACHE_PATH}") diff --git a/cmake/mbgl.cmake b/cmake/mbgl.cmake index 338c2fd708..fdb435ccca 100644 --- a/cmake/mbgl.cmake +++ b/cmake/mbgl.cmake @@ -42,11 +42,12 @@ if(WITH_NODEJS) if("${DIRECTORY}/package.json" IS_NEWER_THAN "${DIRECTORY}/node_modules/.${NAME}.stamp") message(STATUS "Running 'npm install' for ${NAME}...") execute_process( - COMMAND "${NodeJS_EXECUTABLE}" "${npm_EXECUTABLE}" install --ignore-scripts + COMMAND "${NodeJS_EXECUTABLE}" "${npm_EXECUTABLE}" install --verbose --ignore-scripts WORKING_DIRECTORY "${DIRECTORY}" RESULT_VARIABLE NPM_INSTALL_FAILED OUTPUT_VARIABLE NPM_OUTPUT ERROR_VARIABLE NPM_OUTPUT) + message(STATUS "Finished 'npm install' for ${NAME}...") if(NOT NPM_INSTALL_FAILED) execute_process(COMMAND ${CMAKE_COMMAND} -E touch "${DIRECTORY}/node_modules/.${NAME}.stamp") else() @@ -64,9 +65,14 @@ if(WITH_NODEJS) endfunction() # Run submodule update + set(MBGL_SUBMODULES mapbox-gl-js) + if (MBGL_PLATFORM STREQUAL "ios") + list(APPEND MBGL_SUBMODULES platform/ios/vendor/mapbox-events-ios) + endif() + message(STATUS "Updating submodules...") execute_process( - COMMAND git submodule update --init mapbox-gl-js + COMMAND git submodule update --init ${MBGL_SUBMODULES} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") if(NOT EXISTS "${CMAKE_SOURCE_DIR}/mapbox-gl-js/node_modules") @@ -80,7 +86,7 @@ if(WITH_NODEJS) # Add target for running submodule update during builds add_custom_target( update-submodules ALL - COMMAND git submodule update --init mapbox-gl-js + COMMAND git submodule update --init ${MBGL_SUBMODULES} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" COMMENT "Updating submodules..." ) @@ -113,22 +119,42 @@ function(create_source_groups target) endforeach() endfunction() +function(load_sources_list VAR FILELIST) + set(_FILES) + file(STRINGS "${FILELIST}" _LINES) + foreach(_LINE IN LISTS _LINES) + string(STRIP "${_LINE}" _LINE) + string(REGEX MATCH "^([^;#]+)" _FILE "${_LINE}") + if (_FILE) + list(APPEND _FILES "${_FILE}") + endif() + endforeach() + set(${VAR} "${_FILES}" PARENT_SCOPE) +endfunction() + +function(target_sources_from_file TARGET TYPE FILELIST) + load_sources_list(_FILELIST "${FILELIST}") + target_sources(${TARGET} ${TYPE} "${_FILELIST}") +endfunction() + # Creates a library target for a vendored dependency function(add_vendor_target NAME TYPE) - add_library(${NAME} ${TYPE} cmake/empty.cpp) set(INCLUDE_TYPE "INTERFACE") set(SOURCE_TYPE "INTERFACE") if (TYPE STREQUAL "STATIC" OR TYPE STREQUAL "SHARED") + add_library(${NAME} ${TYPE} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/empty.cpp") set(INCLUDE_TYPE "PUBLIC") set(SOURCE_TYPE "PRIVATE") set_target_properties(${NAME} PROPERTIES SOURCES "") + else() + add_library(${NAME} ${TYPE}) endif() set_target_properties(${NAME} PROPERTIES INTERFACE_SOURCES "") - file(STRINGS vendor/${NAME}/files.txt FILES) + file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/vendor/${NAME}/files.txt" FILES) foreach(FILE IN LISTS FILES) - target_sources(${NAME} ${SOURCE_TYPE} "${CMAKE_SOURCE_DIR}/vendor/${NAME}/${FILE}") + target_sources(${NAME} ${SOURCE_TYPE} "${CMAKE_CURRENT_SOURCE_DIR}/vendor/${NAME}/${FILE}") endforeach() - target_include_directories(${NAME} ${INCLUDE_TYPE} vendor/${NAME}/include) + target_include_directories(${NAME} ${INCLUDE_TYPE} "${CMAKE_CURRENT_SOURCE_DIR}/vendor/${NAME}/include") create_source_groups(${NAME}) endfunction() @@ -137,48 +163,25 @@ macro(set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE) set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE}) endmacro (set_xcode_property) -function(_get_xcconfig_property target var) - get_property(result TARGET ${target} PROPERTY INTERFACE_${var} SET) - if (result) - get_property(result TARGET ${target} PROPERTY INTERFACE_${var}) - if (var STREQUAL "LINK_LIBRARIES") - # Remove target names from the list of linker flags, since Xcode can't deal with them. - set(link_flags) - foreach(item IN LISTS result) - if (NOT TARGET ${item}) - list(APPEND link_flags ${item}) - endif() - endforeach() - set(result "${link_flags}") +function(set_xcconfig_target_properties target) + # Create a list of linked libraries for use in the xcconfig generation script. + get_property(result TARGET ${target} PROPERTY INTERFACE_LINK_LIBRARIES) + string(GENEX_STRIP "${result}" result) + # Remove target names from the list of linker flags, since Xcode can't deal with them. + set(link_flags) + foreach(item IN LISTS result) + if (NOT TARGET ${item}) + list(APPEND link_flags ${item}) endif() - string(REPLACE ";-framework " ";-framework;" result "${result}") - string(REPLACE ";" "\" \"" result "${result}") - string(REPLACE "-" "_" target "${target}") - set(${target}_${var} "${result}" PARENT_SCOPE) - endif() -endfunction() - -if(MBGL_PLATFORM STREQUAL "ios") - execute_process( - COMMAND git submodule update --init platform/ios/vendor/mapbox-events-ios - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") -endif() - -function(write_xcconfig_target_properties) - foreach(target ${ARGN}) - _get_xcconfig_property(${target} INCLUDE_DIRECTORIES) - _get_xcconfig_property(${target} LINK_LIBRARIES) endforeach() - configure_file( - "${CMAKE_SOURCE_DIR}/scripts/config.xcconfig.in" - "${CMAKE_BINARY_DIR}/config.xcconfig" - @ONLY - ) + string(REPLACE ";-framework " ";-framework;" link_flags "${link_flags}") + string(REPLACE ";" "\" \"" link_flags "${link_flags}") + set_xcode_property(${target} XCCONFIG_LINK_LIBRARIES "${link_flags}") endfunction() # Set Xcode project build settings to be consistent with the CXX flags we're # using. (Otherwise, Xcode's defaults may override some of these.) -macro(initialize_xcode_cxx_build_settings target) +function(initialize_xcode_cxx_build_settings target) # -Wall set_xcode_property(${target} GCC_WARN_SIGN_COMPARE YES) set_xcode_property(${target} GCC_WARN_UNINITIALIZED_AUTOS YES) @@ -206,7 +209,10 @@ macro(initialize_xcode_cxx_build_settings target) # -flto set_xcode_property(${target} LLVM_LTO $<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebugInfo>>:YES>) -endmacro(initialize_xcode_cxx_build_settings) + + # Make releases debuggable. + set_xcode_property(${target} GCC_GENERATE_DEBUGGING_SYMBOLS YES) +endfunction() # CMake 3.1 does not have this yet. set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14") diff --git a/cmake/node.cmake b/cmake/node.cmake index 103d99b6a5..6750adc03c 100644 --- a/cmake/node.cmake +++ b/cmake/node.cmake @@ -101,6 +101,7 @@ xcode_create_scheme( TYPE node NAME "node render tests (Active ABI)" ARGS + "-r esm" "platform/node/test/render.test.js" OPTIONAL_ARGS "group" @@ -112,6 +113,7 @@ xcode_create_scheme( TYPE node NAME "node query tests (Active ABI)" ARGS + "-r esm" "platform/node/test/query.test.js" OPTIONAL_ARGS "group" @@ -123,6 +125,7 @@ xcode_create_scheme( TYPE node NAME "node expression tests (Active ABI)" ARGS + "-r esm" "platform/node/test/expression.test.js" OPTIONAL_ARGS "group" @@ -134,6 +137,7 @@ xcode_create_scheme( TYPE node NAME "node-benchmark (Active ABI)" ARGS + "-r esm" "platform/node/test/benchmark.js" OPTIONAL_ARGS "group" diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake deleted file mode 100644 index 1961d78dfc..0000000000 --- a/cmake/test-files.cmake +++ /dev/null @@ -1,153 +0,0 @@ -# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js - -set(MBGL_TEST_FILES - # actor - test/actor/actor.test.cpp - test/actor/actor_ref.test.cpp - - # algorithm - test/algorithm/covered_by_children.test.cpp - test/algorithm/generate_clip_ids.test.cpp - test/algorithm/mock.hpp - test/algorithm/update_renderables.test.cpp - test/algorithm/update_tile_masks.test.cpp - - # api - test/api/annotations.test.cpp - test/api/api_misuse.test.cpp - test/api/custom_geometry_source.test.cpp - test/api/custom_layer.test.cpp - test/api/query.test.cpp - test/api/recycle_map.cpp - - # geometry - test/geometry/dem_data.test.cpp - test/geometry/line_atlas.test.cpp - - # gl - test/gl/bucket.test.cpp - test/gl/context.test.cpp - test/gl/object.test.cpp - - # map - test/map/map.test.cpp - test/map/prefetch.test.cpp - test/map/transform.test.cpp - - # math - test/math/clamp.test.cpp - test/math/minmax.test.cpp - test/math/wrap.test.cpp - - # programs - test/programs/binary_program.test.cpp - test/programs/symbol_program.test.cpp - - # renderer - test/renderer/backend_scope.test.cpp - test/renderer/group_by_layout.test.cpp - test/renderer/image_manager.test.cpp - - # sprite - test/sprite/sprite_loader.test.cpp - test/sprite/sprite_parser.test.cpp - - # storage - test/storage/asset_file_source.test.cpp - test/storage/default_file_source.test.cpp - test/storage/headers.test.cpp - test/storage/http_file_source.test.cpp - test/storage/local_file_source.test.cpp - test/storage/offline.test.cpp - test/storage/offline_database.test.cpp - test/storage/offline_download.test.cpp - test/storage/online_file_source.test.cpp - test/storage/resource.test.cpp - test/storage/sqlite.test.cpp - - # style - test/style/filter.test.cpp - test/style/properties.test.cpp - test/style/property_expression.test.cpp - test/style/source.test.cpp - test/style/style.test.cpp - test/style/style_image.test.cpp - test/style/style_layer.test.cpp - test/style/style_parser.test.cpp - - # style/conversion - test/style/conversion/function.test.cpp - test/style/conversion/geojson_options.test.cpp - test/style/conversion/layer.test.cpp - test/style/conversion/light.test.cpp - test/style/conversion/property_value.test.cpp - test/style/conversion/stringify.test.cpp - test/style/conversion/tileset.test.cpp - - # style/expression - test/style/expression/expression.test.cpp - test/style/expression/util.test.cpp - - # test - test/include/mbgl/test.hpp - test/src/mbgl/test/fake_file_source.hpp - test/src/mbgl/test/fixture_log_observer.cpp - test/src/mbgl/test/fixture_log_observer.hpp - test/src/mbgl/test/getrss.cpp - test/src/mbgl/test/getrss.hpp - test/src/mbgl/test/stub_file_source.cpp - test/src/mbgl/test/stub_file_source.hpp - test/src/mbgl/test/stub_geometry_tile_feature.hpp - test/src/mbgl/test/stub_layer_observer.hpp - test/src/mbgl/test/stub_map_observer.hpp - test/src/mbgl/test/stub_render_source_observer.hpp - test/src/mbgl/test/stub_style_observer.hpp - test/src/mbgl/test/stub_tile_observer.hpp - test/src/mbgl/test/test.cpp - test/src/mbgl/test/util.cpp - test/src/mbgl/test/util.hpp - - # text - test/text/cross_tile_symbol_index.test.cpp - test/text/glyph_manager.test.cpp - test/text/glyph_pbf.test.cpp - test/text/language_tag.test.cpp - test/text/local_glyph_rasterizer.test.cpp - test/text/quads.test.cpp - - # tile - test/tile/custom_geometry_tile.test.cpp - test/tile/geojson_tile.test.cpp - test/tile/geometry_tile_data.test.cpp - test/tile/raster_dem_tile.test.cpp - test/tile/raster_tile.test.cpp - test/tile/tile_coordinate.test.cpp - test/tile/tile_id.test.cpp - test/tile/vector_tile.test.cpp - - # util - test/util/async_task.test.cpp - test/util/dtoa.test.cpp - test/util/geo.test.cpp - test/util/grid_index.test.cpp - test/util/http_timeout.test.cpp - test/util/image.test.cpp - test/util/mapbox.test.cpp - test/util/memory.test.cpp - test/util/merge_lines.test.cpp - test/util/number_conversions.test.cpp - test/util/offscreen_texture.test.cpp - test/util/position.test.cpp - test/util/projection.test.cpp - test/util/run_loop.test.cpp - test/util/text_conversions.test.cpp - test/util/thread.test.cpp - test/util/thread_local.test.cpp - test/util/tile_cover.test.cpp - test/util/tile_range.test.cpp - test/util/timer.test.cpp - test/util/token.test.cpp - test/util/unique_any.test.cpp - test/util/url.test.cpp - -) diff --git a/cmake/test-files.txt b/cmake/test-files.txt new file mode 100644 index 0000000000..77a69bb450 --- /dev/null +++ b/cmake/test-files.txt @@ -0,0 +1,153 @@ +# This file is generated. Do not edit. Regenerate this with scripts/generate-cmake-files.js + +# actor +test/actor/actor.test.cpp +test/actor/actor_ref.test.cpp + +# algorithm +test/algorithm/covered_by_children.test.cpp +test/algorithm/generate_clip_ids.test.cpp +test/algorithm/mock.hpp +test/algorithm/update_renderables.test.cpp +test/algorithm/update_tile_masks.test.cpp + +# api +test/api/annotations.test.cpp +test/api/api_misuse.test.cpp +test/api/custom_geometry_source.test.cpp +test/api/custom_layer.test.cpp +test/api/query.test.cpp +test/api/recycle_map.cpp + +# geometry +test/geometry/dem_data.test.cpp +test/geometry/line_atlas.test.cpp + +# gl +test/gl/bucket.test.cpp +test/gl/context.test.cpp +test/gl/object.test.cpp + +# map +test/map/map.test.cpp +test/map/prefetch.test.cpp +test/map/transform.test.cpp + +# math +test/math/clamp.test.cpp +test/math/minmax.test.cpp +test/math/wrap.test.cpp + +# programs +test/programs/binary_program.test.cpp +test/programs/symbol_program.test.cpp + +# renderer +test/renderer/backend_scope.test.cpp +test/renderer/group_by_layout.test.cpp +test/renderer/image_manager.test.cpp + +# sprite +test/sprite/sprite_loader.test.cpp +test/sprite/sprite_parser.test.cpp + +# storage +test/storage/asset_file_source.test.cpp +test/storage/default_file_source.test.cpp +test/storage/headers.test.cpp +test/storage/http_file_source.test.cpp +test/storage/local_file_source.test.cpp +test/storage/offline.test.cpp +test/storage/offline_database.test.cpp +test/storage/offline_download.test.cpp +test/storage/online_file_source.test.cpp +test/storage/resource.test.cpp +test/storage/sqlite.test.cpp + +# style +test/style/filter.test.cpp +test/style/properties.test.cpp +test/style/property_expression.test.cpp +test/style/source.test.cpp +test/style/style.test.cpp +test/style/style_image.test.cpp +test/style/style_layer.test.cpp +test/style/style_parser.test.cpp + +# style/conversion +test/style/conversion/function.test.cpp +test/style/conversion/geojson_options.test.cpp +test/style/conversion/layer.test.cpp +test/style/conversion/light.test.cpp +test/style/conversion/property_value.test.cpp +test/style/conversion/stringify.test.cpp +test/style/conversion/tileset.test.cpp + +# style/expression +test/style/expression/expression.test.cpp +test/style/expression/util.test.cpp + +# test +test/include/mbgl/test.hpp +test/src/mbgl/test/fake_file_source.hpp +test/src/mbgl/test/fixture_log_observer.cpp +test/src/mbgl/test/fixture_log_observer.hpp +test/src/mbgl/test/getrss.cpp +test/src/mbgl/test/getrss.hpp +test/src/mbgl/test/sqlite3_test_fs.cpp +test/src/mbgl/test/sqlite3_test_fs.hpp +test/src/mbgl/test/stub_file_source.cpp +test/src/mbgl/test/stub_file_source.hpp +test/src/mbgl/test/stub_geometry_tile_feature.hpp +test/src/mbgl/test/stub_layer_observer.hpp +test/src/mbgl/test/stub_map_observer.hpp +test/src/mbgl/test/stub_render_source_observer.hpp +test/src/mbgl/test/stub_style_observer.hpp +test/src/mbgl/test/stub_tile_observer.hpp +test/src/mbgl/test/test.cpp +test/src/mbgl/test/util.cpp +test/src/mbgl/test/util.hpp + +# text +test/text/cross_tile_symbol_index.test.cpp +test/text/glyph_manager.test.cpp +test/text/glyph_pbf.test.cpp +test/text/language_tag.test.cpp +test/text/local_glyph_rasterizer.test.cpp +test/text/quads.test.cpp + +# tile +test/tile/custom_geometry_tile.test.cpp +test/tile/geojson_tile.test.cpp +test/tile/geometry_tile_data.test.cpp +test/tile/raster_dem_tile.test.cpp +test/tile/raster_tile.test.cpp +test/tile/tile_coordinate.test.cpp +test/tile/tile_id.test.cpp +test/tile/vector_tile.test.cpp + +# util +test/util/async_task.test.cpp +test/util/dtoa.test.cpp +test/util/geo.test.cpp +test/util/grid_index.test.cpp +test/util/http_timeout.test.cpp +test/util/image.test.cpp +test/util/mapbox.test.cpp +test/util/memory.test.cpp +test/util/merge_lines.test.cpp +test/util/number_conversions.test.cpp +test/util/offscreen_texture.test.cpp +test/util/peer.test.cpp +test/util/position.test.cpp +test/util/projection.test.cpp +test/util/run_loop.test.cpp +test/util/text_conversions.test.cpp +test/util/thread.test.cpp +test/util/thread_local.test.cpp +test/util/tile_cover.test.cpp +test/util/tile_range.test.cpp +test/util/timer.test.cpp +test/util/token.test.cpp +test/util/url.test.cpp + diff --git a/cmake/test.cmake b/cmake/test.cmake index f4309896b8..755edad892 100644 --- a/cmake/test.cmake +++ b/cmake/test.cmake @@ -1,13 +1,10 @@ add_vendor_target(gtest STATIC) +load_sources_list(MBGL_TEST_FILES cmake/test-files.txt) if (MBGL_TEST_TARGET_TYPE STREQUAL "library") - add_library(mbgl-test SHARED - ${MBGL_TEST_FILES} - ) + add_library(mbgl-test SHARED ${MBGL_TEST_FILES}) else() - add_executable(mbgl-test - ${MBGL_TEST_FILES} - ) + add_executable(mbgl-test ${MBGL_TEST_FILES}) endif() diff --git a/include/mbgl/math/log2.hpp b/include/mbgl/math/log2.hpp index 6a1ba23ed9..3136ac22b4 100644 --- a/include/mbgl/math/log2.hpp +++ b/include/mbgl/math/log2.hpp @@ -2,6 +2,11 @@ #include <cmath> #include <cstdint> +#include <type_traits> + +#if defined(__ANDROID__) +#include <android/api-level.h> +#endif namespace mbgl { namespace util { @@ -12,3 +17,14 @@ uint32_t ceil_log2(uint64_t x); } // namespace util } // namespace mbgl + +// log2 is not available on Android before API 18. +#if defined(__ANDROID__) && defined(__GNUC__) && \ + defined(__ANDROID_API__) && __ANDROID_API__ < 18 + +template <typename T> +typename std::enable_if_t<std::is_floating_point<T>::value, T> log2(T x) { + return ::log(x) / M_LN2; +} + +#endif diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index b9c8de5052..e048d82af2 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -5,6 +5,7 @@ #include <mbgl/storage/offline.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/expected.hpp> #include <vector> #include <mutex> @@ -55,8 +56,7 @@ public: * callback, which will be executed on the database thread; it is the responsibility * of the SDK bindings to re-execute a user-provided callback on the main thread. */ - void listOfflineRegions(std::function<void (std::exception_ptr, - optional<std::vector<OfflineRegion>>)>); + void listOfflineRegions(std::function<void (expected<OfflineRegions, std::exception_ptr>)>); /* * Create an offline region in the database. @@ -71,16 +71,14 @@ public: */ void createOfflineRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata, - std::function<void (std::exception_ptr, - optional<OfflineRegion>)>); + std::function<void (expected<OfflineRegion, std::exception_ptr>)>); /* * Update an offline region metadata in the database. */ void updateOfflineMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata, - std::function<void (std::exception_ptr, - optional<OfflineRegionMetadata>)>); + std::function<void (expected<OfflineRegionMetadata, std::exception_ptr>)>); /* * Register an observer to be notified when the state of the region changes. */ @@ -97,8 +95,9 @@ public: * executed on the database thread; it is the responsibility of the SDK bindings * to re-execute a user-provided callback on the main thread. */ - void getOfflineRegionStatus(OfflineRegion&, std::function<void (std::exception_ptr, - optional<OfflineRegionStatus>)>) const; + void getOfflineRegionStatus( + OfflineRegion&, + std::function<void (expected<OfflineRegionStatus, std::exception_ptr>)>) const; /* * Remove an offline region from the database and perform any resources evictions diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp index ef4a499e83..b4e40cb5f3 100644 --- a/include/mbgl/storage/offline.hpp +++ b/include/mbgl/storage/offline.hpp @@ -1,8 +1,10 @@ #pragma once #include <mbgl/util/geo.hpp> +#include <mbgl/util/geometry.hpp> #include <mbgl/util/range.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/variant.hpp> #include <mbgl/style/types.hpp> #include <mbgl/storage/response.hpp> @@ -30,22 +32,40 @@ public: OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float); /* Private */ - std::vector<CanonicalTileID> tileCover(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; - uint64_t tileCount(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; const std::string styleURL; const LatLngBounds bounds; const double minZoom; const double maxZoom; const float pixelRatio; -private: - Range<uint8_t> coveringZoomRange(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const; }; /* - * For the present, a tile pyramid is the only type of offline region. In the future, - * other definition types will be available and this will be a variant type. + * An offline region defined by a style URL, geometry, zoom range, and + * device pixel ratio. + * + * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom. + * + * maxZoom may be ∞, in which case for each tile source, the region will include + * tiles from minZoom up to the maximum zoom level provided by that source. + * + * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0. */ -using OfflineRegionDefinition = OfflineTilePyramidRegionDefinition; +class OfflineGeometryRegionDefinition { +public: + OfflineGeometryRegionDefinition(std::string styleURL, Geometry<double>, double minZoom, double maxZoom, float pixelRatio); + + /* Private */ + const std::string styleURL; + const Geometry<double> geometry; + const double minZoom; + const double maxZoom; + const float pixelRatio; +}; + +/* + * The offline region definition types supported + */ +using OfflineRegionDefinition = variant<OfflineTilePyramidRegionDefinition, OfflineGeometryRegionDefinition>; /* * The encoded format is private. @@ -187,11 +207,11 @@ class OfflineRegion { public: // Move-only; not publicly constructible. OfflineRegion(OfflineRegion&&); - OfflineRegion& operator=(OfflineRegion&&); ~OfflineRegion(); OfflineRegion() = delete; OfflineRegion(const OfflineRegion&) = delete; + OfflineRegion& operator=(OfflineRegion&&) = delete; OfflineRegion& operator=(const OfflineRegion&) = delete; int64_t getID() const; @@ -210,4 +230,6 @@ private: const OfflineRegionMetadata metadata; }; +using OfflineRegions = std::vector<OfflineRegion>; + } // namespace mbgl diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp index 71c2cec237..2c83d1561b 100644 --- a/include/mbgl/style/conversion.hpp +++ b/include/mbgl/style/conversion.hpp @@ -1,306 +1,26 @@ #pragma once -#include <mbgl/util/optional.hpp> -#include <mbgl/util/feature.hpp> -#include <mbgl/util/geojson.hpp> - #include <string> namespace mbgl { namespace style { namespace conversion { -/* - The `conversion` namespace defines conversions from JSON structures 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> - optional<T> convert(const Convertible& input, Error& error); - - Where `T` is one of the above types. If the conversion fails, the result is empty, and the - error parameter includes diagnostic text suitable for presentation to a library user. Otherwise, - a filled optional is returned. - - `Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that - can serve as input to the conversion algorithm. For instance, on macOS, we need to support - conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc. - On Qt, we need to support conversion from RapidJSON types and QVariant. - - We don't want to use traditional forms of polymorphism to accomplish this: - - * Compile time polymorphism using a template parameter for the actual value type leads to - excessive code bloat and long compile times. - * Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous - use of std::unique_ptr, unsuitable for this performance-sensitive code. - - Therefore, we're using a custom implementation of runtime polymorphism where we manually create and - dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible - underlying types inline on the stack, using `std::aligned_storage`. - - For a given underlying type T, an explicit specialization of `ConversionTraits<T>` must be provided. This - specialization must provide the following static methods: - - * `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 - * `toDouble(v)` -- returns `optional<double>`, absence indicating `v` is not a JSON number - * `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string - * `toValue(v)` -- returns `optional<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. - - In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for - `std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the - possible underlying types. (A static assert will fail if this is not the case.) - - `Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state; - you must not do anything with it except let it go out of scope. -*/ +// This is a forward-declaration only header intended to minimize dependencies and to improve +// compilation speed. In order to specialize implementations and get access to the actual +// implementation, include <mbgl/style/conversion_impl.hpp>. struct Error { std::string message; }; template <typename T> class ConversionTraits; -class Convertible { -public: - template <typename T> - Convertible(T&& value) : vtable(vtableForType<std::decay_t<T>>()) { - static_assert(sizeof(Storage) >= sizeof(std::decay_t<T>), "Storage must be large enough to hold value type"); - new (static_cast<void*>(&storage)) std::decay_t<T>(std::forward<T>(value)); - } - - Convertible(Convertible&& v) - : vtable(v.vtable) - { - if (vtable) { - vtable->move(std::move(v.storage), this->storage); - } - } - - ~Convertible() { - if (vtable) { - vtable->destroy(storage); - } - } - - Convertible& operator=(Convertible&& v) { - if (vtable) { - vtable->destroy(storage); - } - vtable = v.vtable; - if (vtable) { - vtable->move(std::move(v.storage), this->storage); - } - v.vtable = nullptr; - return *this; - } - - Convertible() = delete; - Convertible(const Convertible&) = delete; - Convertible& operator=(const Convertible&) = delete; - - friend inline bool isUndefined(const Convertible& v) { - assert(v.vtable); - return v.vtable->isUndefined(v.storage); - } - - friend inline bool isArray(const Convertible& v) { - assert(v.vtable); - return v.vtable->isArray(v.storage); - } - - friend inline std::size_t arrayLength(const Convertible& v) { - assert(v.vtable); - return v.vtable->arrayLength(v.storage); - } - - friend inline Convertible arrayMember(const Convertible& v, std::size_t i) { - assert(v.vtable); - return v.vtable->arrayMember(v.storage, i); - } - - friend inline bool isObject(const Convertible& v) { - assert(v.vtable); - return v.vtable->isObject(v.storage); - } - - friend inline optional<Convertible> objectMember(const Convertible& v, const char * name) { - assert(v.vtable); - return v.vtable->objectMember(v.storage, name); - } - - friend inline optional<Error> eachMember(const Convertible& v, const std::function<optional<Error> (const std::string&, const Convertible&)>& fn) { - assert(v.vtable); - return v.vtable->eachMember(v.storage, fn); - } - - friend inline optional<bool> toBool(const Convertible& v) { - assert(v.vtable); - return v.vtable->toBool(v.storage); - } - - friend inline optional<float> toNumber(const Convertible& v) { - assert(v.vtable); - return v.vtable->toNumber(v.storage); - } - - friend inline optional<double> toDouble(const Convertible& v) { - assert(v.vtable); - return v.vtable->toDouble(v.storage); - } - - friend inline optional<std::string> toString(const Convertible& v) { - assert(v.vtable); - return v.vtable->toString(v.storage); - } - - friend inline optional<Value> toValue(const Convertible& v) { - assert(v.vtable); - return v.vtable->toValue(v.storage); - } - - friend inline optional<GeoJSON> toGeoJSON(const Convertible& v, Error& error) { - assert(v.vtable); - return v.vtable->toGeoJSON(v.storage, error); - } - -private: -#if __ANDROID__ - // Android: JSValue* or mbgl::android::Value - using Storage = std::aligned_storage_t<32, 8>; -#elif __QT__ - // Qt: JSValue* or QVariant - using Storage = std::aligned_storage_t<32, 8>; -#else - // Node: JSValue* or v8::Local<v8::Value> - // iOS/macOS: JSValue* or id - using Storage = std::aligned_storage_t<8, 8>; -#endif - - struct VTable { - void (*move) (Storage&& src, Storage& dest); - void (*destroy) (Storage&); - - bool (*isUndefined) (const Storage&); - - bool (*isArray) (const Storage&); - std::size_t (*arrayLength) (const Storage&); - Convertible (*arrayMember) (const Storage&, std::size_t); - - bool (*isObject) (const Storage&); - optional<Convertible> (*objectMember) (const Storage&, const char *); - optional<Error> (*eachMember) (const Storage&, const std::function<optional<Error> (const std::string&, const Convertible&)>&); - - optional<bool> (*toBool) (const Storage&); - optional<float> (*toNumber) (const Storage&); - optional<double> (*toDouble) (const Storage&); - optional<std::string> (*toString) (const Storage&); - optional<Value> (*toValue) (const Storage&); - - // https://github.com/mapbox/mapbox-gl-native/issues/5623 - optional<GeoJSON> (*toGeoJSON) (const Storage&, Error&); - }; - - // Extracted this function from the table below to work around a GCC bug with differing - // visibility settings for capturing lambdas: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947 - template <typename T> - static auto vtableEachMember(const Storage& s, const std::function<optional<Error>(const std::string&, const Convertible&)>& fn) { - return ConversionTraits<T>::eachMember(reinterpret_cast<const T&>(s), [&](const std::string& k, T&& v) { - return fn(k, Convertible(std::move(v))); - }); - } - - template <typename T> - static VTable* vtableForType() { - using Traits = ConversionTraits<T>; - static VTable vtable = { - [] (Storage&& src, Storage& dest) { - auto srcValue = reinterpret_cast<T&&>(src); - new (static_cast<void*>(&dest)) T(std::move(srcValue)); - srcValue.~T(); - }, - [] (Storage& s) { - reinterpret_cast<T&>(s).~T(); - }, - [] (const Storage& s) { - return Traits::isUndefined(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::isArray(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::arrayLength(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s, std::size_t i) { - return Convertible(Traits::arrayMember(reinterpret_cast<const T&>(s), i)); - }, - [] (const Storage& s) { - return Traits::isObject(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s, const char * key) { - optional<T> member = Traits::objectMember(reinterpret_cast<const T&>(s), key); - if (member) { - return optional<Convertible>(Convertible(std::move(*member))); - } else { - return optional<Convertible>(); - } - }, - vtableEachMember<T>, - [] (const Storage& s) { - return Traits::toBool(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::toNumber(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::toDouble(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::toString(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s) { - return Traits::toValue(reinterpret_cast<const T&>(s)); - }, - [] (const Storage& s, Error& err) { - return Traits::toGeoJSON(reinterpret_cast<const T&>(s), err); - } - }; - return &vtable; - } - - VTable* vtable; - Storage storage; -}; +class Convertible; template <class T, class Enable = void> struct Converter; -template <class T, class...Args> -optional<T> convert(const Convertible& value, Error& error, Args&&...args) { - return Converter<T>()(value, error, 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 index 7d74ec42ce..40657528c4 100644 --- a/include/mbgl/style/conversion/constant.hpp +++ b/include/mbgl/style/conversion/constant.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/style/conversion.hpp> +#include <mbgl/style/types.hpp> #include <mbgl/util/color.hpp> #include <mbgl/util/enum.hpp> #include <mbgl/util/string.hpp> @@ -30,21 +31,7 @@ struct Converter<std::string> { template <class T> struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> { - optional<T> operator()(const Convertible& value, Error& error) const { - optional<std::string> string = toString(value); - if (!string) { - error.message = "value must be a string"; - return nullopt; - } - - const auto result = Enum<T>::toEnum(*string); - if (!result) { - error.message = "value must be a valid enumeration value"; - return nullopt; - } - - return *result; - } + optional<T> operator()(const Convertible& value, Error& error) const; }; template <> @@ -54,23 +41,7 @@ struct Converter<Color> { template <size_t N> struct Converter<std::array<float, N>> { - optional<std::array<float, N>> operator()(const Convertible& value, Error& error) const { - if (!isArray(value) || arrayLength(value) != N) { - error.message = "value must be an array of " + util::toString(N) + " numbers"; - return nullopt; - } - - std::array<float, N> result; - for (size_t i = 0; i < N; i++) { - optional<float> n = toNumber(arrayMember(value, i)); - if (!n) { - error.message = "value must be an array of " + util::toString(N) + " numbers"; - return nullopt; - } - result[i] = *n; - } - return result; - } + optional<std::array<float, N>> operator()(const Convertible& value, Error& error) const; }; template <> diff --git a/include/mbgl/style/conversion/coordinate.hpp b/include/mbgl/style/conversion/coordinate.hpp index e11db5e32f..1346ed738a 100644 --- a/include/mbgl/style/conversion/coordinate.hpp +++ b/include/mbgl/style/conversion/coordinate.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/conversion.hpp> #include <mbgl/util/geo.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/custom_geometry_source_options.hpp b/include/mbgl/style/conversion/custom_geometry_source_options.hpp index f0f505e54f..090e5b6239 100644 --- a/include/mbgl/style/conversion/custom_geometry_source_options.hpp +++ b/include/mbgl/style/conversion/custom_geometry_source_options.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/conversion.hpp> #include <mbgl/style/sources/custom_geometry_source.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp index 9daf6ea7a4..2d7ad0afc7 100644 --- a/include/mbgl/style/conversion/filter.hpp +++ b/include/mbgl/style/conversion/filter.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/filter.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp index 49825a3410..ba9acd7a3b 100644 --- a/include/mbgl/style/conversion/function.hpp +++ b/include/mbgl/style/conversion/function.hpp @@ -1,8 +1,8 @@ #pragma once #include <mbgl/style/property_expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/expression.hpp> #include <mbgl/style/expression/value.hpp> @@ -16,25 +16,7 @@ std::unique_ptr<expression::Expression> convertTokenStringToExpression(const std optional<std::unique_ptr<expression::Expression>> convertFunctionToExpression(expression::type::Type, const Convertible&, Error&, bool convertTokens); template <class T> -optional<PropertyExpression<T>> convertFunctionToExpression(const Convertible& value, Error& error, bool convertTokens) { - auto expression = convertFunctionToExpression(expression::valueTypeToExpressionType<T>(), value, error, convertTokens); - if (!expression) { - return nullopt; - } - - optional<T> defaultValue; - - auto defaultValueValue = objectMember(value, "default"); - if (defaultValueValue) { - defaultValue = convert<T>(*defaultValueValue, error); - if (!defaultValue) { - error.message = R"(wrong type for "default": )" + error.message; - return nullopt; - } - } - - return PropertyExpression<T>(std::move(*expression), defaultValue); -} +optional<PropertyExpression<T>> convertFunctionToExpression(const Convertible& value, Error& error, bool convertTokens); } // namespace conversion } // namespace style diff --git a/include/mbgl/style/conversion/geojson.hpp b/include/mbgl/style/conversion/geojson.hpp index 403c5f953b..90c1d64197 100644 --- a/include/mbgl/style/conversion/geojson.hpp +++ b/include/mbgl/style/conversion/geojson.hpp @@ -1,7 +1,8 @@ #pragma once -#include <mbgl/style/conversion.hpp> #include <mbgl/util/geojson.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp index 3f625babb6..89511848dd 100644 --- a/include/mbgl/style/conversion/geojson_options.hpp +++ b/include/mbgl/style/conversion/geojson_options.hpp @@ -1,7 +1,8 @@ #pragma once -#include <mbgl/style/conversion.hpp> #include <mbgl/style/sources/geojson_source.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp index 1c0e2e2f07..9cf019378b 100644 --- a/include/mbgl/style/conversion/layer.hpp +++ b/include/mbgl/style/conversion/layer.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> #include <memory> @@ -15,8 +16,6 @@ public: optional<std::unique_ptr<Layer>> operator()(const Convertible& value, Error& error) const; }; -optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value); -optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value); optional<Error> setPaintProperties(Layer& layer, const Convertible& value); } // namespace conversion diff --git a/include/mbgl/style/conversion/light.hpp b/include/mbgl/style/conversion/light.hpp index 289fca2e31..2f6f8628b8 100644 --- a/include/mbgl/style/conversion/light.hpp +++ b/include/mbgl/style/conversion/light.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/light.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp index 044c45862d..10db5c6ec1 100644 --- a/include/mbgl/style/conversion/position.hpp +++ b/include/mbgl/style/conversion/position.hpp @@ -1,7 +1,8 @@ #pragma once -#include <mbgl/style/conversion.hpp> #include <mbgl/style/position.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index fa6752867b..f6f36db983 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -1,9 +1,9 @@ #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> +#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/value.hpp> #include <mbgl/style/expression/is_constant.hpp> #include <mbgl/style/expression/is_expression.hpp> @@ -16,53 +16,7 @@ namespace conversion { template <class T> struct Converter<PropertyValue<T>> { - optional<PropertyValue<T>> operator()(const Convertible& value, Error& error, bool allowDataExpressions, bool convertTokens) const { - using namespace mbgl::style::expression; - - if (isUndefined(value)) { - return PropertyValue<T>(); - } - - optional<PropertyExpression<T>> expression; - - if (isExpression(value)) { - ParsingContext ctx(valueTypeToExpressionType<T>()); - ParseResult parsed = ctx.parseLayerPropertyExpression(value); - if (!parsed) { - error.message = ctx.getCombinedErrors(); - return nullopt; - } - expression = PropertyExpression<T>(std::move(*parsed)); - } else if (isObject(value)) { - expression = convertFunctionToExpression<T>(value, error, convertTokens); - } else { - optional<T> constant = convert<T>(value, error); - if (!constant) { - return nullopt; - } - return convertTokens ? maybeConvertTokens(*constant) : PropertyValue<T>(*constant); - } - - if (!expression) { - return nullopt; - } else if (!allowDataExpressions && !(*expression).isFeatureConstant()) { - error.message = "data expressions not supported"; - return nullopt; - } else if (!(*expression).isFeatureConstant() || !(*expression).isZoomConstant()) { - return { std::move(*expression) }; - } else if ((*expression).getExpression().getKind() == Kind::Literal) { - optional<T> constant = fromExpressionValue<T>( - static_cast<const Literal&>((*expression).getExpression()).getValue()); - if (!constant) { - return nullopt; - } - return PropertyValue<T>(*constant); - } else { - assert(false); - error.message = "expected a literal expression"; - return nullopt; - } - } + optional<PropertyValue<T>> operator()(const Convertible& value, Error& error, bool allowDataExpressions, bool convertTokens) const; template <class S> PropertyValue<T> maybeConvertTokens(const S& t) const { diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp index 2cf2e36da4..19bc1ce6b6 100644 --- a/include/mbgl/style/conversion/source.hpp +++ b/include/mbgl/style/conversion/source.hpp @@ -1,7 +1,8 @@ #pragma once -#include <mbgl/style/conversion.hpp> #include <mbgl/style/source.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> #include <memory> diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp index 1fb4acf70d..88661c358c 100644 --- a/include/mbgl/style/conversion/tileset.hpp +++ b/include/mbgl/style/conversion/tileset.hpp @@ -2,6 +2,7 @@ #include <mbgl/util/tileset.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion/transition_options.hpp b/include/mbgl/style/conversion/transition_options.hpp index 0563f39ac3..a72d757d3c 100644 --- a/include/mbgl/style/conversion/transition_options.hpp +++ b/include/mbgl/style/conversion/transition_options.hpp @@ -2,6 +2,7 @@ #include <mbgl/style/transition_options.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/conversion_impl.hpp b/include/mbgl/style/conversion_impl.hpp new file mode 100644 index 0000000000..27b2ee1917 --- /dev/null +++ b/include/mbgl/style/conversion_impl.hpp @@ -0,0 +1,302 @@ +#pragma once + +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/feature.hpp> +#include <mbgl/util/geojson.hpp> + +#include <string> + +namespace mbgl { +namespace style { +namespace conversion { + +/* + The `conversion` namespace defines conversions from JSON structures 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> + optional<T> convert(const Convertible& input, Error& error); + + Where `T` is one of the above types. If the conversion fails, the result is empty, and the + error parameter includes diagnostic text suitable for presentation to a library user. Otherwise, + a filled optional is returned. + + `Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that + can serve as input to the conversion algorithm. For instance, on macOS, we need to support + conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc. + On Qt, we need to support conversion from RapidJSON types and QVariant. + + We don't want to use traditional forms of polymorphism to accomplish this: + + * Compile time polymorphism using a template parameter for the actual value type leads to + excessive code bloat and long compile times. + * Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous + use of std::unique_ptr, unsuitable for this performance-sensitive code. + + Therefore, we're using a custom implementation of runtime polymorphism where we manually create and + dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible + underlying types inline on the stack, using `std::aligned_storage`. + + For a given underlying type T, an explicit specialization of `ConversionTraits<T>` must be provided. This + specialization must provide the following static methods: + + * `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 + * `toDouble(v)` -- returns `optional<double>`, absence indicating `v` is not a JSON number + * `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string + * `toValue(v)` -- returns `optional<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. + + In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for + `std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the + possible underlying types. (A static assert will fail if this is not the case.) + + `Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state; + you must not do anything with it except let it go out of scope. +*/ + +template <typename T> +class ConversionTraits; + +class Convertible { +public: + template <typename T> + Convertible(T&& value) : vtable(vtableForType<std::decay_t<T>>()) { + static_assert(sizeof(Storage) >= sizeof(std::decay_t<T>), "Storage must be large enough to hold value type"); + new (static_cast<void*>(&storage)) std::decay_t<T>(std::forward<T>(value)); + } + + Convertible(Convertible&& v) + : vtable(v.vtable) + { + if (vtable) { + vtable->move(std::move(v.storage), this->storage); + } + } + + ~Convertible() { + if (vtable) { + vtable->destroy(storage); + } + } + + Convertible& operator=(Convertible&& v) { + if (vtable) { + vtable->destroy(storage); + } + vtable = v.vtable; + if (vtable) { + vtable->move(std::move(v.storage), this->storage); + } + v.vtable = nullptr; + return *this; + } + + Convertible() = delete; + Convertible(const Convertible&) = delete; + Convertible& operator=(const Convertible&) = delete; + + friend inline bool isUndefined(const Convertible& v) { + assert(v.vtable); + return v.vtable->isUndefined(v.storage); + } + + friend inline bool isArray(const Convertible& v) { + assert(v.vtable); + return v.vtable->isArray(v.storage); + } + + friend inline std::size_t arrayLength(const Convertible& v) { + assert(v.vtable); + return v.vtable->arrayLength(v.storage); + } + + friend inline Convertible arrayMember(const Convertible& v, std::size_t i) { + assert(v.vtable); + return v.vtable->arrayMember(v.storage, i); + } + + friend inline bool isObject(const Convertible& v) { + assert(v.vtable); + return v.vtable->isObject(v.storage); + } + + friend inline optional<Convertible> objectMember(const Convertible& v, const char * name) { + assert(v.vtable); + return v.vtable->objectMember(v.storage, name); + } + + friend inline optional<Error> eachMember(const Convertible& v, const std::function<optional<Error> (const std::string&, const Convertible&)>& fn) { + assert(v.vtable); + return v.vtable->eachMember(v.storage, fn); + } + + friend inline optional<bool> toBool(const Convertible& v) { + assert(v.vtable); + return v.vtable->toBool(v.storage); + } + + friend inline optional<float> toNumber(const Convertible& v) { + assert(v.vtable); + return v.vtable->toNumber(v.storage); + } + + friend inline optional<double> toDouble(const Convertible& v) { + assert(v.vtable); + return v.vtable->toDouble(v.storage); + } + + friend inline optional<std::string> toString(const Convertible& v) { + assert(v.vtable); + return v.vtable->toString(v.storage); + } + + friend inline optional<Value> toValue(const Convertible& v) { + assert(v.vtable); + return v.vtable->toValue(v.storage); + } + + friend inline optional<GeoJSON> toGeoJSON(const Convertible& v, Error& error) { + assert(v.vtable); + return v.vtable->toGeoJSON(v.storage, error); + } + +private: +#if __ANDROID__ + // Android: JSValue* or mbgl::android::Value + using Storage = std::aligned_storage_t<32, 8>; +#elif __QT__ + // Qt: JSValue* or QVariant + using Storage = std::aligned_storage_t<32, 8>; +#else + // Node: JSValue* or v8::Local<v8::Value> + // iOS/macOS: JSValue* or id + using Storage = std::aligned_storage_t<8, 8>; +#endif + + struct VTable { + void (*move) (Storage&& src, Storage& dest); + void (*destroy) (Storage&); + + bool (*isUndefined) (const Storage&); + + bool (*isArray) (const Storage&); + std::size_t (*arrayLength) (const Storage&); + Convertible (*arrayMember) (const Storage&, std::size_t); + + bool (*isObject) (const Storage&); + optional<Convertible> (*objectMember) (const Storage&, const char *); + optional<Error> (*eachMember) (const Storage&, const std::function<optional<Error> (const std::string&, const Convertible&)>&); + + optional<bool> (*toBool) (const Storage&); + optional<float> (*toNumber) (const Storage&); + optional<double> (*toDouble) (const Storage&); + optional<std::string> (*toString) (const Storage&); + optional<Value> (*toValue) (const Storage&); + + // https://github.com/mapbox/mapbox-gl-native/issues/5623 + optional<GeoJSON> (*toGeoJSON) (const Storage&, Error&); + }; + + // Extracted this function from the table below to work around a GCC bug with differing + // visibility settings for capturing lambdas: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947 + template <typename T> + static auto vtableEachMember(const Storage& s, const std::function<optional<Error>(const std::string&, const Convertible&)>& fn) { + return ConversionTraits<T>::eachMember(reinterpret_cast<const T&>(s), [&](const std::string& k, T&& v) { + return fn(k, Convertible(std::move(v))); + }); + } + + template <typename T> + static VTable* vtableForType() { + using Traits = ConversionTraits<T>; + static VTable vtable = { + [] (Storage&& src, Storage& dest) { + auto srcValue = reinterpret_cast<T&&>(src); + new (static_cast<void*>(&dest)) T(std::move(srcValue)); + srcValue.~T(); + }, + [] (Storage& s) { + reinterpret_cast<T&>(s).~T(); + }, + [] (const Storage& s) { + return Traits::isUndefined(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::isArray(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::arrayLength(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s, std::size_t i) { + return Convertible(Traits::arrayMember(reinterpret_cast<const T&>(s), i)); + }, + [] (const Storage& s) { + return Traits::isObject(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s, const char * key) { + optional<T> member = Traits::objectMember(reinterpret_cast<const T&>(s), key); + if (member) { + return optional<Convertible>(Convertible(std::move(*member))); + } else { + return optional<Convertible>(); + } + }, + vtableEachMember<T>, + [] (const Storage& s) { + return Traits::toBool(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toNumber(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toDouble(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toString(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s) { + return Traits::toValue(reinterpret_cast<const T&>(s)); + }, + [] (const Storage& s, Error& err) { + return Traits::toGeoJSON(reinterpret_cast<const T&>(s), err); + } + }; + return &vtable; + } + + VTable* vtable; + Storage storage; +}; + +template <class T, class...Args> +optional<T> convert(const Convertible& value, Error& error, Args&&...args) { + return Converter<T>()(value, error, std::forward<Args>(args)...); +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/assertion.hpp b/include/mbgl/style/expression/assertion.hpp index 90da16b068..239cdf2ea6 100644 --- a/include/mbgl/style/expression/assertion.hpp +++ b/include/mbgl/style/expression/assertion.hpp @@ -1,8 +1,8 @@ #pragma once #include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> #include <memory> #include <vector> diff --git a/include/mbgl/style/expression/case.hpp b/include/mbgl/style/expression/case.hpp index 02dc3bc4c2..7cd007d3c7 100644 --- a/include/mbgl/style/expression/case.hpp +++ b/include/mbgl/style/expression/case.hpp @@ -1,8 +1,8 @@ #pragma once #include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> #include <memory> #include <vector> diff --git a/include/mbgl/style/expression/coalesce.hpp b/include/mbgl/style/expression/coalesce.hpp index cd60cee02e..c4216f234f 100644 --- a/include/mbgl/style/expression/coalesce.hpp +++ b/include/mbgl/style/expression/coalesce.hpp @@ -1,8 +1,8 @@ #pragma once #include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> #include <memory> #include <map> diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp index ef10dadb55..b54720a258 100644 --- a/include/mbgl/style/expression/compound_expression.hpp +++ b/include/mbgl/style/expression/compound_expression.hpp @@ -1,10 +1,10 @@ #pragma once #include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/expression/parsing_context.hpp> #include <mbgl/style/expression/type.hpp> #include <mbgl/style/expression/value.hpp> +#include <mbgl/style/conversion.hpp> #include <mbgl/util/optional.hpp> #include <memory> diff --git a/include/mbgl/style/expression/parsing_context.hpp b/include/mbgl/style/expression/parsing_context.hpp index c19974a4f7..66014e33d4 100644 --- a/include/mbgl/style/expression/parsing_context.hpp +++ b/include/mbgl/style/expression/parsing_context.hpp @@ -7,6 +7,7 @@ #include <iterator> #include <map> +#include <unordered_map> #include <string> #include <vector> #include <memory> diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp index 12494f5387..3b7969ea79 100644 --- a/include/mbgl/style/layer.hpp +++ b/include/mbgl/style/layer.hpp @@ -1,10 +1,12 @@ #pragma once #include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/unique_any.hpp> +#include <mbgl/util/peer.hpp> #include <mbgl/util/immutable.hpp> +#include <mbgl/util/optional.hpp> #include <mbgl/style/layer_type.hpp> #include <mbgl/style/types.hpp> +#include <mbgl/style/conversion.hpp> #include <cassert> #include <memory> @@ -98,7 +100,6 @@ public: return std::forward<V>(visitor)(*as<HeatmapLayer>()); } - // Not reachable, but placate GCC. assert(false); throw new std::runtime_error("unknown layer type"); @@ -117,6 +118,11 @@ public: virtual void setMinZoom(float) = 0; virtual void setMaxZoom(float) = 0; + // Dynamic properties + virtual optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) = 0; + virtual optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) = 0; + optional<conversion::Error> setVisibility(const conversion::Convertible& value); + // Private implementation class Impl; Immutable<Impl> baseImpl; @@ -132,7 +138,7 @@ public: // For use in SDK bindings, which store a reference to a platform-native peer // object here, so that separately-obtained references to this object share // identical platform-native peers. - util::unique_any peer; + util::peer peer; }; } // namespace style diff --git a/include/mbgl/style/layers/background_layer.hpp b/include/mbgl/style/layers/background_layer.hpp index eab2681fec..76230df12c 100644 --- a/include/mbgl/style/layers/background_layer.hpp +++ b/include/mbgl/style/layers/background_layer.hpp @@ -25,6 +25,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<Color> getDefaultBackgroundColor(); diff --git a/include/mbgl/style/layers/circle_layer.hpp b/include/mbgl/style/layers/circle_layer.hpp index 89ef926221..cde691c893 100644 --- a/include/mbgl/style/layers/circle_layer.hpp +++ b/include/mbgl/style/layers/circle_layer.hpp @@ -33,6 +33,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultCircleRadius(); diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp index fbe3a4a6c2..4b4c770489 100644 --- a/include/mbgl/style/layers/custom_layer.hpp +++ b/include/mbgl/style/layers/custom_layer.hpp @@ -75,6 +75,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Private implementation class Impl; diff --git a/include/mbgl/style/layers/fill_extrusion_layer.hpp b/include/mbgl/style/layers/fill_extrusion_layer.hpp index 742bac8c7e..e72fcade61 100644 --- a/include/mbgl/style/layers/fill_extrusion_layer.hpp +++ b/include/mbgl/style/layers/fill_extrusion_layer.hpp @@ -33,6 +33,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultFillExtrusionOpacity(); diff --git a/include/mbgl/style/layers/fill_layer.hpp b/include/mbgl/style/layers/fill_layer.hpp index d0b2a25bfe..430d7a011f 100644 --- a/include/mbgl/style/layers/fill_layer.hpp +++ b/include/mbgl/style/layers/fill_layer.hpp @@ -33,6 +33,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<bool> getDefaultFillAntialias(); diff --git a/include/mbgl/style/layers/heatmap_layer.hpp b/include/mbgl/style/layers/heatmap_layer.hpp index 53fd24aa6c..fd0051f44c 100644 --- a/include/mbgl/style/layers/heatmap_layer.hpp +++ b/include/mbgl/style/layers/heatmap_layer.hpp @@ -34,6 +34,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultHeatmapRadius(); diff --git a/include/mbgl/style/layers/hillshade_layer.hpp b/include/mbgl/style/layers/hillshade_layer.hpp index 214576b120..89d0ae686f 100644 --- a/include/mbgl/style/layers/hillshade_layer.hpp +++ b/include/mbgl/style/layers/hillshade_layer.hpp @@ -28,6 +28,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultHillshadeIlluminationDirection(); diff --git a/include/mbgl/style/layers/layer.hpp.ejs b/include/mbgl/style/layers/layer.hpp.ejs index 9d52973af4..db7052387c 100644 --- a/include/mbgl/style/layers/layer.hpp.ejs +++ b/include/mbgl/style/layers/layer.hpp.ejs @@ -53,6 +53,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + <% if (layoutProperties.length) { -%> // Layout properties diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp index 26e3b81fc9..fe4cd7c0d1 100644 --- a/include/mbgl/style/layers/line_layer.hpp +++ b/include/mbgl/style/layers/line_layer.hpp @@ -35,6 +35,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Layout properties static PropertyValue<LineCapType> getDefaultLineCap(); diff --git a/include/mbgl/style/layers/raster_layer.hpp b/include/mbgl/style/layers/raster_layer.hpp index c133c23484..fcc35412a0 100644 --- a/include/mbgl/style/layers/raster_layer.hpp +++ b/include/mbgl/style/layers/raster_layer.hpp @@ -28,6 +28,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Paint properties static PropertyValue<float> getDefaultRasterOpacity(); diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 8c0b45d796..fa0b0c4e4e 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -35,6 +35,10 @@ public: void setMinZoom(float) final; void setMaxZoom(float) final; + // Dynamic properties + optional<conversion::Error> setLayoutProperty(const std::string& name, const conversion::Convertible& value) final; + optional<conversion::Error> setPaintProperty(const std::string& name, const conversion::Convertible& value) final; + // Layout properties static PropertyValue<SymbolPlacementType> getDefaultSymbolPlacement(); diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp index 2f2838ade8..dc3a8d73fb 100644 --- a/include/mbgl/style/source.hpp +++ b/include/mbgl/style/source.hpp @@ -2,7 +2,7 @@ #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/optional.hpp> -#include <mbgl/util/unique_any.hpp> +#include <mbgl/util/peer.hpp> #include <mbgl/util/immutable.hpp> #include <mbgl/style/types.hpp> @@ -77,7 +77,7 @@ public: // For use in SDK bindings, which store a reference to a platform-native peer // object here, so that separately-obtained references to this object share // identical platform-native peers. - util::unique_any peer; + util::peer peer; }; } // namespace style diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index 372e7c7a78..a03b910279 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -18,6 +18,7 @@ struct GeoJSONOptions { uint16_t tileSize = util::tileSize; uint16_t buffer = 128; double tolerance = 0.375; + bool lineMetrics = false; // Supercluster options bool cluster = false; diff --git a/include/mbgl/util/expected.hpp b/include/mbgl/util/expected.hpp new file mode 100644 index 0000000000..a45f071065 --- /dev/null +++ b/include/mbgl/util/expected.hpp @@ -0,0 +1,16 @@ +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#include <expected.hpp> +#pragma GCC diagnostic pop + +namespace mbgl { + +template <class T, class E> +using expected = nonstd::expected<T, E>; + +template <class E> +using unexpected = nonstd::unexpected_type<E>; + +} // namespace mbgl diff --git a/include/mbgl/util/font_stack.hpp b/include/mbgl/util/font_stack.hpp index d0b431e9ea..ace60a4ba6 100644 --- a/include/mbgl/util/font_stack.hpp +++ b/include/mbgl/util/font_stack.hpp @@ -1,7 +1,11 @@ #pragma once +#include <mbgl/util/immutable.hpp> +#include <mbgl/style/layer.hpp> + #include <string> #include <vector> +#include <set> namespace mbgl { @@ -14,4 +18,7 @@ struct FontStackHash { std::size_t operator()(const FontStack&) const; }; +// Statically evaluate layer properties to determine what font stacks are used. +std::set<FontStack> fontStacks(const std::vector<Immutable<style::Layer::Impl>>&); + } // namespace mbgl diff --git a/include/mbgl/util/peer.hpp b/include/mbgl/util/peer.hpp new file mode 100644 index 0000000000..a4abea0e88 --- /dev/null +++ b/include/mbgl/util/peer.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include <type_traits> +#include <utility> + +namespace mbgl { +namespace util { + +class peer { +public: + peer() = default; + peer(const peer&) = delete; + + peer(peer&& other) + : vtable(other.vtable) + { + if (vtable) { + vtable->move(other.storage, storage); + } + other.vtable = nullptr; + } + + template <class T> + peer(T&& value) { + using _Vt = std::decay_t<T>; + vtable = get_vtable<_Vt>(); + new (&storage) _Vt(std::forward<T>(value)); + } + + ~peer() { + reset(); + } + + peer& operator=(peer&& rhs) { + peer(std::move(rhs)).swap(*this); + return *this; + } + + void reset() { + if (vtable) { + vtable->destroy(storage); + vtable = nullptr; + } + } + + void swap(peer& rhs) { + if (this == &rhs) { + return; + } else { + peer tmp(std::move(rhs)); + rhs.vtable = vtable; + if (rhs.vtable) { + rhs.vtable->move(storage, rhs.storage); + } + vtable = tmp.vtable; + if (vtable) { + vtable->move(tmp.storage, storage); + } + } + } + + bool has_value() const { + return vtable != nullptr; + } + + template <class T> + T& get() { + return reinterpret_cast<T&>(storage); + } + + template <class T> + T&& take() { + reset(); + return std::move(get<T>()); + } + +private: + using storage_t = std::aligned_storage_t<2*sizeof(void*), alignof(void*)>; + + struct vtable { + virtual ~vtable() = default; + virtual void move(storage_t&, storage_t&) = 0; + virtual void destroy(storage_t&) = 0; + }; + + template <class T> + struct vtable_impl : public vtable { + static_assert(sizeof(T) <= sizeof(storage_t), "peer object is too big"); + + void move(storage_t& src, storage_t& dst) override { + new (&dst) T(std::move(reinterpret_cast<T&>(src))); + destroy(src); + } + + void destroy(storage_t& s) override { + reinterpret_cast<T&>(s).~T(); + } + }; + + template <class T> + static vtable* get_vtable() { + static vtable_impl<T> vtable; + return &vtable; + } + + vtable* vtable = nullptr; + storage_t storage; +}; + +} // namespace util +} // namespace mbgl diff --git a/include/mbgl/util/unique_any.hpp b/include/mbgl/util/unique_any.hpp deleted file mode 100644 index c7dc8b38ea..0000000000 --- a/include/mbgl/util/unique_any.hpp +++ /dev/null @@ -1,271 +0,0 @@ -#pragma once - -#include <typeinfo> -#include <type_traits> -#include <stdexcept> -namespace mbgl { -namespace util { - -class bad_any_cast : public std::bad_cast { -public: - const char* what() const noexcept override { - return "bad any_cast<>()"; - } -}; -/** - * A variant of `std::any` for non-copyable types. - * - * Use `unique_any` for non-copyable types (e.g. `std::unique_ptr<T>`) - * or to ensure that no copies are made of copyable types that are - * moved in. - * - * `uniqe_any` differs from `std::any` in that it does not support copy construction - * or copy assignment. It also does not require the contained type to be copy - * constructible. - * - * The `any_cast<T>()` methods work similar to `std::any_cast<T>()` except that - * non-copyable types may only be cast to references. - * - * Example usage: - * unique_any u1(3); - * auto u2 = unique_any(std::move(u1)); // u1 is moved from - * int i = any_cast<int>(u2); - * - * unique_any u2; - * u2 = std::unique_ptr<int>(new int); - * std::unique_ptr<int> iPtr = any_cast<std::unique_ptr<int>>(std::move(u2)); - * - * Inspired by linb::any (https://github.com/thelink2012/any) and the - * libc++ implementation (https://github.com/llvm-mirror/libcxx). - */ -class unique_any final -{ -public: - unique_any() = default; - - //Copy constructor (deleted) - unique_any(const unique_any& rhs) = delete; - - unique_any(unique_any&& rhs) : vtable(rhs.vtable) { - if (vtable) { - vtable->move(std::move(rhs.storage), storage); - } - rhs.vtable = nullptr; - } - - // Constructs with a direct-initilizated object of type ValueType - template <typename ValueType, - typename _Vt = std::decay_t<ValueType>, - typename = std::enable_if_t<!std::is_same<_Vt, unique_any>::value> > - unique_any(ValueType&& value) { - create(std::forward<ValueType>(value)); - } - - ~unique_any() { - reset(); - } - - unique_any& operator=(unique_any&& rhs) { - unique_any(std::move(rhs)).swap(*this); - return *this; - } - - template <class ValueType, - typename = std::enable_if_t<!std::is_same<std::decay_t<ValueType>, unique_any>::value> > - unique_any& operator=(ValueType&& rhs) { - unique_any(std::forward<ValueType>(rhs)).swap(*this); - return *this; - } - - void reset() { - if (vtable) { - vtable->destroy(storage); - vtable = nullptr; - } - } - - void swap(unique_any& rhs) { - if (this == &rhs) { - return; - } else { - unique_any tmp(std::move(rhs)); - rhs.vtable = vtable; - if (rhs.vtable) { - rhs.vtable->move(std::move(storage), rhs.storage); - } - vtable = tmp.vtable; - if (vtable) { - vtable->move(std::move(tmp.storage), storage); - } - } - } - - const std::type_info& type() const { - return !has_value()? typeid(void) : vtable->type(); - } - - bool has_value() const { - return vtable != nullptr; - } - -private: - - union Storage { - using StackStorage = std::aligned_storage_t<3*sizeof(void*), std::alignment_of<void*>::value>; - Storage() = default; - - void * dynamic { nullptr }; - StackStorage stack; - }; - - template<typename T> - struct AllocateOnStack : std::integral_constant<bool, - sizeof(T) <= sizeof(Storage::stack) - && std::alignment_of<T>::value <= std::alignment_of<Storage::StackStorage>::value - && std::is_nothrow_move_constructible<T>::value> { - }; - - struct VTable { - virtual ~VTable() = default; - virtual void move(Storage&& src, Storage& dest) = 0; - virtual void destroy(Storage&) = 0; - virtual const std::type_info& type() = 0; - }; - - template <typename ValueType> - struct VTableHeap : public VTable { - void move(Storage&& src, Storage& dest) override { - dest.dynamic = src.dynamic; - src.dynamic = nullptr; - } - - void destroy(Storage& s) override { - delete reinterpret_cast<ValueType*>(s.dynamic); - } - - const std::type_info& type() override { - return typeid(ValueType); - } - }; - - template <typename ValueType> - struct VTableStack : public VTable { - void move(Storage&& src, Storage& dest) override { - new (&dest.stack) ValueType(std::move(reinterpret_cast<ValueType&>(src.stack))); - destroy(src); - } - - void destroy(Storage& s) override { - reinterpret_cast<ValueType&>(s.stack).~ValueType(); - } - - const std::type_info& type() override { - return typeid(ValueType); - } - }; - - template <typename ValueType> - static VTable* vtableForType() { - using VTableType = std::conditional_t<AllocateOnStack<ValueType>::value, VTableStack<ValueType>, VTableHeap<ValueType> >; - static VTableType vtable; - return &vtable; - } - - template <typename ValueType, typename _Vt> - std::enable_if_t<AllocateOnStack<_Vt>::value> - createStorage(ValueType&& value) { - new (&storage.stack) _Vt(std::forward<ValueType>(value)); - } - - template <typename ValueType, typename _Vt> - std::enable_if_t<!AllocateOnStack<_Vt>::value> - createStorage(ValueType&& value) { - storage.dynamic = new _Vt(std::forward<ValueType>(value)); - } - - template <typename ValueType> - void create(ValueType&& value) { - using _Vt = std::decay_t<ValueType>; - vtable = vtableForType<_Vt>(); - createStorage<ValueType, _Vt>(std::forward<ValueType>(value)); - } - - VTable* vtable { nullptr }; - Storage storage; - -protected: - template<class ValueType> - friend const ValueType* any_cast(const unique_any* operand) ; - - template<class ValueType> - friend ValueType* any_cast(unique_any* operand) ; - - template<typename ValueType, typename _Vt = std::decay_t<ValueType> > - ValueType* cast() - { - return reinterpret_cast<ValueType *>( - AllocateOnStack<_Vt>::value ? &storage.stack : storage.dynamic); - } -}; - -template<typename ValueType> -inline const ValueType* any_cast(const unique_any* any) -{ - return any_cast<ValueType>(const_cast<unique_any *>(any)); -} - -template<typename ValueType> -inline ValueType* any_cast(unique_any* any) -{ - if(any == nullptr || any->type() != typeid(ValueType)) - return nullptr; - else - return any->cast<ValueType>(); -} - -template<typename ValueType, typename _Vt = std::decay_t<ValueType> > -inline ValueType any_cast(const unique_any& any) -{ - static_assert(std::is_constructible<ValueType, const _Vt&>::value, - "any_cast type can't construct copy of contained object"); - auto temp = any_cast<_Vt>(&any); - if (temp == nullptr) { - throw bad_any_cast(); - } - return static_cast<ValueType>(*temp); -} - -template<typename ValueType, typename _Vt = std::decay_t<ValueType> > -inline ValueType any_cast(unique_any& any) -{ - static_assert(std::is_constructible<ValueType, const _Vt&>::value, - "any_cast type can't construct copy of contained object"); - auto temp = any_cast<_Vt>(&any); - if (temp == nullptr) { - throw bad_any_cast(); - } - return static_cast<ValueType>(*temp); -} - -template<typename ValueType, typename _Vt = std::remove_cv_t<ValueType> > -inline ValueType any_cast(unique_any&& any) -{ - auto temp = any_cast<_Vt>(&any); - if (temp == nullptr) { - throw bad_any_cast(); - } - auto retValue = static_cast<ValueType>(std::move(*temp)); - any.reset(); - return std::move(retValue); -} - -} // namespace util -} // namespace mbgl - -namespace std { - -inline void swap(mbgl::util::unique_any& lhs, mbgl::util::unique_any& rhs) { - lhs.swap(rhs); -} - -} // namespace std diff --git a/mapbox-gl-js b/mapbox-gl-js -Subproject 47e637c3984a5121589bd17b51c6605f223aaea +Subproject 754ec0bf51c3798d096aa38c3f009dcb58d3be7 diff --git a/package.json b/package.json index ef23f4aa99..799d3ab85f 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "aws-sdk": "^2.285.1", "csscolorparser": "^1.0.2", "ejs": "^2.4.1", + "esm": "^3.0.55", "express": "^4.11.1", "flow-remove-types": "^1.2.1", "json-stringify-pretty-compact": "^1.0.4", @@ -43,9 +44,9 @@ "test": "tape platform/node/test/js/**/*.test.js", "test-memory": "node --expose-gc platform/node/test/memory.test.js", "test-suite": "run-s test-render test-query test-expressions", - "test-expressions": "node platform/node/test/expression.test.js", - "test-render": "node platform/node/test/render.test.js", - "test-query": "node platform/node/test/query.test.js" + "test-expressions": "node -r esm platform/node/test/expression.test.js", + "test-render": "node -r esm platform/node/test/render.test.js", + "test-query": "node -r esm platform/node/test/query.test.js" }, "gypfile": true, "binary": { diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 5db2fb213c..ccae27f51d 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,21 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## master +- Don't default-show text/icons that depend on the placement of a paired icon/text [#12483](https://github.com/mapbox/mapbox-gl-native/issues/12483) +- Fix symbol querying for markers near tile boundaries at high zoom. ([#12472](https://github.com/mapbox/mapbox-gl-native/issues/12472)) + +## 6.4.0 - August 15, 2018 + - Use unconverted bearing value for LatLngBounds calculation [#12616](https://github.com/mapbox/mapbox-gl-native/pull/12616) + - Store release so files with debugging information [#12628](https://github.com/mapbox/mapbox-gl-native/pull/12628) + - Close a security vulnerability introduced in v6.2.0 that would potentially allow the owner of a style to compromise apps loading that style. + +## 6.4.0-beta.1 - August 9, 2018 + - Don't prefetch tiles for geojson sources [#12529](https://github.com/mapbox/mapbox-gl-native/pull/12529) + - Enable LTO in release builds [#12546](https://github.com/mapbox/mapbox-gl-native/pull/12546) + - Update Java Services to v3.4.0 [#12564](https://github.com/mapbox/mapbox-gl-native/pull/12564) + - Telemetry bump to 3.1.5 [#12589](https://github.com/mapbox/mapbox-gl-native/pull/12589) + ## 6.4.0-alpha.2 - August 1, 2018 - Compress shader source code [#12477](https://github.com/mapbox/mapbox-gl-native/pull/12477) - Add minimal touch target to marker click detection [#12482](https://github.com/mapbox/mapbox-gl-native/pull/12482) @@ -38,7 +53,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Add LatLngForScreenCoordinate to MapSnapshotter API, This allows to convert a LatLng value to the x,y position on the MapSnasphot image [#12221](https://github.com/mapbox/mapbox-gl-native/pull/12221) - Expose multiple getCameraFor equivalent methods to convert a geometry or a bounds to a camera position with taking in account padding, tilt and bearing [#12290](https://github.com/mapbox/mapbox-gl-native/pull/12290) - Avoid race condition when calling getMapAsync from a non-UI thread when running instrumentation tests [#12308](https://github.com/mapbox/mapbox-gl-native/pull/12308) - + ## 6.2.1 - June 27, 2018 - Backport range alpha values from 0 to 1 with int color conversion [#12235](https://github.com/mapbox/mapbox-gl-native/pull/12235) diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 0529489783..597eddc334 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -10,6 +10,7 @@ dependencies { api (dependenciesList.mapboxAndroidGestures) { exclude group: 'com.android.support', module: 'appcompat-v7' } + implementation dependenciesList.mapboxJavaTurf implementation dependenciesList.supportAnnotations implementation dependenciesList.supportFragmentV4 implementation dependenciesList.timber @@ -30,7 +31,7 @@ android { buildConfigField "String", "MAPBOX_SDK_IDENTIFIER", String.format("\"%s\"", "mapbox-maps-android") buildConfigField "String", "MAPBOX_SDK_VERSION", String.format("\"%s\"", project.VERSION_NAME) buildConfigField "String", "MAPBOX_VERSION_STRING", String.format("\"Mapbox/%s\"", project.VERSION_NAME) - buildConfigField "String", "MAPBOX_EVENTS_USER_AGENT", String.format("\"MapboxEventsAndroid/%s\"", project.VERSION_NAME) + buildConfigField "String", "MAPBOX_EVENTS_USER_AGENT", String.format("\"mapbox-maps-android/%s\"", project.VERSION_NAME) } defaultPublishConfig project.hasProperty("mapbox.buildtype") ? project.getProperty("mapbox.buildtype") : "debug" diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index 77a93829f9..e8abad77e1 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=6.4.0-SNAPSHOT +VERSION_NAME=6.5.0-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java index 6897cf66df..8c9cd362d3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java @@ -11,6 +11,9 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; import com.mapbox.mapboxsdk.maps.Telemetry; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; +import com.mapbox.mapboxsdk.storage.FileSource; +import com.mapbox.mapboxsdk.utils.ThreadUtils; + import timber.log.Timber; /** @@ -42,8 +45,10 @@ public final class Mapbox { */ @UiThread public static synchronized Mapbox getInstance(@NonNull Context context, @Nullable String accessToken) { + ThreadUtils.checkThread("Mapbox"); if (INSTANCE == null) { Context appContext = context.getApplicationContext(); + FileSource.initializeFileDirsPaths(appContext); INSTANCE = new Mapbox(appContext, accessToken); if (isAccessTokenValid(accessToken)) { initializeTelemetry(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java index 90bb23429f..3e75a34b4a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java @@ -1,10 +1,13 @@ package com.mapbox.mapboxsdk.attribution; +import android.content.Context; import android.text.Html; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.URLSpan; +import com.mapbox.mapboxsdk.R; +import java.lang.ref.WeakReference; import java.util.LinkedHashSet; import java.util.Set; @@ -17,6 +20,7 @@ import java.util.Set; */ public class AttributionParser { + private final WeakReference<Context> context; private final Set<Attribution> attributions = new LinkedHashSet<>(); private final String attributionData; private final boolean withImproveMap; @@ -24,8 +28,9 @@ public class AttributionParser { private final boolean withTelemetryAttribution; private final boolean withMapboxAttribution; - AttributionParser(String attributionData, boolean withImproveMap, boolean withCopyrightSign, - boolean withTelemetryAttribution, boolean withMapboxAttribution) { + AttributionParser(WeakReference<Context> context, String attributionData, boolean withImproveMap, + boolean withCopyrightSign, boolean withTelemetryAttribution, boolean withMapboxAttribution) { + this.context = context; this.attributionData = attributionData; this.withImproveMap = withImproveMap; this.withCopyrightSign = withCopyrightSign; @@ -167,7 +172,13 @@ public class AttributionParser { */ private void addAdditionalAttributions() { if (withTelemetryAttribution) { - attributions.add(new Attribution(Attribution.TELEMETRY, Attribution.TELEMETRY_URL)); + Context context = this.context.get(); + attributions.add( + new Attribution( + context != null ? context.getString(R.string.mapbox_telemetrySettings) : Attribution.TELEMETRY, + Attribution.TELEMETRY_URL + ) + ); } } @@ -196,6 +207,7 @@ public class AttributionParser { * </p> */ public static class Options { + private WeakReference<Context> context; private boolean withImproveMap = true; private boolean withCopyrightSign = true; private boolean withTelemetryAttribution = false; @@ -227,6 +239,11 @@ public class AttributionParser { return this; } + public Options withContext(Context context) { + this.context = new WeakReference<>(context); + return this; + } + public AttributionParser build() { if (attributionDataStringArray == null) { throw new IllegalStateException("Using builder without providing attribution data"); @@ -234,6 +251,7 @@ public class AttributionParser { String fullAttributionString = parseAttribution(attributionDataStringArray); AttributionParser attributionParser = new AttributionParser( + context, fullAttributionString, withImproveMap, withCopyrightSign, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java index 546ee62804..c2114f458e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java @@ -17,10 +17,12 @@ import com.mapbox.mapboxsdk.attribution.AttributionParser; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.style.sources.Source; +import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.Set; +import java.util.Locale; /** * Responsible for managing attribution interactions on the map. @@ -47,7 +49,7 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt // Called when someone presses the attribution icon on the map @Override public void onClick(View view) { - attributionSet = new AttributionBuilder(mapboxMap).build(); + attributionSet = new AttributionBuilder(mapboxMap, view.getContext()).build(); boolean isActivityFinishing = false; if (context instanceof Activity) { @@ -148,12 +150,19 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt private static class AttributionBuilder { private final MapboxMap mapboxMap; + private final WeakReference<Context> context; - AttributionBuilder(MapboxMap mapboxMap) { + AttributionBuilder(MapboxMap mapboxMap, Context context) { this.mapboxMap = mapboxMap; + this.context = new WeakReference<>(context); } private Set<Attribution> build() { + Context context = this.context.get(); + if (context == null) { + return Collections.emptySet(); + } + List<String> attributions = new ArrayList<>(); for (Source source : mapboxMap.getSources()) { attributions.add(source.getAttribution()); @@ -162,6 +171,7 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt return new AttributionParser.Options() .withCopyrightSign(true) .withImproveMap(true) + .withContext(context) .withTelemetryAttribution(true) .withAttributionData(attributions.toArray(new String[attributions.size()])) .build().getAttributions(); 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 aaed71ddec..d480d28f26 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 @@ -37,11 +37,13 @@ import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer; import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; +import com.mapbox.mapboxsdk.offline.OfflineGeometryRegionDefinition; import com.mapbox.mapboxsdk.offline.OfflineRegionDefinition; import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition; import com.mapbox.mapboxsdk.storage.FileSource; @@ -567,22 +569,46 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { return; } - OfflineTilePyramidRegionDefinition regionDefinition = (OfflineTilePyramidRegionDefinition) definition; - setStyleUrl(regionDefinition.getStyleURL()); + if (definition instanceof OfflineTilePyramidRegionDefinition) { + setOfflineTilePyramidRegionDefinition((OfflineTilePyramidRegionDefinition) definition); + } else if (definition instanceof OfflineGeometryRegionDefinition) { + setOfflineGeometryRegionDefinition((OfflineGeometryRegionDefinition) definition); + } else { + throw new UnsupportedOperationException("OfflineRegionDefintion instance not supported"); + } + } + + private void setOfflineRegionDefinition(String styleUrl, LatLng cameraTarget, double minZoom, double maxZoom) { CameraPosition cameraPosition = new CameraPosition.Builder() - .target(regionDefinition.getBounds().getCenter()) - .zoom(regionDefinition.getMinZoom()) + .target(cameraTarget) + .zoom(minZoom) .build(); - + setStyleUrl(styleUrl); if (!isMapInitialized()) { mapboxMapOptions.camera(cameraPosition); - mapboxMapOptions.minZoomPreference(regionDefinition.getMinZoom()); - mapboxMapOptions.maxZoomPreference(regionDefinition.getMaxZoom()); + mapboxMapOptions.minZoomPreference(minZoom); + mapboxMapOptions.maxZoomPreference(maxZoom); return; } mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); - mapboxMap.setMinZoomPreference(regionDefinition.getMinZoom()); - mapboxMap.setMaxZoomPreference(regionDefinition.getMaxZoom()); + mapboxMap.setMinZoomPreference(minZoom); + mapboxMap.setMaxZoomPreference(maxZoom); + } + + private void setOfflineTilePyramidRegionDefinition(OfflineTilePyramidRegionDefinition regionDefinition) { + setOfflineRegionDefinition(regionDefinition.getStyleURL(), + regionDefinition.getBounds().getCenter(), + regionDefinition.getMinZoom(), + regionDefinition.getMaxZoom() + ); + } + + private void setOfflineGeometryRegionDefinition(OfflineGeometryRegionDefinition regionDefinition) { + setOfflineRegionDefinition(regionDefinition.getStyleURL(), + regionDefinition.getBounds().getCenter(), + regionDefinition.getMinZoom(), + regionDefinition.getMaxZoom() + ); } // 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 0719412b4c..45e54a3d14 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 @@ -95,6 +95,7 @@ public final class MapboxMap { setDebugActive(options.getDebugActive()); setApiBaseUrl(options); setStyleUrl(options); + setStyleJson(options); setPrefetchesTiles(options); } @@ -433,7 +434,7 @@ public final class MapboxMap { } /** - * Removes the source, preserving the reverence for re-use + * Removes the source, preserving the reference for re-use * * @param source the source to remove * @return the source @@ -1058,7 +1059,7 @@ public final class MapboxMap { * @param options the object containing the style url */ private void setStyleUrl(@NonNull MapboxMapOptions options) { - String style = options.getStyle(); + String style = options.getStyleUrl(); if (!TextUtils.isEmpty(style)) { setStyleUrl(style, null); } @@ -1087,6 +1088,18 @@ public final class MapboxMap { } /** + * Loads a new map style json from MapboxMapOptions if available. + * + * @param options the object containing the style json + */ + private void setStyleJson(@NonNull MapboxMapOptions options) { + String styleJson = options.getStyleJson(); + if (!TextUtils.isEmpty(styleJson)) { + setStyleJson(styleJson); + } + } + + /** * Returns the map style json currently displayed in the map view. * * @return The json of the map style @@ -1601,7 +1614,7 @@ public final class MapboxMap { public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, @NonNull @Size(value = 4) int[] padding) { // we use current camera tilt/bearing value to provide expected transformations as #11993 - return getCameraForLatLngBounds(latLngBounds, padding, transform.getBearing(), transform.getTilt()); + return getCameraForLatLngBounds(latLngBounds, padding, transform.getRawBearing(), transform.getTilt()); } 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 0075199b1e..f48bd92327 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 @@ -77,7 +77,8 @@ public class MapboxMapOptions implements Parcelable { @ColorInt private int foregroundLoadColor; - private String style; + private String styleUrl; + private String styleJson; private float pixelRatio; @@ -120,7 +121,8 @@ public class MapboxMapOptions implements Parcelable { zoomGesturesEnabled = in.readByte() != 0; doubleTapGesturesEnabled = in.readByte() != 0; - style = in.readString(); + styleUrl = in.readString(); + styleJson = in.readString(); apiBaseUrl = in.readString(); textureMode = in.readByte() != 0; translucentTextureSurface = in.readByte() != 0; @@ -145,6 +147,7 @@ public class MapboxMapOptions implements Parcelable { try { mapboxMapOptions.camera(new CameraPosition.Builder(typedArray).build()); mapboxMapOptions.styleUrl(typedArray.getString(R.styleable.mapbox_MapView_mapbox_styleUrl)); + mapboxMapOptions.styleJson(typedArray.getString(R.styleable.mapbox_MapView_mapbox_styleJson)); mapboxMapOptions.apiBaseUrl(typedArray.getString(R.styleable.mapbox_MapView_mapbox_apiBaseUrl)); mapboxMapOptions.zoomGesturesEnabled( @@ -258,13 +261,24 @@ public class MapboxMapOptions implements Parcelable { } /** - * Specifies the style url associated with a map view. + * Specifies the styleUrl url associated with a map view. * - * @param styleUrl Url to be used to load a style + * @param styleUrl Url to be used to load a styleUrl * @return This */ public MapboxMapOptions styleUrl(String styleUrl) { - style = styleUrl; + this.styleUrl = styleUrl; + return this; + } + + /** + * Specifies the styleJson associated with a map view. + * + * @param styleJson json to used as style + * @return This + */ + public MapboxMapOptions styleJson(String styleJson) { + this.styleJson = styleJson; return this; } @@ -716,12 +730,21 @@ public class MapboxMapOptions implements Parcelable { } /** - * Get the current configured style url for a map view. + * Get the current configured styleUrl url for a map view. * * @return Style url to be used. */ - public String getStyle() { - return style; + public String getStyleUrl() { + return styleUrl; + } + + /** + * Get the current configured styleJson for a map view. + * + * @return Style json to be used. + */ + public String getStyleJson() { + return styleJson; } /** @@ -912,7 +935,8 @@ public class MapboxMapOptions implements Parcelable { dest.writeByte((byte) (zoomGesturesEnabled ? 1 : 0)); dest.writeByte((byte) (doubleTapGesturesEnabled ? 1 : 0)); - dest.writeString(style); + dest.writeString(styleUrl); + dest.writeString(styleJson); dest.writeString(apiBaseUrl); dest.writeByte((byte) (textureMode ? 1 : 0)); dest.writeByte((byte) (translucentTextureSurface ? 1 : 0)); @@ -1002,9 +1026,14 @@ public class MapboxMapOptions implements Parcelable { if (!Arrays.equals(attributionMargins, options.attributionMargins)) { return false; } - if (style != null ? !style.equals(options.style) : options.style != null) { + if (styleUrl != null ? !styleUrl.equals(options.styleUrl) : options.styleUrl != null) { return false; } + + if (styleJson != null ? !styleJson.equals(options.styleJson) : options.styleJson != null) { + return false; + } + if (apiBaseUrl != null ? !apiBaseUrl.equals(options.apiBaseUrl) : options.apiBaseUrl != null) { return false; } @@ -1055,7 +1084,8 @@ public class MapboxMapOptions implements Parcelable { result = 31 * result + (apiBaseUrl != null ? apiBaseUrl.hashCode() : 0); result = 31 * result + (textureMode ? 1 : 0); result = 31 * result + (translucentTextureSurface ? 1 : 0); - result = 31 * result + (style != null ? style.hashCode() : 0); + result = 31 * result + (styleUrl != null ? styleUrl.hashCode() : 0); + result = 31 * result + (styleJson != null ? styleJson.hashCode() : 0); result = 31 * result + (prefetchesTiles ? 1 : 0); result = 31 * result + (zMediaOverlay ? 1 : 0); result = 31 * result + (localIdeographFontFamily != null ? localIdeographFontFamily.hashCode() : 0); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java index 1129b8000e..492f653dae 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java @@ -28,7 +28,7 @@ public abstract class MapRenderer implements MapRendererScheduler { public MapRenderer(Context context, String localIdeographFontFamily) { FileSource fileSource = FileSource.getInstance(context); float pixelRatio = context.getResources().getDisplayMetrics().density; - String programCacheDir = context.getCacheDir().getAbsolutePath(); + String programCacheDir = FileSource.getInternalCachePath(context); // Initialise native peer nativeInitialize(this, fileSource, pixelRatio, programCacheDir, localIdeographFontFamily); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineGeometryRegionDefinition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineGeometryRegionDefinition.java new file mode 100644 index 0000000000..0db3ee3202 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineGeometryRegionDefinition.java @@ -0,0 +1,128 @@ +package com.mapbox.mapboxsdk.offline; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.Geometry; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.turf.TurfMeasurement; + +/** + * An offline region defined by a style URL, geometry, zoom range, and + * device pixel ratio. + * <p> + * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom. + * <p> + * maxZoom may be ∞, in which case for each tile source, the region will include + * tiles from minZoom up to the maximum zoom level provided by that source. + * <p> + * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0. + */ +public class OfflineGeometryRegionDefinition implements OfflineRegionDefinition, Parcelable { + + private String styleURL; + private Geometry geometry; + private double minZoom; + private double maxZoom; + private float pixelRatio; + + /** + * Constructor to create an OfflineGeometryRegionDefinition from parameters. + * + * @param styleURL the style + * @param geometry the geometry + * @param minZoom min zoom + * @param maxZoom max zoom + * @param pixelRatio pixel ratio of the device + */ + public OfflineGeometryRegionDefinition( + String styleURL, Geometry geometry, double minZoom, double maxZoom, float pixelRatio) { + // Note: Also used in JNI + this.styleURL = styleURL; + this.geometry = geometry; + this.minZoom = minZoom; + this.maxZoom = maxZoom; + this.pixelRatio = pixelRatio; + } + + /** + * Constructor to create an OfflineGeometryRegionDefinition from a Parcel. + * + * @param parcel the parcel to create the OfflineGeometryRegionDefinition from + */ + public OfflineGeometryRegionDefinition(Parcel parcel) { + this.styleURL = parcel.readString(); + this.geometry = Feature.fromJson(parcel.readString()).geometry(); + this.minZoom = parcel.readDouble(); + this.maxZoom = parcel.readDouble(); + this.pixelRatio = parcel.readFloat(); + } + + /* + * Getters + */ + + public String getStyleURL() { + return styleURL; + } + + public Geometry getGeometry() { + return geometry; + } + + /** + * Calculates the bounding box for the Geometry it contains + * to retain backwards compatibility + * @return the {@link LatLngBounds} or null + */ + @Override + public LatLngBounds getBounds() { + if (geometry == null) { + return null; + } + + double[] bbox = TurfMeasurement.bbox(geometry); + return LatLngBounds.from(bbox[3], bbox[2], bbox[1], bbox[0]); + } + + public double getMinZoom() { + return minZoom; + } + + public double getMaxZoom() { + return maxZoom; + } + + public float getPixelRatio() { + return pixelRatio; + } + + /* + * Parceable + */ + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(styleURL); + dest.writeString(Feature.fromGeometry(geometry).toJson()); + dest.writeDouble(minZoom); + dest.writeDouble(maxZoom); + dest.writeFloat(pixelRatio); + } + + public static final Creator CREATOR = new Creator() { + public OfflineGeometryRegionDefinition createFromParcel(Parcel in) { + return new OfflineGeometryRegionDefinition(in); + } + + public OfflineGeometryRegionDefinition[] newArray(int size) { + return new OfflineGeometryRegionDefinition[size]; + } + }; +} 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 dbf425986d..f6dd7ff7a1 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 @@ -109,7 +109,7 @@ public class OfflineManager { @Override public void run() { try { - String path = context.getCacheDir().getAbsolutePath() + File.separator + "mbgl-cache.db"; + String path = FileSource.getInternalCachePath(context) + File.separator + "mbgl-cache.db"; File file = new File(path); if (file.exists()) { file.delete(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java index 2aea565d8b..354a8fb6cd 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java @@ -217,7 +217,7 @@ public class MapSnapshotter { checkThread(); this.context = context.getApplicationContext(); FileSource fileSource = FileSource.getInstance(context); - String programCacheDir = context.getCacheDir().getAbsolutePath(); + String programCacheDir = FileSource.getInternalCachePath(context); nativeInitialize(this, fileSource, options.pixelRatio, options.width, options.height, options.styleUrl, options.styleJson, options.region, options.cameraPosition, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java index c8d02c05d9..495e425976 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; +import android.os.AsyncTask; import android.os.Environment; import android.support.annotation.Keep; import android.support.annotation.NonNull; @@ -11,6 +12,10 @@ import android.support.annotation.UiThread; import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.constants.MapboxConstants; +import com.mapbox.mapboxsdk.utils.ThreadUtils; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import timber.log.Timber; @@ -20,6 +25,11 @@ import timber.log.Timber; */ public class FileSource { + private static String resourcesCachePath; + private static String internalCachePath; + private static final Lock resourcesCachePathLoaderLock = new ReentrantLock(); + private static final Lock internalCachePathLoaderLock = new ReentrantLock(); + /** * This callback allows implementors to transform URLs before they are requested * from the internet. This can be used add or remove custom parameters, or reroute @@ -51,19 +61,20 @@ public class FileSource { @UiThread public static synchronized FileSource getInstance(Context context) { if (INSTANCE == null) { - String cachePath = getCachePath(context); - INSTANCE = new FileSource(cachePath, context.getResources().getAssets()); + INSTANCE = new FileSource(getResourcesCachePath(context), context.getResources().getAssets()); } return INSTANCE; } /** - * Get the cache path for a context. + * Get files directory path for a context. * - * @param context the context to derive the cache path from - * @return the cache path + * @param context the context to derive the files directory path from + * @return the files directory path + * @deprecated Use {@link #getResourcesCachePath(Context)} instead. */ + @Deprecated public static String getCachePath(Context context) { // Default value boolean setStorageExternal = MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL; @@ -122,6 +133,89 @@ public class FileSource { return false; } + /** + * Initializes file directories paths. + * + * @param context the context to derive paths from + */ + @UiThread + public static void initializeFileDirsPaths(Context context) { + ThreadUtils.checkThread("FileSource"); + lockPathLoaders(); + if (resourcesCachePath == null || internalCachePath == null) { + new FileDirsPathsTask().execute(context); + } + } + + private static class FileDirsPathsTask extends AsyncTask<Context, Void, String[]> { + + @Override + protected void onCancelled() { + unlockPathLoaders(); + } + + @Override + protected String[] doInBackground(Context... contexts) { + return new String[] { + getCachePath(contexts[0]), + contexts[0].getCacheDir().getAbsolutePath() + }; + } + + @Override + protected void onPostExecute(String[] paths) { + resourcesCachePath = paths[0]; + internalCachePath = paths[1]; + unlockPathLoaders(); + } + } + + /** + * Get files directory path for a context. + * + * @param context the context to derive the files directory path from + * @return the files directory path + */ + public static String getResourcesCachePath(Context context) { + resourcesCachePathLoaderLock.lock(); + try { + if (resourcesCachePath == null) { + resourcesCachePath = getCachePath(context); + } + return resourcesCachePath; + } finally { + resourcesCachePathLoaderLock.unlock(); + } + } + + /** + * Get internal cache path for a context. + * + * @param context the context to derive the internal cache path from + * @return the internal cache path + */ + public static String getInternalCachePath(Context context) { + internalCachePathLoaderLock.lock(); + try { + if (internalCachePath == null) { + internalCachePath = context.getCacheDir().getAbsolutePath(); + } + return internalCachePath; + } finally { + internalCachePathLoaderLock.unlock(); + } + } + + private static void lockPathLoaders() { + internalCachePathLoaderLock.lock(); + resourcesCachePathLoaderLock.lock(); + } + + private static void unlockPathLoaders() { + resourcesCachePathLoaderLock.unlock(); + internalCachePathLoaderLock.unlock(); + } + @Keep private long nativePtr; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java index 1aa0ce9093..bd4d5d7d4b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java @@ -4,17 +4,22 @@ import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.Size; + import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonNull; +import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.mapbox.mapboxsdk.style.layers.PropertyFactory; import com.mapbox.mapboxsdk.style.layers.PropertyValue; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Locale; +import java.util.Map; /** * The value for any layout property, paint property, or filter may be specified as an expression. @@ -345,7 +350,8 @@ public class Expression { * @return expression * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-rgba">Style specification</a> */ - public static Expression rgba(@NonNull Number red, @NonNull Number green, @NonNull Number blue, @NonNull Number alpha) { + public static Expression rgba(@NonNull Number red, @NonNull Number green, @NonNull Number blue, @NonNull Number + alpha) { return rgba(literal(red), literal(green), literal(blue), literal(alpha)); } @@ -386,6 +392,32 @@ public class Expression { /** * Returns true if the input values are equal, false otherwise. + * The inputs must be numbers, strings, or booleans, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * eq(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second expression + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-==">Style specification</a> + */ + public static Expression eq(@NonNull Expression compareOne, @NonNull Expression compareTwo, + @NonNull Expression collator) { + return new Expression("==", compareOne, compareTwo, collator); + } + + /** + * Returns true if the input values are equal, false otherwise. * <p> * Example usage: * </p> @@ -432,6 +464,32 @@ public class Expression { /** * Returns true if the input values are equal, false otherwise. + * The inputs must be numbers, strings, or booleans, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * eq(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second String + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-==">Style specification</a> + */ + public static Expression eq(@NonNull Expression compareOne, @NonNull String compareTwo, + @NonNull Expression collator) { + return eq(compareOne, literal(compareTwo), collator); + } + + /** + * Returns true if the input values are equal, false otherwise. * <p> * Example usage: * </p> @@ -478,6 +536,32 @@ public class Expression { } /** + * Returns true if the input values are not equal, false otherwise. + * The inputs must be numbers, strings, or booleans, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * neq(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second expression + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-!=">Style specification</a> + */ + public static Expression neq(@NonNull Expression compareOne, @NonNull Expression compareTwo, + @NonNull Expression collator) { + return new Expression("!=", compareOne, compareTwo, collator); + } + + /** * Returns true if the input values are equal, false otherwise. * <p> * Example usage: @@ -524,6 +608,32 @@ public class Expression { } /** + * Returns true if the input values are not equal, false otherwise. + * The inputs must be numbers, strings, or booleans, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * neq(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second String + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-!=">Style specification</a> + */ + public static Expression neq(@NonNull Expression compareOne, @NonNull String compareTwo, + @NonNull Expression collator) { + return new Expression("!=", compareOne, literal(compareTwo), collator); + } + + /** * Returns `true` if the input values are not equal, `false` otherwise. * <p> * Example usage: @@ -572,6 +682,32 @@ public class Expression { /** * Returns true if the first input is strictly greater than the second, false otherwise. + * The inputs must be numbers or strings, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * gt(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second expression + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E">Style specification</a> + */ + public static Expression gt(@NonNull Expression compareOne, @NonNull Expression compareTwo, + @NonNull Expression collator) { + return new Expression(">", compareOne, compareTwo, collator); + } + + /** + * Returns true if the first input is strictly greater than the second, false otherwise. * <p> * Example usage: * </p> @@ -617,6 +753,32 @@ public class Expression { } /** + * Returns true if the first input is strictly greater than the second, false otherwise. + * The inputs must be numbers or strings, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * gt(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second String + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E">Style specification</a> + */ + public static Expression gt(@NonNull Expression compareOne, @NonNull String compareTwo, + @NonNull Expression collator) { + return new Expression(">", compareOne, literal(compareTwo), collator); + } + + /** * Returns true if the first input is strictly less than the second, false otherwise. * The inputs must be numbers or strings, and both of the same type. * <p> @@ -626,13 +788,13 @@ public class Expression { * {@code * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); * fillLayer.setFilter( - * lt(get("keyToValue"), get("keyToOtherValue")) + * lt(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) * ); * } * </pre> * * @param compareOne the first expression - * @param compareTwo the second number + * @param compareTwo the second expression * @return expression * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C">Style specification</a> */ @@ -642,6 +804,32 @@ public class Expression { /** * Returns true if the first input is strictly less than the second, false otherwise. + * The inputs must be numbers or strings, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * lt(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second number + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C">Style specification</a> + */ + public static Expression lt(@NonNull Expression compareOne, @NonNull Expression compareTwo, + @NonNull Expression collator) { + return new Expression("<", compareOne, compareTwo, collator); + } + + /** + * Returns true if the first input is strictly less than the second, false otherwise. * <p> * Example usage: * </p> @@ -687,6 +875,32 @@ public class Expression { } /** + * Returns true if the first input is strictly less than the second, false otherwise. + * The inputs must be numbers or strings, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * lt(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second String + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C">Style specification</a> + */ + public static Expression lt(@NonNull Expression compareOne, @NonNull String compareTwo, + @NonNull Expression collator) { + return new Expression("<", compareOne, literal(compareTwo), collator); + } + + /** * Returns true if the first input is greater than or equal to the second, false otherwise. * The inputs must be numbers or strings, and both of the same type. * <p> @@ -712,6 +926,32 @@ public class Expression { /** * Returns true if the first input is greater than or equal to the second, false otherwise. + * The inputs must be numbers or strings, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * gte(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second expression + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E%3D">Style specification</a> + */ + public static Expression gte(@NonNull Expression compareOne, @NonNull Expression compareTwo, + @NonNull Expression collator) { + return new Expression(">=", compareOne, compareTwo, collator); + } + + /** + * Returns true if the first input is greater than or equal to the second, false otherwise. * <p> * Example usage: * </p> @@ -757,6 +997,32 @@ public class Expression { } /** + * Returns true if the first input is greater than or equal to the second, false otherwise. + * The inputs must be numbers or strings, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * gte(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second String + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E%3D">Style specification</a> + */ + public static Expression gte(@NonNull Expression compareOne, @NonNull String compareTwo, + @NonNull Expression collator) { + return new Expression(">=", compareOne, literal(compareTwo), collator); + } + + /** * Returns true if the first input is less than or equal to the second, false otherwise. * The inputs must be numbers or strings, and both of the same type. * <p> @@ -782,6 +1048,32 @@ public class Expression { /** * Returns true if the first input is less than or equal to the second, false otherwise. + * The inputs must be numbers or strings, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * lte(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second expression + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C%3D">Style specification</a> + */ + public static Expression lte(@NonNull Expression compareOne, @NonNull Expression compareTwo, + @NonNull Expression collator) { + return new Expression("<=", compareOne, compareTwo, collator); + } + + /** + * Returns true if the first input is less than or equal to the second, false otherwise. * <p> * Example usage: * </p> @@ -827,6 +1119,32 @@ public class Expression { } /** + * Returns true if the first input is less than or equal to the second, false otherwise. + * The inputs must be numbers or strings, and both of the same type. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * FillLayer fillLayer = new FillLayer("layer-id", "source-id"); + * fillLayer.setFilter( + * lte(get("keyToValue"), get("keyToOtherValue"), collator(true, false)) + * ); + * } + * </pre> + * + * @param compareOne the first expression + * @param compareTwo the second String + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C%3D">Style specification</a> + */ + public static Expression lte(@NonNull Expression compareOne, @NonNull String compareTwo, + @NonNull Expression collator) { + return new Expression("<=", compareOne, literal(compareTwo), collator); + } + + /** * Returns `true` if all the inputs are `true`, `false` otherwise. * <p> * The inputs are evaluated in order, and evaluation is short-circuiting: @@ -2479,6 +2797,32 @@ public class Expression { } /** + * Returns the IETF language tag of the locale being used by the provided collator. + * This can be used to determine the default system locale, + * or to determine if a requested locale was successfully loaded. + * <p> + * Example usage: + * </p> + * <pre> + * {@code + * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id"); + * circleLayer.setProperties( + * circleColor(switchCase( + eq(literal("it"), resolvedLocale(collator(true, true, "it"))), literal(ColorUtils.colorToRgbaString(Color.GREEN)), + literal(ColorUtils.colorToRgbaString(Color.RED)))) + * ); + * } + * </pre> + * + * @param collator the collator expression + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-resolved-locale">Style specification</a> + */ + public static Expression resolvedLocale(Expression collator) { + return new Expression("resolved-locale", collator); + } + + /** * Returns the input string converted to uppercase. * <p> * Follows the Unicode Default Case Conversion algorithm @@ -2697,6 +3041,104 @@ public class Expression { } /** + * Returns a collator for use in locale-dependent comparison operations. + * The case-sensitive and diacritic-sensitive options default to false. + * The locale argument specifies the IETF language tag of the locale to use. + * If none is provided, the default locale is used. If the requested locale is not available, + * the collator will use a system-defined fallback locale. + * Use resolved-locale to test the results of locale fallback behavior. + * + * @param caseSensitive case sensitive flag + * @param diacriticSensitive diacritic sensitive flag + * @param locale locale + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-collator">Style specification</a> + */ + public static Expression collator(boolean caseSensitive, boolean diacriticSensitive, Locale locale) { + Map<String, Expression> map = new HashMap<>(); + map.put("case-sensitive", literal(caseSensitive)); + map.put("diacritic-sensitive", literal(diacriticSensitive)); + + StringBuilder localeStringBuilder = new StringBuilder(); + + String language = locale.getLanguage(); + if (language != null && !language.isEmpty()) { + localeStringBuilder.append(language); + } + + String country = locale.getCountry(); + if (country != null && !country.isEmpty()) { + localeStringBuilder.append("-"); + localeStringBuilder.append(country); + } + + map.put("locale", literal(localeStringBuilder.toString())); + return new Expression("collator", new ExpressionMap(map)); + } + + /** + * Returns a collator for use in locale-dependent comparison operations. + * The case-sensitive and diacritic-sensitive options default to false. + * The locale argument specifies the IETF language tag of the locale to use. + * If none is provided, the default locale is used. If the requested locale is not available, + * the collator will use a system-defined fallback locale. + * Use resolved-locale to test the results of locale fallback behavior. + * + * @param caseSensitive case sensitive flag + * @param diacriticSensitive diacritic sensitive flag + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-collator">Style specification</a> + */ + public static Expression collator(boolean caseSensitive, boolean diacriticSensitive) { + Map<String, Expression> map = new HashMap<>(); + map.put("case-sensitive", literal(caseSensitive)); + map.put("diacritic-sensitive", literal(diacriticSensitive)); + return new Expression("collator", new ExpressionMap(map)); + } + + /** + * Returns a collator for use in locale-dependent comparison operations. + * The case-sensitive and diacritic-sensitive options default to false. + * The locale argument specifies the IETF language tag of the locale to use. + * If none is provided, the default locale is used. If the requested locale is not available, + * the collator will use a system-defined fallback locale. + * Use resolved-locale to test the results of locale fallback behavior. + * + * @param caseSensitive case sensitive flag + * @param diacriticSensitive diacritic sensitive flag + * @param locale locale + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-collator">Style specification</a> + */ + public static Expression collator(Expression caseSensitive, Expression diacriticSensitive, Expression locale) { + Map<String, Expression> map = new HashMap<>(); + map.put("case-sensitive", caseSensitive); + map.put("diacritic-sensitive", diacriticSensitive); + map.put("locale", locale); + return new Expression("collator", new ExpressionMap(map)); + } + + /** + * Returns a collator for use in locale-dependent comparison operations. + * The case-sensitive and diacritic-sensitive options default to false. + * The locale argument specifies the IETF language tag of the locale to use. + * If none is provided, the default locale is used. If the requested locale is not available, + * the collator will use a system-defined fallback locale. + * Use resolved-locale to test the results of locale fallback behavior. + * + * @param caseSensitive case sensitive flag + * @param diacriticSensitive diacritic sensitive flag + * @return expression + * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-collator">Style specification</a> + */ + public static Expression collator(Expression caseSensitive, Expression diacriticSensitive) { + Map<String, Expression> map = new HashMap<>(); + map.put("case-sensitive", caseSensitive); + map.put("diacritic-sensitive", diacriticSensitive); + return new Expression("collator", new ExpressionMap(map)); + } + + /** * Asserts that the input value is an object. If it is not, the expression is an error * The asserted input value is returned as result. * @@ -3422,8 +3864,8 @@ public class Expression { array.add(operator); if (arguments != null) { for (Expression argument : arguments) { - if (argument instanceof Expression.ExpressionLiteral) { - array.add(toValue((ExpressionLiteral) argument)); + if (argument instanceof ValueExpression) { + array.add(((ValueExpression) argument).toValue()); } else { array.add(argument.toArray()); } @@ -3433,22 +3875,6 @@ public class Expression { } /** - * Converts the expression value to an Object. - * - * @param expressionValue the expression value to convert - * @return the converted object expression - */ - private Object toValue(ExpressionLiteral expressionValue) { - Object value = expressionValue.toValue(); - if (value instanceof PropertyValue) { - throw new IllegalArgumentException("PropertyValue are not allowed as an expression literal, use value instead."); - } else if (value instanceof Expression.ExpressionLiteral) { - return toValue((ExpressionLiteral) value); - } - return value; - } - - /** * Returns a string representation of the object that matches the definition set in the style specification. * <p> * If this expression contains a coma (,) delimited literal, like 'rgba(r, g, b, a)`, @@ -3549,7 +3975,7 @@ public class Expression { * {@link #literal(String)} and {@link #literal(Object)}. * </p> */ - public static class ExpressionLiteral extends Expression { + public static class ExpressionLiteral extends Expression implements ValueExpression { protected Object literal; @@ -3572,7 +3998,14 @@ public class Expression { * * @return the literal object */ - Object toValue() { + @Override + public Object toValue() { + if (literal instanceof PropertyValue) { + throw new IllegalArgumentException( + "PropertyValue are not allowed as an expression literal, use value instead."); + } else if (literal instanceof Expression.ExpressionLiteral) { + return ((ExpressionLiteral) literal).toValue(); + } return literal; } @@ -3589,7 +4022,13 @@ public class Expression { */ @Override public String toString() { - return literal.toString(); + String string; + if (literal instanceof String) { + string = "\"" + literal + "\""; + } else { + string = literal.toString(); + } + return string; } /** @@ -3627,7 +4066,7 @@ public class Expression { return result; } - private String unwrapStringLiteral(String value) { + private static String unwrapStringLiteral(String value) { if (value.length() > 1 && value.charAt(0) == '\"' && value.charAt(value.length() - 1) == '\"') { return value.substring(1, value.length() - 1); @@ -3727,20 +4166,36 @@ public class Expression { JsonElement jsonElement; for (int i = 1; i < jsonArray.size(); i++) { jsonElement = jsonArray.get(i); - if (jsonElement instanceof JsonArray) { - arguments.add(convert((JsonArray) jsonElement)); - } else if (jsonElement instanceof JsonPrimitive) { - arguments.add(convert((JsonPrimitive) jsonElement)); - } else if (jsonElement instanceof JsonNull) { - arguments.add(new Expression.ExpressionLiteral("")); - } else { - throw new RuntimeException("Unsupported expression conversion for " + jsonElement.getClass()); - } + arguments.add(convert(jsonElement)); } return new Expression(operator, arguments.toArray(new Expression[arguments.size()])); } /** + * Converts a JsonElement to an expression + * + * @param jsonElement the json element to convert + * @return the expression + */ + private static Expression convert(@NonNull JsonElement jsonElement) { + if (jsonElement instanceof JsonArray) { + return convert((JsonArray) jsonElement); + } else if (jsonElement instanceof JsonPrimitive) { + return convert((JsonPrimitive) jsonElement); + } else if (jsonElement instanceof JsonNull) { + return new Expression.ExpressionLiteral(""); + } else if (jsonElement instanceof JsonObject) { + Map<String, Expression> map = new HashMap<>(); + for (String key : ((JsonObject) jsonElement).keySet()) { + map.put(key, convert(((JsonObject) jsonElement).get(key))); + } + return new ExpressionMap(map); + } else { + throw new RuntimeException("Unsupported expression conversion for " + jsonElement.getClass()); + } + } + + /** * Converts a JsonPrimitive to an expression literal * * @param jsonPrimitive the json primitive to convert @@ -3816,6 +4271,79 @@ public class Expression { } /** + * Wraps an expression value stored in a Map. + */ + private static class ExpressionMap extends Expression implements ValueExpression { + private Map<String, Expression> map; + + ExpressionMap(Map<String, Expression> map) { + this.map = map; + } + + @Override + public Object toValue() { + Map<String, Object> unwrappedMap = new HashMap<>(); + for (String key : map.keySet()) { + Expression expression = map.get(key); + if (expression instanceof Expression.ExpressionLiteral) { + unwrappedMap.put(key, ((ExpressionLiteral) expression).toValue()); + } else { + unwrappedMap.put(key, expression.toArray()); + } + } + + return unwrappedMap; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("{"); + for (String key : map.keySet()) { + builder.append("\"").append(key).append("\": "); + builder.append(map.get(key)); + builder.append(", "); + } + + if (map.size() > 0) { + builder.delete(builder.length() - 2, builder.length()); + } + + builder.append("}"); + return builder.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + ExpressionMap that = (ExpressionMap) o; + return map.equals(that.map); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (map == null ? 0 : map.hashCode()); + return result; + } + } + + /** + * Interface used to describe expressions that hold a Java value. + */ + private interface ValueExpression { + Object toValue(); + } + + /** * Converts an object that is a primitive array to an Object[] * * @param object the object to convert to an object array 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 index ddb4d04fff..dd20a4b957 100644 --- 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 @@ -28,7 +28,7 @@ public class BackgroundLayer extends Layer { * @param nativePtr pointer used by core */ @Keep - public BackgroundLayer(long nativePtr) { + BackgroundLayer(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java index 8abd78ace2..9d2b9d89a5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java @@ -28,7 +28,7 @@ public class CircleLayer extends Layer { * @param nativePtr pointer used by core */ @Keep - public CircleLayer(long nativePtr) { + CircleLayer(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java index 79b68bbfc0..c03f8689fc 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java @@ -16,7 +16,7 @@ public class CustomLayer extends Layer { } @Keep - public CustomLayer(long nativePtr) { + CustomLayer(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java index 6efe04e39d..2de0de06d9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java @@ -28,7 +28,7 @@ public class FillExtrusionLayer extends Layer { * @param nativePtr pointer used by core */ @Keep - public FillExtrusionLayer(long nativePtr) { + FillExtrusionLayer(long nativePtr) { super(nativePtr); } 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 index aaa20d6f3a..6e28900c26 100644 --- 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 @@ -28,7 +28,7 @@ public class FillLayer extends Layer { * @param nativePtr pointer used by core */ @Keep - public FillLayer(long nativePtr) { + FillLayer(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java index 6d7cece124..f1076ecd96 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java @@ -28,7 +28,7 @@ public class HeatmapLayer extends Layer { * @param nativePtr pointer used by core */ @Keep - public HeatmapLayer(long nativePtr) { + HeatmapLayer(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java index f906104e91..f706e5a234 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java @@ -28,7 +28,7 @@ public class HillshadeLayer extends Layer { * @param nativePtr pointer used by core */ @Keep - public HillshadeLayer(long nativePtr) { + HillshadeLayer(long nativePtr) { super(nativePtr); } 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 index 92aa54e55f..605f883bdb 100644 --- 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 @@ -18,7 +18,7 @@ public abstract class Layer { private boolean invalidated; @Keep - public Layer(long nativePtr) { + protected Layer(long nativePtr) { checkThread(); this.nativePtr = nativePtr; } 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 index 4a742cd7a6..d6519c991f 100644 --- 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 @@ -28,7 +28,7 @@ public class LineLayer extends Layer { * @param nativePtr pointer used by core */ @Keep - public LineLayer(long nativePtr) { + LineLayer(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java index 1cedf8c9ca..cd7bd473f3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java @@ -2055,7 +2055,7 @@ public class PropertyFactory { } /** - * Value to use for a text label. + * Value to use for a text label. If a plain `string` is provided, it will be treated as a `formatted` with default/inherited formatting options. * * @param value a String value * @return property wrapper around String @@ -2065,7 +2065,7 @@ public class PropertyFactory { } /** - * Value to use for a text label. + * Value to use for a text label. If a plain `string` is provided, it will be treated as a `formatted` with default/inherited formatting options. * * @param value a String value * @return property wrapper around String 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 index 83e228cba0..50837a97be 100644 --- 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 @@ -28,7 +28,7 @@ public class RasterLayer extends Layer { * @param nativePtr pointer used by core */ @Keep - public RasterLayer(long nativePtr) { + RasterLayer(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java index f2ddb600a5..4389f6cb21 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java @@ -28,7 +28,7 @@ public class SymbolLayer extends Layer { * @param nativePtr pointer used by core */ @Keep - public SymbolLayer(long nativePtr) { + SymbolLayer(long nativePtr) { super(nativePtr); } 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 index 2d4db2b55d..6ed58b1928 100644 --- 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 @@ -33,7 +33,7 @@ public class <%- camelize(type) %>Layer extends Layer { * @param nativePtr pointer used by core */ @Keep - public <%- camelize(type) %>Layer(long nativePtr) { + <%- camelize(type) %>Layer(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java index c0cf33e150..f3886d6cce 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java @@ -29,7 +29,7 @@ public class Light { * @param nativePtr pointer used by core */ @Keep - public Light(long nativePtr) { + Light(long nativePtr) { checkThread(); this.nativePtr = nativePtr; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs index 59b07d32d4..7c9893a3a4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs @@ -33,7 +33,7 @@ public class Light { * @param nativePtr pointer used by core */ @Keep - public Light(long nativePtr) { + Light(long nativePtr) { checkThread(); this.nativePtr = nativePtr; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java index 1f6029e2a2..6c0b76f00e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java @@ -15,18 +15,26 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * Custom Vector Source, allows using FeatureCollections. - * */ @UiThread public class CustomGeometrySource extends Source { + public static final String THREAD_PREFIX = "CustomGeom"; + public static final int THREAD_POOL_LIMIT = 4; + private static final AtomicInteger poolCount = new AtomicInteger(); + private final Lock executorLock = new ReentrantLock(); private ExecutorService executor; private GeometryTileProvider provider; private final Map<TileID, AtomicBoolean> cancelledTileRequests = new ConcurrentHashMap<>(); @@ -34,7 +42,7 @@ public class CustomGeometrySource extends Source { /** * Create a CustomGeometrySource * - * @param id The source id. + * @param id The source id. * @param provider The tile provider that returns geometry data for this source. */ public CustomGeometrySource(String id, GeometryTileProvider provider) { @@ -45,21 +53,20 @@ public class CustomGeometrySource extends Source { * Create a CustomGeometrySource with non-default CustomGeometrySourceOptions. * <p>Supported options are minZoom, maxZoom, buffer, and tolerance.</p> * - * @param id The source id. + * @param id The source id. * @param provider The tile provider that returns geometry data for this source. - * @param options CustomGeometrySourceOptions. + * @param options CustomGeometrySourceOptions. */ public CustomGeometrySource(String id, GeometryTileProvider provider, CustomGeometrySourceOptions options) { super(); this.provider = provider; - executor = Executors.newFixedThreadPool(4); initialize(id, options); } /** - * Invalidate previously provided features within a given bounds at all zoom levels. - * Invoking this method will result in new requests to `GeometryTileProvider` for regions - * that contain, include, or intersect with the provided bounds. + * Invalidate previously provided features within a given bounds at all zoom levels. + * Invoking this method will result in new requests to `GeometryTileProvider` for regions + * that contain, include, or intersect with the provided bounds. * * @param bounds The region in which features should be invalidated at all zoom levels */ @@ -73,8 +80,8 @@ public class CustomGeometrySource extends Source { * in new requests to `GeometryTileProvider` for visible tiles. * * @param zoomLevel Tile zoom level. - * @param x Tile X coordinate. - * @param y Tile Y coordinate. + * @param x Tile X coordinate. + * @param y Tile Y coordinate. */ public void invalidateTile(int zoomLevel, int x, int y) { checkThread(); @@ -87,9 +94,9 @@ public class CustomGeometrySource extends Source { * background threads. * * @param zoomLevel Tile zoom level. - * @param x Tile X coordinate. - * @param y Tile Y coordinate. - * @param data Feature collection for the tile. + * @param x Tile X coordinate. + * @param y Tile Y coordinate. + * @param data Feature collection for the tile. */ public void setTileData(int zoomLevel, int x, int y, FeatureCollection data) { checkThread(); @@ -140,7 +147,15 @@ public class CustomGeometrySource extends Source { TileID tileID = new TileID(z, x, y); cancelledTileRequests.put(tileID, cancelFlag); GeometryTileRequest request = new GeometryTileRequest(tileID, provider, this, cancelFlag); - executor.execute(request); + + executorLock.lock(); + try { + if (executor != null && !executor.isShutdown()) { + executor.execute(request); + } + } finally { + executorLock.unlock(); + } } @WorkerThread @@ -152,6 +167,40 @@ public class CustomGeometrySource extends Source { } } + @Keep + private void startThreads() { + executorLock.lock(); + try { + if (executor != null && !executor.isShutdown()) { + executor.shutdownNow(); + } + + executor = Executors.newFixedThreadPool(THREAD_POOL_LIMIT, new ThreadFactory() { + final AtomicInteger threadCount = new AtomicInteger(); + final int poolId = poolCount.getAndIncrement(); + + @Override + public Thread newThread(@NonNull Runnable runnable) { + return new Thread( + runnable, + String.format(Locale.US, "%s-%d-%d", THREAD_PREFIX, poolId, threadCount.getAndIncrement())); + } + }); + } finally { + executorLock.unlock(); + } + } + + @Keep + private void releaseThreads() { + executorLock.lock(); + try { + executor.shutdownNow(); + } finally { + executorLock.unlock(); + } + } + private static class TileID { public int z; public int x; @@ -164,7 +213,7 @@ public class CustomGeometrySource extends Source { } public int hashCode() { - return Arrays.hashCode(new int[]{z, x, y}); + return Arrays.hashCode(new int[] {z, x, y}); } public boolean equals(Object object) { @@ -177,7 +226,7 @@ public class CustomGeometrySource extends Source { } if (object instanceof TileID) { - TileID other = (TileID)object; + TileID other = (TileID) object; return this.z == other.z && this.x == other.x && this.y == other.y; } return false; @@ -205,7 +254,7 @@ public class CustomGeometrySource extends Source { FeatureCollection data = provider.getFeaturesForBounds(LatLngBounds.from(id.z, id.x, id.y), id.z); CustomGeometrySource source = sourceRef.get(); - if (!isCancelled() && source != null && data != null) { + if (!isCancelled() && source != null && data != null) { source.setTileData(id, data); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java index 79cde7429c..6961027338 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java @@ -11,9 +11,9 @@ import java.util.HashMap; public class GeoJsonOptions extends HashMap<String, Object> { /** - * Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels). + * Minimum zoom level at which to create vector tiles (lower means more field of view detail at low zoom levels). * - * @param minZoom the maximum zoom - Defaults to 18. + * @param minZoom the minimum zoom - Defaults to 0. * @return the current instance for chaining */ public GeoJsonOptions withMinZoom(int minZoom) { @@ -24,7 +24,7 @@ public class GeoJsonOptions extends HashMap<String, Object> { /** * Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels). * - * @param maxZoom the maximum zoom - Defaults to 18. + * @param maxZoom the maximum zoom - Defaults to 25.5 * @return the current instance for chaining */ public GeoJsonOptions withMaxZoom(int maxZoom) { 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 index 33d8ba03ee..2d9b1c985a 100644 --- 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 @@ -29,7 +29,7 @@ public class GeoJsonSource extends Source { * @param nativePtr - pointer to native peer */ @Keep - public GeoJsonSource(long nativePtr) { + GeoJsonSource(long nativePtr) { super(nativePtr); } @@ -188,7 +188,8 @@ public class GeoJsonSource extends Source { } /** - * Updates the GeoJson with a single feature + * Updates the GeoJson with a single feature. The update is performed asynchronously, + * so the data won't be immediately visible or available to query when this method returns. * * @param feature the GeoJSON {@link Feature} to set */ @@ -198,7 +199,8 @@ public class GeoJsonSource extends Source { } /** - * Updates the GeoJson with a single geometry + * Updates the GeoJson with a single geometry. The update is performed asynchronously, + * so the data won't be immediately visible or available to query when this method returns. * * @param geometry the GeoJSON {@link Geometry} to set */ @@ -208,7 +210,8 @@ public class GeoJsonSource extends Source { } /** - * Updates the GeoJson + * Updates the GeoJson. The update is performed asynchronously, + * so the data won't be immediately visible or available to query when this method returns. * * @param features the GeoJSON FeatureCollection */ @@ -218,7 +221,8 @@ public class GeoJsonSource extends Source { } /** - * Updates the GeoJson + * Updates the GeoJson. The update is performed asynchronously, + * so the data won't be immediately visible or available to query when this method returns. * * @param json the raw GeoJson FeatureCollection string */ diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java index d84105a05c..d0ca5e050b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java @@ -35,7 +35,7 @@ public class ImageSource extends Source { * @param nativePtr - pointer to native peer */ @Keep - public ImageSource(long nativePtr) { + ImageSource(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterDemSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterDemSource.java index dc3635ca86..4c2b39375a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterDemSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterDemSource.java @@ -21,7 +21,7 @@ public class RasterDemSource extends Source { * @param nativePtr - pointer to native peer */ @Keep - private RasterDemSource(long nativePtr) { + RasterDemSource(long nativePtr) { super(nativePtr); } 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 index 6bd0456e0c..dac9b02166 100644 --- 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 @@ -19,7 +19,7 @@ public class RasterSource extends Source { * @param nativePtr - pointer to native peer */ @Keep - public RasterSource(long nativePtr) { + RasterSource(long nativePtr) { super(nativePtr); } 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 index 53c8148580..14d9ef1cc5 100644 --- 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 @@ -18,7 +18,7 @@ public abstract class Source { * @param nativePtr - pointer to native peer */ @Keep - public Source(long nativePtr) { + protected Source(long nativePtr) { checkThread(); this.nativePtr = nativePtr; } 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 index 393d8c2b81..5888eaa7e1 100644 --- 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 @@ -28,7 +28,7 @@ public class VectorSource extends Source { * @param nativePtr - pointer to native peer */ @Keep - public VectorSource(long nativePtr) { + VectorSource(long nativePtr) { super(nativePtr); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml index 8acb0c27cc..1c3653479a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml @@ -10,6 +10,7 @@ <!-- Exposed attrs.xml --> <!--Configuration--> <public name="mapbox_styleUrl" type="attr" /> + <public name="mapbox_styleJson" type="attr" /> <public name="mapbox_apiBaseUrl" type="attr" /> <public name="mapbox_localIdeographFontFamily" type="attr" /> <public name="mapbox_pixelRatio" type="float" /> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-zh-rCN/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..f56e16dde2 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="mapbox_attributionsIconContentDescription">Attribution图标,点击以显示attribution对话框。</string> + <string name="mapbox_myLocationViewContentDescription">定位视图,在地图上显示当前位置。</string> + <string name="mapbox_mapActionDescription">显示由Mapbox创建的地图,通过拖动两个手指来滚动,捏两个手指来放大。</string> + <string name="mapbox_attributionsDialogTitle">Mapbox Maps SDK for Android</string> + <string name="mapbox_attributionTelemetryTitle">让Mapbox地图变得更好</string> + <string name="mapbox_attributionTelemetryMessage">您的匿名数据帮助OpenStreetMap和Mapbox的地图变得更好。</string> + <string name="mapbox_attributionTelemetryPositive">继续参与</string> + <string name="mapbox_attributionTelemetryNegative">不再参与</string> + <string name="mapbox_attributionTelemetryNeutral">更多信息</string> + <string name="mapbox_attributionErrorNoBrowser">设备中未安装任何浏览器,不能打开该网页</string> + <string name="mapbox_offline_error_region_definition_invalid">提供的OfflineRegionDefinition不符合标准地理范围:%s</string> + <string name="mapbox_telemetrySettings">Telemetry设置</string> +</resources> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml index 3a8fa74b34..053da80ade 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml @@ -4,6 +4,7 @@ <!--Configuration--> <attr name="mapbox_styleUrl" format="string"/> + <attr name="mapbox_styleJson" format="string"/> <attr name="mapbox_apiBaseUrl" format="string"/> <attr name="mapbox_localIdeographFontFamily" format="string"/> diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java index f25cf1b7d8..7eacda43c3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java @@ -4,6 +4,7 @@ import com.mapbox.mapboxsdk.BuildConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.util.Set; @@ -21,6 +22,7 @@ public class AttributionParseTest { public void testParseAttributionStringSatellite() throws Exception { AttributionParser attributionParser = new AttributionParser.Options() .withAttributionData(SATELLITE_ATTRIBUTION) + .withContext(RuntimeEnvironment.application) .build(); Set<Attribution> attributionList = attributionParser.getAttributions(); @@ -54,6 +56,7 @@ public class AttributionParseTest { public void testParseAttributionStringStreets() throws Exception { AttributionParser attributionParser = new AttributionParser.Options() .withAttributionData(STREETS_ATTRIBUTION) + .withContext(RuntimeEnvironment.application) .build(); Set<Attribution> attributionList = attributionParser.getAttributions(); @@ -84,6 +87,7 @@ public class AttributionParseTest { AttributionParser attributionParser = new AttributionParser.Options() .withAttributionData(STREETS_ATTRIBUTION) .withMapboxAttribution(false) + .withContext(RuntimeEnvironment.application) .build(); Set<Attribution> attributionList = attributionParser.getAttributions(); @@ -109,6 +113,7 @@ public class AttributionParseTest { public void testParseAttributionArrayString() throws Exception { AttributionParser attributionParser = new AttributionParser.Options() .withAttributionData(new String[] {STREETS_ATTRIBUTION, "", SATELLITE_ATTRIBUTION}) + .withContext(RuntimeEnvironment.application) .build(); Set<Attribution> attributionList = attributionParser.getAttributions(); assertEquals("Size of list should match", 4, attributionList.size()); @@ -142,6 +147,7 @@ public class AttributionParseTest { AttributionParser attributionParser = new AttributionParser.Options() .withAttributionData(SATELLITE_ATTRIBUTION) .withImproveMap(false) + .withContext(RuntimeEnvironment.application) .build(); Set<Attribution> attributionList = attributionParser.getAttributions(); assertEquals("Size of list should match", 3, attributionList.size()); @@ -171,6 +177,7 @@ public class AttributionParseTest { AttributionParser attributionParser = new AttributionParser.Options() .withAttributionData(STREETS_ATTRIBUTION, "", SATELLITE_ATTRIBUTION) .withCopyrightSign(false) + .withContext(RuntimeEnvironment.application) .build(); Set<Attribution> attributionList = attributionParser.getAttributions(); assertEquals("Size of list should match", 4, attributionList.size()); @@ -205,6 +212,7 @@ public class AttributionParseTest { .withAttributionData(STREETS_ATTRIBUTION) .withCopyrightSign(false) .withImproveMap(false) + .withContext(RuntimeEnvironment.application) .build(); assertEquals( @@ -220,6 +228,7 @@ public class AttributionParseTest { AttributionParser attributionParser = new AttributionParser.Options() .withAttributionData(STREETS_ATTRIBUTION) .withImproveMap(false) + .withContext(RuntimeEnvironment.application) .build(); assertEquals( @@ -236,6 +245,7 @@ public class AttributionParseTest { .withCopyrightSign(false) .withImproveMap(false) .withMapboxAttribution(false) + .withContext(RuntimeEnvironment.application) .build(); assertEquals( @@ -251,6 +261,7 @@ public class AttributionParseTest { .withAttributionData(STREETS_ATTRIBUTION) .withImproveMap(false) .withMapboxAttribution(false) + .withContext(RuntimeEnvironment.application) .build(); assertEquals( @@ -267,6 +278,7 @@ public class AttributionParseTest { .withImproveMap(false) .withCopyrightSign(false) .withMapboxAttribution(false) + .withContext(RuntimeEnvironment.application) .build(); assertEquals( @@ -283,6 +295,7 @@ public class AttributionParseTest { .withImproveMap(false) .withCopyrightSign(false) .withMapboxAttribution(false) + .withContext(RuntimeEnvironment.application) .build(); assertEquals( @@ -298,6 +311,7 @@ public class AttributionParseTest { .withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "") .withImproveMap(false) .withCopyrightSign(false) + .withContext(RuntimeEnvironment.application) .build(); assertEquals( diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java index 9dd0ca9285..b8a377604f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java @@ -155,9 +155,9 @@ public class MapboxMapOptionsTest { @Test public void testStyleUrl() { - assertEquals(Style.DARK, new MapboxMapOptions().styleUrl(Style.DARK).getStyle()); - assertNotEquals(Style.LIGHT, new MapboxMapOptions().styleUrl(Style.DARK).getStyle()); - assertNull(new MapboxMapOptions().getStyle()); + assertEquals(Style.DARK, new MapboxMapOptions().styleUrl(Style.DARK).getStyleUrl()); + assertNotEquals(Style.LIGHT, new MapboxMapOptions().styleUrl(Style.DARK).getStyleUrl()); + assertNull(new MapboxMapOptions().getStyleUrl()); } @Test diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java index fb74904f96..79bcdd7b5e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java @@ -1,12 +1,16 @@ package com.mapbox.mapboxsdk.style.expressions; import android.graphics.Color; + import com.mapbox.mapboxsdk.style.layers.PropertyFactory; + import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; import static com.mapbox.mapboxsdk.style.expressions.Expression.abs; import static com.mapbox.mapboxsdk.style.expressions.Expression.acos; @@ -19,6 +23,7 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.atan; import static com.mapbox.mapboxsdk.style.expressions.Expression.bool; import static com.mapbox.mapboxsdk.style.expressions.Expression.ceil; import static com.mapbox.mapboxsdk.style.expressions.Expression.coalesce; +import static com.mapbox.mapboxsdk.style.expressions.Expression.collator; import static com.mapbox.mapboxsdk.style.expressions.Expression.color; import static com.mapbox.mapboxsdk.style.expressions.Expression.concat; import static com.mapbox.mapboxsdk.style.expressions.Expression.cos; @@ -60,6 +65,7 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.pow; import static com.mapbox.mapboxsdk.style.expressions.Expression.product; import static com.mapbox.mapboxsdk.style.expressions.Expression.properties; import static com.mapbox.mapboxsdk.style.expressions.Expression.raw; +import static com.mapbox.mapboxsdk.style.expressions.Expression.resolvedLocale; import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb; import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba; import static com.mapbox.mapboxsdk.style.expressions.Expression.round; @@ -1290,4 +1296,75 @@ public class ExpressionTest { String alpha = color.substring(0, color.length() - 1); assertEquals("alpha value should match", 0.254f, Float.valueOf(alpha), 0.001f); } + + @Test + public void testCollator() { + Object[] expected = new Object[] {"collator", + new HashMap<String, Object>() { + { + put("case-sensitive", true); + put("diacritic-sensitive", true); + put("locale", "it-IT"); + } + } + }; + Object[] actual = collator(true, true, Locale.ITALY).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testStringCollator() { + String expected = "[\"collator\", {\"diacritic-sensitive\": true, \"case-sensitive\": true, \"locale\": " + + "\"it\"}]"; + String actual = collator(true, true, Locale.ITALIAN).toString(); + assertEquals("expression should match", expected, actual); + } + + @Test + public void testResolvedLocale() { + Object[] expected = new Object[] {"resolved-locale", + new Object[] {"collator", + new HashMap<String, Object>() { + { + put("case-sensitive", false); + put("diacritic-sensitive", false); + put("locale", "it"); + } + } + } + }; + Object[] actual = resolvedLocale(collator(false, false, Locale.ITALIAN)).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testRawCollator() { + Object[] expected = new Object[] {"collator", + new HashMap<String, Object>() { + { + put("case-sensitive", true); + put("diacritic-sensitive", true); + put("locale", "it-IT"); + } + } + }; + Object[] actual = raw("[\"collator\", {\"diacritic-sensitive\": true, \"case-sensitive\": true, \"locale\": " + + "\"it-IT\"}]").toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected, actual)); + } + + @Test + public void testRawCollatorDoubleConversion() { + Expression expected = collator(false, false, Locale.ITALIAN); + Object[] actual = raw(expected.toString()).toArray(); + assertTrue("expression should match", Arrays.deepEquals(expected.toArray(), actual)); + } + + @Test + public void testStringNestedCollator() { + String expected = "[\"collator\", {\"diacritic-sensitive\": [\"==\", 2.0, 1.0], \"case-sensitive\": false," + + " \"locale\": \"it\"}]"; + String actual = collator(literal(false), eq(literal(2), literal(1)), literal("it")).toString(); + assertEquals("expression should match", expected, actual); + } }
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java index 738f1e203f..7aaca370b2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java @@ -7,13 +7,18 @@ import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction; import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; import com.mapbox.mapboxsdk.testapp.activity.feature.QueryRenderedFeaturesBoxHighlightActivity; +import com.mapbox.mapboxsdk.testapp.utils.TestConstants; import org.junit.Test; +import static junit.framework.Assert.assertEquals; + /** * Instrumentation test to validate integration of LatLngBounds */ public class LatLngBoundsTest extends BaseActivityTest { + private static final double MAP_BEARING = 50; + @Override protected Class getActivityClass() { return QueryRenderedFeaturesBoxHighlightActivity.class; @@ -31,4 +36,31 @@ public class LatLngBoundsTest extends BaseActivityTest { mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)); }); } + + @Test + public void testLatLngBoundsBearing() { + // regression test for #12549 + validateTestSetup(); + MapboxMapAction.invoke(mapboxMap, (uiController, mapboxMap) -> { + LatLngBounds bounds = new LatLngBounds.Builder() + .include(new LatLng(48.8589506, 2.2773457)) + .include(new LatLng(47.2383171, -1.6309316)) + .build(); + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)); + mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(MAP_BEARING)); + assertEquals( + "Initial bearing should match for latlngbounds", + mapboxMap.getCameraPosition().bearing, + MAP_BEARING, + TestConstants.BEARING_DELTA + ); + + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)); + assertEquals("Bearing should match after resetting latlngbounds", + mapboxMap.getCameraPosition().bearing, + MAP_BEARING, + TestConstants.BEARING_DELTA); + }); + } + }
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CustomGeometrySourceTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CustomGeometrySourceTest.kt new file mode 100644 index 0000000000..b60a4cff67 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CustomGeometrySourceTest.kt @@ -0,0 +1,71 @@ +package com.mapbox.mapboxsdk.testapp.style + +import android.support.test.espresso.Espresso.onView +import android.support.test.espresso.matcher.ViewMatchers.isRoot +import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource +import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource.THREAD_POOL_LIMIT +import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource.THREAD_PREFIX +import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke +import com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationLandscape +import com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationPortrait +import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest +import com.mapbox.mapboxsdk.testapp.activity.style.GridSourceActivity +import com.mapbox.mapboxsdk.testapp.activity.style.GridSourceActivity.ID_GRID_LAYER +import com.mapbox.mapboxsdk.testapp.activity.style.GridSourceActivity.ID_GRID_SOURCE +import org.junit.Assert +import org.junit.Ignore +import org.junit.Test + +class CustomGeometrySourceTest : BaseActivityTest() { + + override fun getActivityClass(): Class<*> = GridSourceActivity::class.java + + @Test + fun sourceNotLeakingThreadsTest() { + validateTestSetup() + waitAction(4000) + onView(isRoot()).perform(orientationLandscape()) + waitAction(2000) + onView(isRoot()).perform(orientationPortrait()) + waitAction(2000) + Assert.assertFalse("Threads should be shutdown when the source is destroyed.", + Thread.getAllStackTraces().keys.filter { + it.name.startsWith(THREAD_PREFIX) + }.count() > THREAD_POOL_LIMIT) + } + + @Test + @Ignore + fun threadsShutdownWhenSourceRemovedTest() { + validateTestSetup() + invoke(mapboxMap) { uiController, mapboxMap -> + mapboxMap.removeLayer(ID_GRID_LAYER) + uiController.loopMainThreadForAtLeast(3000) + mapboxMap.removeSource(ID_GRID_SOURCE) + uiController.loopMainThreadForAtLeast(1000) + Assert.assertTrue("There should be no threads running when the source is removed.", + Thread.getAllStackTraces().keys.filter { + it.name.startsWith(CustomGeometrySource.THREAD_PREFIX) + }.count() == 0) + } + } + + @Test + @Ignore + fun threadsRestartedWhenSourceReAddedTest() { + validateTestSetup() + invoke(mapboxMap) { uiController, mapboxMap -> + mapboxMap.removeLayer((rule.activity as GridSourceActivity).layer) + uiController.loopMainThreadForAtLeast(3000) + mapboxMap.removeSource(ID_GRID_SOURCE) + uiController.loopMainThreadForAtLeast(1000) + mapboxMap.addSource((rule.activity as GridSourceActivity).source) + mapboxMap.addLayer((rule.activity as GridSourceActivity).layer) + uiController.loopMainThreadForAtLeast(1000) + Assert.assertTrue("Threads should be restarted when the source is re-added to the map.", + Thread.getAllStackTraces().keys.filter { + it.name.startsWith(CustomGeometrySource.THREAD_PREFIX) + }.count() == CustomGeometrySource.THREAD_POOL_LIMIT) + } + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java index f1f260c919..4e284cdc14 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java @@ -2,20 +2,30 @@ package com.mapbox.mapboxsdk.testapp.style; import android.graphics.Color; import android.support.test.runner.AndroidJUnit4; + +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.style.expressions.Expression; +import com.mapbox.mapboxsdk.style.layers.CircleLayer; import com.mapbox.mapboxsdk.style.layers.FillLayer; +import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; +import com.mapbox.mapboxsdk.utils.ColorUtils; + import org.junit.Test; import org.junit.runner.RunWith; -import timber.log.Timber; import java.io.IOException; +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.collator; +import static com.mapbox.mapboxsdk.style.expressions.Expression.eq; import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential; import static com.mapbox.mapboxsdk.style.expressions.Expression.get; import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; @@ -26,13 +36,16 @@ import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba; import static com.mapbox.mapboxsdk.style.expressions.Expression.step; import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; import static com.mapbox.mapboxsdk.style.expressions.Expression.string; +import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase; import static com.mapbox.mapboxsdk.style.expressions.Expression.toColor; import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; 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.fillOutlineColor; import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; @RunWith(AndroidJUnit4.class) public class ExpressionTest extends BaseActivityTest { @@ -204,6 +217,54 @@ public class ExpressionTest extends BaseActivityTest { }); } + @Test + public void testCollatorExpression() { + validateTestSetup(); + setupStyle(); + invoke(mapboxMap, (uiController, mapboxMap) -> { + LatLng latLng = new LatLng(51, 17); + + Expression expression1 = eq(literal("Łukasz"), literal("lukasz"), collator(true, true)); + Expression expression2 = eq(literal("Łukasz"), literal("lukasz"), collator(literal(false), eq(literal(1), + literal(1)), literal("en"))); + Expression expression3 = eq(literal("Łukasz"), literal("lukasz"), collator(literal(false), eq(literal(2), + literal(1)))); + + mapboxMap.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()))); + Layer layer = new CircleLayer("layer", "source") + .withProperties(circleColor( + switchCase( + expression1, literal(ColorUtils.colorToRgbaString(Color.GREEN)), + literal(ColorUtils.colorToRgbaString(Color.RED)) + ) + )); + mapboxMap.addLayer(layer); + uiController.loopMainThreadForAtLeast(1000); + assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer") + .isEmpty()); + + layer.setProperties(circleColor( + switchCase( + expression2, literal(ColorUtils.colorToRgbaString(Color.GREEN)), + literal(ColorUtils.colorToRgbaString(Color.RED)) + ) + )); + uiController.loopMainThreadForAtLeast(1000); + assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer") + .isEmpty()); + + layer.setProperties(circleColor( + switchCase( + expression3, literal(ColorUtils.colorToRgbaString(Color.GREEN)), + literal(ColorUtils.colorToRgbaString(Color.RED)) + ) + )); + uiController.loopMainThreadForAtLeast(1000); + assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer") + .isEmpty()); + }); + } + private void setupStyle() { invoke(mapboxMap, (uiController, mapboxMap) -> { // Add a source diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java index 2156c96973..124edacbd2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java @@ -1,21 +1,22 @@ package com.mapbox.mapboxsdk.testapp.style; import android.support.annotation.RawRes; -import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; import android.support.test.runner.AndroidJUnit4; import android.view.View; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.style.layers.CircleLayer; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction; import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import com.mapbox.geojson.Feature; -import com.mapbox.geojson.FeatureCollection; -import com.mapbox.geojson.Point; import org.hamcrest.Matcher; import org.junit.Test; @@ -25,9 +26,8 @@ import java.io.IOException; import timber.log.Timber; -import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static org.junit.Assert.assertTrue; /** * Tests for {@link GeoJsonSource} @@ -41,60 +41,69 @@ public class GeoJsonSourceTests extends BaseActivityTest { } @Test - public void testFeatureCollection() throws Exception { + public void testFeatureCollection() { validateTestSetup(); - onView(withId(R.id.mapView)).perform(new BaseViewAction() { - - @Override - public void perform(UiController uiController, View view) { - GeoJsonSource source = null; - try { - source = new GeoJsonSource("source", FeatureCollection - .fromJson(ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_collection))); - } catch (IOException exception) { - Timber.e(exception); - } - mapboxMap.addSource(source); - mapboxMap.addLayer(new CircleLayer("layer", source.getId())); + MapboxMapAction.invoke(mapboxMap, (uiController, mapboxMap) -> { + GeoJsonSource source = null; + try { + source = new GeoJsonSource("source", FeatureCollection + .fromJson(ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_collection))); + } catch (IOException exception) { + Timber.e(exception); } + mapboxMap.addSource(source); + mapboxMap.addLayer(new CircleLayer("layer", source.getId())); }); } @Test public void testPointGeometry() { validateTestSetup(); - onView(withId(R.id.mapView)).perform(new BaseViewAction() { - - @Override - public void perform(UiController uiController, View view) { - GeoJsonSource source = new GeoJsonSource("source", Point.fromLngLat(0d, 0d)); - mapboxMap.addSource(source); + MapboxMapAction.invoke(mapboxMap, (uiController, mapboxMap) -> { + GeoJsonSource source = new GeoJsonSource("source", Point.fromLngLat(0d, 0d)); + mapboxMap.addSource(source); + mapboxMap.addLayer(new CircleLayer("layer", source.getId())); + }); + } - mapboxMap.addLayer(new CircleLayer("layer", source.getId())); + @Test + public void testFeatureProperties() { + validateTestSetup(); + MapboxMapAction.invoke(mapboxMap, (uiController, mapboxMap) -> { + GeoJsonSource source = null; + try { + source = new GeoJsonSource("source", + ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_properties)); + } catch (IOException exception) { + Timber.e(exception); } - + mapboxMap.addSource(source); + mapboxMap.addLayer(new CircleLayer("layer", source.getId())); }); } @Test - public void testFeatureProperties() throws IOException { + public void testUpdateCoalescing() { validateTestSetup(); - onView(withId(R.id.mapView)).perform(new BaseViewAction() { - - @Override - public void perform(UiController uiController, View view) { - GeoJsonSource source = null; - try { - source = new GeoJsonSource("source", - ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_properties)); - } catch (IOException exception) { - Timber.e(exception); - } - mapboxMap.addSource(source); - - mapboxMap.addLayer(new CircleLayer("layer", source.getId())); + MapboxMapAction.invoke(mapboxMap, (uiController, mapboxMap) -> { + GeoJsonSource source = new GeoJsonSource("source"); + mapboxMap.addSource(source); + mapboxMap.addLayer(new CircleLayer("layer", source.getId())); + + source.setGeoJson(Point.fromLngLat(0, 0)); + source.setGeoJson(Point.fromLngLat(-25, -25)); + try { + source.setGeoJson(ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_properties)); + } catch (IOException exception) { + Timber.e(exception); } + source.setGeoJson(Point.fromLngLat(20, 55)); + uiController.loopMainThreadForAtLeast(1000); + assertTrue( + mapboxMap.queryRenderedFeatures( + mapboxMap.getProjection().toScreenLocation( + new LatLng(55, 20)), "layer").size() == 1); }); } @@ -135,25 +144,20 @@ public class GeoJsonSourceTests extends BaseActivityTest { protected void testFeatureFromResource(final @RawRes int resource) { validateTestSetup(); - onView(withId(R.id.mapView)).perform(new BaseViewAction() { - - @Override - public void perform(UiController uiController, View view) { - GeoJsonSource source = new GeoJsonSource("source"); - mapboxMap.addSource(source); - Layer layer = new CircleLayer("layer", source.getId()); - mapboxMap.addLayer(layer); - - try { - source.setGeoJson(Feature.fromJson(ResourceUtils.readRawResource(rule.getActivity(), resource))); - } catch (IOException exception) { - Timber.e(exception); - } - - mapboxMap.removeLayer(layer); - mapboxMap.removeSource(source); + MapboxMapAction.invoke(mapboxMap, (uiController, mapboxMap) -> { + GeoJsonSource source = new GeoJsonSource("source"); + mapboxMap.addSource(source); + Layer layer = new CircleLayer("layer", source.getId()); + mapboxMap.addLayer(layer); + + try { + source.setGeoJson(Feature.fromJson(ResourceUtils.readRawResource(rule.getActivity(), resource))); + } catch (IOException exception) { + Timber.e(exception); } + mapboxMap.removeLayer(layer); + mapboxMap.removeSource(source); }); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Bold,Arial Unicode MS Bold/0-255.pbf b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Bold,Arial Unicode MS Bold/0-255.pbf Binary files differnew file mode 100644 index 0000000000..457ef91106 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Bold,Arial Unicode MS Bold/0-255.pbf diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Italic,Arial Unicode MS Regular/0-255.pbf b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Italic,Arial Unicode MS Regular/0-255.pbf Binary files differnew file mode 100644 index 0000000000..e7b9431539 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Italic,Arial Unicode MS Regular/0-255.pbf diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Medium,Arial Unicode MS Regular/0-255.pbf b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Medium,Arial Unicode MS Regular/0-255.pbf Binary files differnew file mode 100644 index 0000000000..44b361e00a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Medium,Arial Unicode MS Regular/0-255.pbf diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Regular,Arial Unicode MS Regular/0-255.pbf b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Regular,Arial Unicode MS Regular/0-255.pbf Binary files differnew file mode 100644 index 0000000000..e05af54e91 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Regular,Arial Unicode MS Regular/0-255.pbf diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Regular,Arial Unicode MS Regular/8192-8447.pbf b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Regular,Arial Unicode MS Regular/8192-8447.pbf Binary files differnew file mode 100644 index 0000000000..f05c342aaa --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Regular,Arial Unicode MS Regular/8192-8447.pbf diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/streets.json b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/streets.json new file mode 100644 index 0000000000..5a5e298fb6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/streets.json @@ -0,0 +1,12447 @@ +{ + "version": 8, + "name": "Streets", + "metadata": { + "mapbox:autocomposite": true, + "mapbox:type": "default", + "mapbox:origin": "streets-v10", + "mapbox:groups": { + "1444934828655.3389": { + "name": "Aeroways", + "collapsed": true + }, + "1444933322393.2852": { + "name": "POI labels (scalerank 1)", + "collapsed": true + }, + "1444855786460.0557": { + "name": "Roads", + "collapsed": true + }, + "1444933575858.6992": { + "name": "Highway shields", + "collapsed": true + }, + "1444934295202.7542": { + "name": "Admin boundaries", + "collapsed": true + }, + "1444856151690.9143": { + "name": "State labels", + "collapsed": true + }, + "1444933721429.3076": { + "name": "Road labels", + "collapsed": true + }, + "1444933358918.2366": { + "name": "POI labels (scalerank 2)", + "collapsed": true + }, + "1444933808272.805": { + "name": "Water labels", + "collapsed": true + }, + "1444933372896.5967": { + "name": "POI labels (scalerank 3)", + "collapsed": true + }, + "1444855799204.86": { + "name": "Bridges", + "collapsed": true + }, + "1444856087950.3635": { + "name": "Marine labels", + "collapsed": true + }, + "1456969573402.7817": { + "name": "Hillshading", + "collapsed": true + }, + "1444862510685.128": { + "name": "City labels", + "collapsed": true + }, + "1444855769305.6016": { + "name": "Tunnels", + "collapsed": true + }, + "1456970288113.8113": { + "name": "Landcover", + "collapsed": true + }, + "1444856144497.7825": { + "name": "Country labels", + "collapsed": true + }, + "1444933456003.5437": { + "name": "POI labels (scalerank 4)", + "collapsed": true + } + }, + "mapbox:sdk-support": { + "js": "0.46.0", + "android": "6.0.0", + "ios": "4.0.0" + } + }, + "center": [ + -122.4241, + 37.78 + ], + "zoom": 9, + "sources": { + "composite": { + "url": "mapbox://mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7", + "type": "vector" + } + }, + "sprite": "mapbox://sprites/lukaspaczos/cjkwfdgzn1fz42rqtvsk6rrmd", + "glyphs": "asset://fonts/{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%)" + ] + ] + } + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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%)" + ] + ] + } + }, + "interactive": true + }, + { + "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%)" + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "pitch", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ + "==", + "class", + "pitch" + ], + "layout": {}, + "paint": { + "fill-color": "hsl(100, 57%, 72%)" + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "id": "cemetery", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ + "==", + "class", + "cemetery" + ], + "layout": {}, + "paint": { + "fill-color": "hsl(75, 37%, 81%)" + }, + "interactive": true + }, + { + "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%)" + ] + ] + } + }, + "interactive": true + }, + { + "id": "sand", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ + "==", + "class", + "sand" + ], + "layout": {}, + "paint": { + "fill-color": "hsl(60, 46%, 87%)" + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "id": "water", + "type": "fill", + "source": "composite", + "source-layer": "water", + "layout": {}, + "paint": { + "fill-color": "hsl(196, 80%, 70%)" + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "id": "aeroway-polygon", + "type": "fill", + "metadata": { + "mapbox:group": "1444934828655.3389" + }, + "source": "composite", + "source-layer": "aeroway", + "minzoom": 11, + "filter": [ + "all", + [ + "!=", + "type", + "apron" + ], + [ + "==", + "$type", + "Polygon" + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "building-line", + "type": "line", + "source": "composite", + "source-layer": "building", + "minzoom": 15, + "filter": [ + "all", + [ + "!=", + "type", + "building:part" + ], + [ + "==", + "underground", + "false" + ] + ], + "layout": {}, + "paint": { + "line-color": "hsl(35, 6%, 79%)", + "line-width": { + "base": 1.5, + "stops": [ + [ + 15, + 0.75 + ], + [ + 20, + 3 + ] + ] + }, + "line-opacity": { + "base": 1, + "stops": [ + [ + 15.5, + 0 + ], + [ + 16, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "building", + "type": "fill", + "source": "composite", + "source-layer": "building", + "minzoom": 15, + "filter": [ + "all", + [ + "!=", + "type", + "building:part" + ], + [ + "==", + "underground", + "false" + ] + ], + "layout": {}, + "paint": { + "fill-color": { + "base": 1, + "stops": [ + [ + 15, + "hsl(35, 11%, 88%)" + ], + [ + 16, + "hsl(35, 8%, 85%)" + ] + ] + }, + "fill-opacity": { + "base": 1, + "stops": [ + [ + 15.5, + 0 + ], + [ + 16, + 1 + ] + ] + }, + "fill-outline-color": "hsl(35, 6%, 79%)" + }, + "interactive": true + }, + { + "id": "tunnel-street-low", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { + "stops": [ + [ + 11.5, + 0 + ], + [ + 12, + 1 + ], + [ + 14, + 1 + ], + [ + 14.01, + 0 + ] + ] + } + }, + "interactive": true + }, + { + "id": "tunnel-street_limited-low", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street_limited" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { + "stops": [ + [ + 11.5, + 0 + ], + [ + 12, + 1 + ], + [ + 14, + 1 + ], + [ + 14.01, + 0 + ] + ] + } + }, + "interactive": true + }, + { + "id": "tunnel-service-link-track-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "==", + "structure", + "tunnel" + ], + [ + "in", + "class", + "link", + "service", + "track" + ] + ] + ], + "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 + ] + }, + "interactive": true + }, + { + "id": "tunnel-street_limited-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street_limited" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "tunnel-street-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "tunnel-secondary-tertiary-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "tunnel" + ], + [ + "in", + "class", + "secondary", + "tertiary" + ] + ] + ], + "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%)" + }, + "interactive": true + }, + { + "id": "tunnel-primary-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "primary" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 10, + 1 + ], + [ + 16, + 2 + ] + ] + }, + "line-dasharray": [ + 3, + 3 + ], + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 5, + 0.75 + ], + [ + 18, + 32 + ] + ] + }, + "line-color": "hsl(230, 19%, 75%)" + }, + "interactive": true + }, + { + "id": "tunnel-trunk_link-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "tunnel" + ], + [ + "==", + "type", + "trunk_link" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.75 + ], + [ + 20, + 2 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-dasharray": [ + 3, + 3 + ] + }, + "interactive": true + }, + { + "id": "tunnel-motorway_link-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "motorway_link" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.75 + ], + [ + 20, + 2 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-dasharray": [ + 3, + 3 + ] + }, + "interactive": true + }, + { + "id": "tunnel-trunk-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "tunnel" + ], + [ + "==", + "type", + "trunk" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 10, + 1 + ], + [ + 16, + 2 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 5, + 0.75 + ], + [ + 18, + 32 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 3, + 3 + ] + }, + "interactive": true + }, + { + "id": "tunnel-motorway-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 10, + 1 + ], + [ + 16, + 2 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 5, + 0.75 + ], + [ + 18, + 32 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 3, + 3 + ] + }, + "interactive": true + }, + { + "id": "tunnel-construction", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "construction" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-join": "miter" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.99, + 0 + ], + [ + 14, + 1 + ] + ] + }, + "line-dasharray": { + "base": 1, + "stops": [ + [ + 14, + [ + 0.4, + 0.8 + ] + ], + [ + 15, + [ + 0.3, + 0.6 + ] + ], + [ + 16, + [ + 0.2, + 0.3 + ] + ], + [ + 17, + [ + 0.2, + 0.25 + ] + ], + [ + 18, + [ + 0.15, + 0.15 + ] + ] + ] + } + }, + "interactive": true + }, + { + "id": "tunnel-path", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "steps" + ], + [ + "==", + "class", + "path" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "tunnel-trunk_link", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "tunnel" + ], + [ + "==", + "type", + "trunk_link" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(46, 77%, 78%)", + "line-opacity": 1, + "line-dasharray": [ + 1, + 0 + ] + }, + "interactive": true + }, + { + "id": "tunnel-motorway_link", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "motorway_link" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(26, 100%, 78%)", + "line-opacity": 1, + "line-dasharray": [ + 1, + 0 + ] + }, + "interactive": true + }, + { + "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 + ] + ] + ] + } + }, + "interactive": true + }, + { + "id": "tunnel-service-link-track", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "==", + "structure", + "tunnel" + ], + [ + "in", + "class", + "link", + "service", + "track" + ] + ] + ], + "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%)", + "line-dasharray": [ + 1, + 0 + ] + }, + "interactive": true + }, + { + "id": "tunnel-street_limited", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street_limited" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(35, 14%, 93%)", + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.99, + 0 + ], + [ + 14, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "tunnel-street", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.99, + 0 + ], + [ + 14, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "tunnel-secondary-tertiary", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "tunnel" + ], + [ + "in", + "class", + "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": 1, + "line-dasharray": [ + 1, + 0 + ], + "line-blur": 0 + }, + "interactive": true + }, + { + "id": "tunnel-primary", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "primary" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 5, + 0.75 + ], + [ + 18, + 32 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": [ + 1, + 0 + ], + "line-blur": 0 + }, + "interactive": true + }, + { + "id": "tunnel-oneway-arrows-blue-minor", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "==", + "oneway", + "true" + ], + [ + "==", + "structure", + "tunnel" + ], + [ + "in", + "class", + "link", + "path", + "pedestrian", + "service", + "track" + ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { + "base": 1, + "stops": [ + [ + 17, + "oneway-small" + ], + [ + 18, + "oneway-large" + ] + ] + }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {}, + "interactive": true + }, + { + "id": "tunnel-oneway-arrows-blue-major", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "==", + "oneway", + "true" + ], + [ + "==", + "structure", + "tunnel" + ], + [ + "in", + "class", + "primary", + "secondary", + "street", + "street_limited", + "tertiary" + ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { + "base": 1, + "stops": [ + [ + 16, + "oneway-small" + ], + [ + 17, + "oneway-large" + ] + ] + }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {}, + "interactive": true + }, + { + "id": "tunnel-trunk", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "trunk" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 5, + 0.75 + ], + [ + 18, + 32 + ] + ] + }, + "line-color": "hsl(46, 77%, 78%)" + }, + "interactive": true + }, + { + "id": "tunnel-motorway", + "type": "line", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "structure", + "tunnel" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 5, + 0.75 + ], + [ + 18, + 32 + ] + ] + }, + "line-dasharray": [ + 1, + 0 + ], + "line-opacity": 1, + "line-color": "hsl(26, 100%, 78%)", + "line-blur": 0 + }, + "interactive": true + }, + { + "id": "tunnel-oneway-arrows-white", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855769305.6016" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "type", + "primary_link", + "secondary_link", + "tertiary_link" + ], + [ + "==", + "oneway", + "true" + ], + [ + "==", + "structure", + "tunnel" + ], + [ + "in", + "class", + "link", + "motorway", + "motorway_link", + "trunk" + ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { + "base": 1, + "stops": [ + [ + 16, + "oneway-white-small" + ], + [ + 17, + "oneway-white-large" + ] + ] + }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {}, + "interactive": true + }, + { + "id": "ferry", + "type": "line", + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "type", + "ferry" + ] + ], + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1, + "stops": [ + [ + 15, + "hsl(205, 73%, 63%)" + ], + [ + 17, + "hsl(230, 73%, 63%)" + ] + ] + }, + "line-opacity": 1, + "line-width": { + "base": 1.5, + "stops": [ + [ + 14, + 0.5 + ], + [ + 20, + 1 + ] + ] + }, + "line-dasharray": { + "base": 1, + "stops": [ + [ + 12, + [ + 1, + 0 + ] + ], + [ + 13, + [ + 12, + 4 + ] + ] + ] + } + }, + "interactive": true + }, + { + "id": "ferry_auto", + "type": "line", + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "type", + "ferry_auto" + ] + ], + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1, + "stops": [ + [ + 15, + "hsl(205, 73%, 63%)" + ], + [ + 17, + "hsl(230, 73%, 63%)" + ] + ] + }, + "line-opacity": 1, + "line-width": { + "base": 1.5, + "stops": [ + [ + 14, + 0.5 + ], + [ + 20, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-path-bg", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "!in", + "type", + "crossing", + "sidewalk", + "steps" + ], + [ + "==", + "class", + "path" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "turning-features-outline", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ + "==", + "$type", + "Point" + ], + [ + "in", + "class", + "turning_circle", + "turning_loop" + ] + ], + "layout": { + "icon-image": "turning-circle-outline", + "icon-size": { + "base": 1.5, + "stops": [ + [ + 14, + 0.122 + ], + [ + 18, + 0.969 + ], + [ + 20, + 1 + ] + ] + }, + "icon-allow-overlap": true, + "icon-ignore-placement": true, + "icon-padding": 0, + "icon-rotation-alignment": "map" + }, + "paint": {}, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-service-link-track-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "link", + "service", + "track" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-street_limited-case", + "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, + 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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-street-case", + "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, + 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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-secondary-tertiary-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "secondary", + "tertiary" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-primary-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "primary" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-motorway_link-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "motorway_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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-trunk_link-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "type", + "trunk_link" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.75 + ], + [ + 20, + 2 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-opacity": { + "base": 1, + "stops": [ + [ + 10.99, + 0 + ], + [ + 11, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-trunk-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "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": { + "base": 1, + "stops": [ + [ + 6, + 0 + ], + [ + 6.1, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-motorway-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "motorway" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-sidewalks", + "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, + 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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-path", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "!in", + "type", + "crossing", + "sidewalk", + "steps" + ], + [ + "==", + "class", + "path" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-steps", + "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, + 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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-trunk_link", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "type", + "trunk_link" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(46, 85%, 67%)", + "line-opacity": 1 + }, + "interactive": true + }, + { + "id": "road-motorway_link", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "motorway_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(26, 100%, 68%)", + "line-opacity": 1 + }, + "interactive": true + }, + { + "id": "road-pedestrian", + "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, + 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 + ] + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-pedestrian-polygon-fill", + "type": "fill", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + [ + "==", + "$type", + "Polygon" + ], + [ + "all", + [ + "==", + "structure", + "none" + ], + [ + "in", + "class", + "path", + "pedestrian" + ] + ] + ], + "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 + }, + "interactive": true + }, + { + "id": "road-pedestrian-polygon-pattern", + "type": "fill", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + [ + "==", + "$type", + "Polygon" + ], + [ + "all", + [ + "==", + "structure", + "none" + ], + [ + "in", + "class", + "path", + "pedestrian" + ] + ] + ], + "layout": {}, + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-polygon", + "type": "fill", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + [ + "==", + "$type", + "Polygon" + ], + [ + "all", + [ + "!in", + "class", + "motorway", + "path", + "pedestrian", + "trunk" + ], + [ + "!in", + "structure", + "bridge", + "tunnel" + ] + ] + ], + "layout": {}, + "paint": { + "fill-color": "hsl(0, 0%, 100%)", + "fill-outline-color": "#d6d9e6" + }, + "interactive": true + }, + { + "id": "road-service-link-track", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "link", + "service", + "track" + ] + ] + ], + "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%)" + }, + "interactive": true + }, + { + "id": "road-street_limited", + "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(35, 14%, 93%)", + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.99, + 0 + ], + [ + 14, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-street", + "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": { + "base": 1, + "stops": [ + [ + 13.99, + 0 + ], + [ + 14, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-secondary-tertiary", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "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": { + "base": 1, + "stops": [ + [ + 5, + "hsl(35, 32%, 91%)" + ], + [ + 8, + "hsl(0, 0%, 100%)" + ] + ] + }, + "line-opacity": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 5.5, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-primary", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "primary" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "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 + }, + "interactive": true + }, + { + "id": "road-oneway-arrows-blue-minor", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "oneway", + "true" + ], + [ + "in", + "class", + "link", + "path", + "pedestrian", + "service", + "track" + ] + ] + ], + "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": {}, + "interactive": true + }, + { + "id": "road-oneway-arrows-blue-major", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "oneway", + "true" + ], + [ + "in", + "class", + "primary", + "secondary", + "street", + "street_limited", + "tertiary" + ] + ] + ], + "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": {}, + "interactive": true + }, + { + "id": "road-trunk", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "trunk" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "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%)" + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-motorway", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "motorway" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "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%)" + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-rail", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "major_rail", + "minor_rail" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "road-rail-tracks", + "type": "line", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "major_rail", + "minor_rail" + ] + ] + ], + "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, + 4 + ], + [ + 20, + 8 + ] + ] + }, + "line-dasharray": [ + 0.1, + 15 + ], + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.75, + 0 + ], + [ + 14, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "level-crossings", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ + "==", + "$type", + "Point" + ], + [ + "==", + "class", + "level_crossing" + ] + ], + "layout": { + "icon-size": 1, + "icon-image": "level-crossing", + "icon-allow-overlap": true + }, + "paint": {}, + "interactive": true + }, + { + "id": "road-oneway-arrows-white", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "structure", + "bridge", + "tunnel" + ], + [ + "!in", + "type", + "primary_link", + "secondary_link", + "tertiary_link" + ], + [ + "==", + "oneway", + "true" + ], + [ + "in", + "class", + "link", + "motorway", + "motorway_link", + "trunk" + ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { + "base": 1, + "stops": [ + [ + 16, + "oneway-white-small" + ], + [ + 17, + "oneway-white-large" + ] + ] + }, + "icon-padding": 2, + "symbol-spacing": 200 + }, + "paint": {}, + "interactive": true + }, + { + "id": "turning-features", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855786460.0557" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ + "==", + "$type", + "Point" + ], + [ + "in", + "class", + "turning_circle", + "turning_loop" + ] + ], + "layout": { + "icon-image": "turning-circle", + "icon-size": { + "base": 1.5, + "stops": [ + [ + 14, + 0.095 + ], + [ + 18, + 1 + ] + ] + }, + "icon-allow-overlap": true, + "icon-ignore-placement": true, + "icon-padding": 0, + "icon-rotation-alignment": "map" + }, + "paint": {}, + "interactive": true + }, + { + "id": "bridge-path-bg", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "steps" + ], + [ + "==", + "class", + "path" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-street-low", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { + "stops": [ + [ + 11.5, + 0 + ], + [ + 12, + 1 + ], + [ + 14, + 1 + ], + [ + 14.01, + 0 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-street_limited-low", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street_limited" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { + "stops": [ + [ + 11.5, + 0 + ], + [ + 12, + 1 + ], + [ + 14, + 1 + ], + [ + 14.01, + 0 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-service-link-track-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "link", + "service", + "track" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-street_limited-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street_limited" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "layout": { + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.75 + ], + [ + 20, + 2 + ] + ] + }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 13, + 0 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-street-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "layout": { + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.75 + ], + [ + 20, + 2 + ] + ] + }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.99, + 0 + ], + [ + 14, + 1 + ] + ] + }, + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 13, + 0 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-secondary-tertiary-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "secondary", + "tertiary" + ] + ] + ], + "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 + ] + }, + "interactive": true + }, + { + "id": "bridge-primary-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "primary" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "layout": { + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 10, + 1 + ], + [ + 16, + 2 + ] + ] + }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 5, + 0.75 + ], + [ + 18, + 32 + ] + ] + }, + "line-translate": [ + 0, + 0 + ] + }, + "interactive": true + }, + { + "id": "bridge-trunk_link-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "layer", + 2, + 3, + 4, + 5 + ], + [ + "==", + "structure", + "bridge" + ], + [ + "==", + "type", + "trunk_link" + ] + ] + ], + "layout": { + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.75 + ], + [ + 20, + 2 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-opacity": { + "base": 1, + "stops": [ + [ + 10.99, + 0 + ], + [ + 11, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-motorway_link-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "layer", + 2, + 3, + 4, + 5 + ], + [ + "==", + "class", + "motorway_link" + ], + [ + "==", + "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 + }, + "interactive": true + }, + { + "id": "bridge-trunk-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "layer", + 2, + 3, + 4, + 5 + ], + [ + "==", + "class", + "trunk" + ], + [ + "==", + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-motorway-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "layer", + 2, + 3, + 4, + 5 + ], + [ + "==", + "class", + "motorway" + ], + [ + "==", + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-construction", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "construction" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "layout": { + "line-join": "miter" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.99, + 0 + ], + [ + 14, + 1 + ] + ] + }, + "line-dasharray": { + "base": 1, + "stops": [ + [ + 14, + [ + 0.4, + 0.8 + ] + ], + [ + 15, + [ + 0.3, + 0.6 + ] + ], + [ + 16, + [ + 0.2, + 0.3 + ] + ], + [ + 17, + [ + 0.2, + 0.25 + ] + ], + [ + 18, + [ + 0.15, + 0.15 + ] + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-path", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "steps" + ], + [ + "==", + "class", + "path" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-steps", + "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, + 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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-trunk_link", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "layer", + 2, + 3, + 4, + 5 + ], + [ + "==", + "structure", + "bridge" + ], + [ + "==", + "type", + "trunk_link" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(46, 85%, 67%)" + }, + "interactive": true + }, + { + "id": "bridge-motorway_link", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "layer", + 2, + 3, + 4, + 5 + ], + [ + "==", + "class", + "motorway_link" + ], + [ + "==", + "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%)" + }, + "interactive": true + }, + { + "id": "bridge-pedestrian", + "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, + 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 + ] + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-service-link-track", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!=", + "type", + "trunk_link" + ], + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "link", + "service", + "track" + ] + ] + ], + "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%)" + }, + "interactive": true + }, + { + "id": "bridge-street_limited", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street_limited" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(35, 14%, 93%)", + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.99, + 0 + ], + [ + 14, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-street", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "street" + ], + [ + "==", + "structure", + "bridge" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 12.5, + 0.5 + ], + [ + 14, + 2 + ], + [ + 18, + 18 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.99, + 0 + ], + [ + 14, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-secondary-tertiary", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "type", + "secondary", + "tertiary" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 8.5, + 0.5 + ], + [ + 10, + 0.75 + ], + [ + 18, + 26 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 5.5, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-primary", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "==", + "type", + "primary" + ] + ] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": { + "base": 1.5, + "stops": [ + [ + 5, + 0.75 + ], + [ + 18, + 32 + ] + ] + }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1 + }, + "interactive": true + }, + { + "id": "bridge-oneway-arrows-blue-minor", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "oneway", + "true" + ], + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "link", + "path", + "pedestrian", + "service", + "track" + ] + ] + ], + "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": {}, + "interactive": true + }, + { + "id": "bridge-oneway-arrows-blue-major", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "oneway", + "true" + ], + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "primary", + "secondary", + "street", + "street_limited", + "tertiary" + ] + ] + ], + "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": {}, + "interactive": true + }, + { + "id": "bridge-trunk", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "layer", + 2, + 3, + 4, + 5 + ], + [ + "==", + "class", + "trunk" + ], + [ + "==", + "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%)" + }, + "interactive": true + }, + { + "id": "bridge-motorway", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "layer", + 2, + 3, + 4, + 5 + ], + [ + "==", + "class", + "motorway" + ], + [ + "==", + "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%)" + }, + "interactive": true + }, + { + "id": "bridge-rail", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "major_rail", + "minor_rail" + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-rail-tracks", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "major_rail", + "minor_rail" + ] + ] + ], + "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, + 4 + ], + [ + 20, + 8 + ] + ] + }, + "line-dasharray": [ + 0.1, + 15 + ], + "line-opacity": { + "base": 1, + "stops": [ + [ + 13.75, + 0 + ], + [ + 20, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-trunk_link-2-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "==", + "type", + "trunk_link" + ], + [ + ">=", + "layer", + 2 + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-motorway_link-2-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "motorway_link" + ], + [ + "==", + "structure", + "bridge" + ], + [ + ">=", + "layer", + 2 + ] + ] + ], + "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 + }, + "interactive": true + }, + { + "id": "bridge-trunk-2-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "trunk" + ], + [ + "==", + "structure", + "bridge" + ], + [ + ">=", + "layer", + 2 + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-motorway-2-case", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "structure", + "bridge" + ], + [ + ">=", + "layer", + 2 + ] + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "bridge-trunk_link-2", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "structure", + "bridge" + ], + [ + "==", + "type", + "trunk_link" + ], + [ + ">=", + "layer", + 2 + ] + ] + ], + "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%)" + }, + "interactive": true + }, + { + "id": "bridge-motorway_link-2", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "motorway_link" + ], + [ + "==", + "structure", + "bridge" + ], + [ + ">=", + "layer", + 2 + ] + ] + ], + "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%)" + }, + "interactive": true + }, + { + "id": "bridge-trunk-2", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "trunk" + ], + [ + "==", + "structure", + "bridge" + ], + [ + ">=", + "layer", + 2 + ] + ] + ], + "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%)" + }, + "interactive": true + }, + { + "id": "bridge-motorway-2", + "type": "line", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "structure", + "bridge" + ], + [ + ">=", + "layer", + 2 + ] + ] + ], + "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%)" + }, + "interactive": true + }, + { + "id": "bridge-oneway-arrows-white", + "type": "symbol", + "metadata": { + "mapbox:group": "1444855799204.86" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "type", + "primary_link", + "secondary_link", + "tertiary_link" + ], + [ + "==", + "oneway", + "true" + ], + [ + "==", + "structure", + "bridge" + ], + [ + "in", + "class", + "link", + "motorway", + "motorway_link", + "trunk" + ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { + "base": 1, + "stops": [ + [ + 16, + "oneway-white-small" + ], + [ + 17, + "oneway-white-large" + ] + ] + }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {}, + "interactive": true + }, + { + "id": "aerialway", + "type": "line", + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "aerialway" + ] + ], + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "hsl(230, 10%, 74%)", + "line-width": { + "base": 1.5, + "stops": [ + [ + 14, + 0.5 + ], + [ + 20, + 1 + ] + ] + } + }, + "interactive": true + }, + { + "id": "admin-3-4-boundaries-bg", + "type": "line", + "metadata": { + "mapbox:group": "1444934295202.7542" + }, + "source": "composite", + "source-layer": "admin", + "filter": [ + "all", + [ + "==", + "maritime", + 0 + ], + [ + ">=", + "admin_level", + 3 + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "admin-3-4-boundaries", + "type": "line", + "metadata": { + "mapbox:group": "1444934295202.7542" + }, + "source": "composite", + "source-layer": "admin", + "filter": [ + "all", + [ + "==", + "maritime", + 0 + ], + [ + ">=", + "admin_level", + 3 + ] + ], + "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%)" + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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-pitch-alignment": "viewport", + "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 + }, + "interactive": true + }, + { + "id": "poi-scalerank4-l15", + "type": "symbol", + "metadata": { + "mapbox:group": "1444933456003.5437" + }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 17, + "filter": [ + "all", + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ + "==", + "scalerank", + 4 + ], + [ + ">=", + "localrank", + 15 + ] + ], + "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 + }, + "interactive": true + }, + { + "id": "poi-scalerank4-l1", + "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" + ], + [ + "<=", + "localrank", + 14 + ], + [ + "==", + "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 + }, + "interactive": true + }, + { + "id": "poi-parks_scalerank4", + "type": "symbol", + "metadata": { + "mapbox:group": "1444933456003.5437" + }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 15, + "filter": [ + "all", + [ + "==", + "scalerank", + 4 + ], + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ] + ], + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "id": "poi-parks-scalerank3", + "type": "symbol", + "metadata": { + "mapbox:group": "1444933372896.5967" + }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "==", + "scalerank", + 3 + ], + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ] + ], + "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 + }, + "interactive": true + }, + { + "id": "road-label-small", + "type": "symbol", + "metadata": { + "mapbox:group": "1444933721429.3076" + }, + "source": "composite", + "source-layer": "road_label", + "minzoom": 15, + "filter": [ + "all", + [ + "!in", + "class", + "golf", + "link", + "motorway", + "pedestrian", + "primary", + "secondary", + "street", + "street_limited", + "tertiary", + "trunk" + ], + [ + "==", + "$type", + "LineString" + ] + ], + "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-pitch-alignment": "viewport", + "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 + }, + "interactive": true + }, + { + "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-pitch-alignment": "viewport", + "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 + }, + "interactive": true + }, + { + "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-pitch-alignment": "viewport", + "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 + }, + "interactive": true + }, + { + "id": "road-shields-black", + "type": "symbol", + "metadata": { + "mapbox:group": "1444933575858.6992" + }, + "source": "composite", + "source-layer": "road_label", + "filter": [ + "all", + [ + "!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" + ], + [ + "<=", + "reflen", + 6 + ] + ], + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "id": "motorway-junction", + "type": "symbol", + "metadata": { + "mapbox:group": "1444933575858.6992" + }, + "source": "composite", + "source-layer": "motorway_junction", + "minzoom": 14, + "filter": [ + "all", + [ + "<=", + "reflen", + 9 + ], + [ + ">", + "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 + ] + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "id": "poi-parks-scalerank2", + "type": "symbol", + "metadata": { + "mapbox:group": "1444933358918.2366" + }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "==", + "scalerank", + 2 + ], + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ] + ], + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "id": "place-residential", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "maxzoom": 18, + "filter": [ + "all", + [ + "all", + [ + "<=", + "localrank", + 10 + ], + [ + "==", + "type", + "residential" + ] + ], + [ + "in", + "$type", + "LineString", + "Point", + "Polygon" + ] + ], + "layout": { + "text-line-height": 1.2, + "text-size": { + "base": 1, + "stops": [ + [ + 10, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ + "DIN Offc Pro Regular,Arial Unicode MS Regular" + ], + "text-padding": 2, + "text-offset": [ + 0, + 0 + ], + "text-rotation-alignment": "viewport", + "text-field": "{name_en}", + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "text-halo-blur": 0.5 + }, + "interactive": true + }, + { + "id": "poi-parks-scalerank1", + "type": "symbol", + "metadata": { + "mapbox:group": "1444933322393.2852" + }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "<=", + "scalerank", + 1 + ], + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ] + ], + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "place-city-md-s", + "type": "symbol", + "metadata": { + "mapbox:group": "1444862510685.128" + }, + "source": "composite", + "source-layer": "place_label", + "maxzoom": 14, + "filter": [ + "all", + [ + "==", + "type", + "city" + ], + [ + "in", + "ldir", + "E", + "S", + "SE", + "SW" + ], + [ + "in", + "scalerank", + 3, + 4, + 5 + ] + ], + "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 + ] + ] + } + }, + "interactive": true + }, + { + "id": "place-city-md-n", + "type": "symbol", + "metadata": { + "mapbox:group": "1444862510685.128" + }, + "source": "composite", + "source-layer": "place_label", + "maxzoom": 14, + "filter": [ + "all", + [ + "==", + "type", + "city" + ], + [ + "in", + "ldir", + "N", + "NE", + "NW", + "W" + ], + [ + "in", + "scalerank", + 3, + 4, + 5 + ] + ], + "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 + }, + "interactive": true + }, + { + "id": "place-city-lg-s", + "type": "symbol", + "metadata": { + "mapbox:group": "1444862510685.128" + }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 1, + "maxzoom": 14, + "filter": [ + "all", + [ + "<=", + "scalerank", + 2 + ], + [ + "==", + "type", + "city" + ], + [ + "in", + "ldir", + "E", + "S", + "SE", + "SW" + ] + ], + "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 + }, + "interactive": true + }, + { + "id": "place-city-lg-n", + "type": "symbol", + "metadata": { + "mapbox:group": "1444862510685.128" + }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 1, + "maxzoom": 14, + "filter": [ + "all", + [ + "<=", + "scalerank", + 2 + ], + [ + "==", + "type", + "city" + ], + [ + "in", + "ldir", + "N", + "NE", + "NW", + "W" + ] + ], + "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 + }, + "interactive": true + }, + { + "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-pitch-alignment": "viewport", + "text-field": "{name_en}", + "text-letter-spacing": 0.1, + "text-max-width": 5 + }, + "paint": { + "text-color": "hsl(205, 83%, 88%)" + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "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-pitch-alignment": "viewport", + "text-field": "{name_en}", + "text-letter-spacing": 0.15, + "text-max-width": 5 + }, + "paint": { + "text-color": "hsl(205, 83%, 88%)" + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "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-pitch-alignment": "viewport", + "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%)" + }, + "interactive": true + }, + { + "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%)" + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + }, + { + "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 + }, + "interactive": true + } + ], + "created": "2018-08-16T10:31:44.982Z", + "id": "cjkwfdgzn1fz42rqtvsk6rrmd", + "modified": "2018-08-16T10:31:44.982Z", + "owner": "lukaspaczos", + "visibility": "private", + "draft": false +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java index 8f8a5af3cc..a1f7b4af01 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java @@ -17,7 +17,6 @@ public class SimpleMapActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map_simple); - mapView = (MapView) findViewById(R.id.mapView); mapView.onCreate(savedInstanceState); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java index fdc3826fb1..195aa63edd 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java @@ -30,12 +30,16 @@ import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineColor; */ public class GridSourceActivity extends AppCompatActivity implements OnMapReadyCallback { - private static final String ID_GRID_SOURCE = "grid_source"; - private static final String ID_GRID_LAYER = "grid_layer"; + public static final String ID_GRID_SOURCE = "grid_source"; + public static final String ID_GRID_LAYER = "grid_layer"; private MapView mapView; private MapboxMap mapboxMap; + // public for testing purposes + public CustomGeometrySource source; + public LineLayer layer; + /** * Implementation of GeometryTileProvider that returns features representing a zoom-dependent * grid. @@ -101,11 +105,11 @@ public class GridSourceActivity extends AppCompatActivity implements OnMapReadyC mapboxMap = map; // add source - CustomGeometrySource source = new CustomGeometrySource(ID_GRID_SOURCE, new GridProvider()); + source = new CustomGeometrySource(ID_GRID_SOURCE, new GridProvider()); mapboxMap.addSource(source); // add layer - LineLayer layer = new LineLayer(ID_GRID_LAYER, ID_GRID_SOURCE); + layer = new LineLayer(ID_GRID_LAYER, ID_GRID_SOURCE); layer.setProperties( lineColor(Color.parseColor("#000000")) ); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java index f49d80d704..20fa2e7d52 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java @@ -197,6 +197,9 @@ public class RuntimeStyleActivity extends AppCompatActivity { case R.id.action_numeric_filter: styleNumericFillLayer(); return true; + case R.id.action_bring_water_to_front: + bringWaterToFront(); + return true; default: return super.onOptionsItemSelected(item); } @@ -572,6 +575,16 @@ public class RuntimeStyleActivity extends AppCompatActivity { }, 2000); } + private void bringWaterToFront() { + Layer water = mapboxMap.getLayer("water"); + if (water != null) { + mapboxMap.removeLayer(water); + mapboxMap.addLayerAt(water, mapboxMap.getLayers().size() - 1); + } else { + Toast.makeText(this, "No water layer in this style", Toast.LENGTH_SHORT).show(); + } + } + private static class DefaultCallback implements MapboxMap.CancelableCallback { @Override diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java index 13913de26e..c62eef20f4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java @@ -9,100 +9,119 @@ import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; - +import android.view.ViewGroup; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.mapbox.geojson.Feature; import com.mapbox.geojson.FeatureCollection; import com.mapbox.geojson.Point; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.MapboxMapOptions; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.style.layers.Property; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.utils.BitmapUtils; - -import java.util.Arrays; import java.util.List; -import static com.mapbox.mapboxsdk.style.expressions.Expression.any; import static com.mapbox.mapboxsdk.style.expressions.Expression.get; -import static com.mapbox.mapboxsdk.style.expressions.Expression.has; -import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; -import static com.mapbox.mapboxsdk.style.expressions.Expression.lte; -import static com.mapbox.mapboxsdk.style.expressions.Expression.not; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAnchor; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconColor; +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.iconSize; +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.textIgnorePlacement; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize; /** * Test activity showcasing runtime manipulation of symbol layers. + * <p> + * Showcases the ability to offline render a symbol layer by using a packaged style and fonts from the assets folder. + * </p> */ -public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap.OnMapClickListener { +public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap.OnMapClickListener, OnMapReadyCallback { + + private static final String MARKER_SOURCE = "marker-source"; + private static final String MARKER_LAYER = "marker-layer"; + private static final String MARKER_ICON = "my-layers-image"; - public static final String MARKER_SOURCE = "marker-source"; - public static final String MARKER_LAYER = "marker-layer"; private MapboxMap mapboxMap; private MapView mapView; + private boolean initialFont; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_symbollayer); - mapView = (MapView) findViewById(R.id.mapView); + // Create map configuration + MapboxMapOptions mapboxMapOptions = new MapboxMapOptions(); + mapboxMapOptions.camera(new CameraPosition.Builder().target( + new LatLng(52.35273, 4.91638)) + .zoom(13) + .build() + ); + mapboxMapOptions.styleUrl("asset://streets.json"); + + // Create map programmatically, add to view hierarchy + mapView = new MapView(this, mapboxMapOptions); + mapView.getMapAsync(this); mapView.onCreate(savedInstanceState); - mapView.getMapAsync(map -> { - mapboxMap = map; - - // Add a sdf image for the makers - Drawable icLayersDrawable = getResources().getDrawable(R.drawable.ic_layers); - Bitmap icLayersBitmap = BitmapUtils.getBitmapFromDrawable(icLayersDrawable); - mapboxMap.addImage( - "my-layers-image", - icLayersBitmap, - true - ); - - // Add a source - FeatureCollection markers = FeatureCollection.fromFeatures(new Feature[] { - Feature.fromGeometry(Point.fromLngLat(4.91638, 52.35673), featureProperties("Marker 1")), - Feature.fromGeometry(Point.fromLngLat(4.91638, 52.34673), featureProperties("Marker 2")) - }); - mapboxMap.addSource(new GeoJsonSource(MARKER_SOURCE, markers)); - - // Add the symbol-layer - mapboxMap.addLayer( - new SymbolLayer(MARKER_LAYER, MARKER_SOURCE) - .withProperties( - iconImage("my-layers-image"), - iconAllowOverlap(true), - iconAnchor(Property.ICON_ANCHOR_BOTTOM), - textField(get("title")), - iconColor(Color.RED), - textColor(Color.RED), - textAnchor(Property.TEXT_ANCHOR_TOP), - textSize(10f) - ).withFilter((any(not(has("price")), lte(get("price"), literal(25))))) - ); - - // Show - mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.35273, 4.91638), 14)); - - // Set a click-listener so we can manipulate the map - mapboxMap.setOnMapClickListener(SymbolLayerActivity.this); + ((ViewGroup) findViewById(R.id.container)).addView(mapView); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + + // Add a sdf image for the makers + Drawable icLayersDrawable = getResources().getDrawable(R.drawable.ic_layers); + Bitmap icLayersBitmap = BitmapUtils.getBitmapFromDrawable(icLayersDrawable); + mapboxMap.addImage( + MARKER_ICON, + icLayersBitmap, + true + ); + + // Add a source + FeatureCollection markers = FeatureCollection.fromFeatures(new Feature[] { + Feature.fromGeometry(Point.fromLngLat(4.91638, 52.35673), featureProperties("Marker 1")), + Feature.fromGeometry(Point.fromLngLat(4.91638, 52.34673), featureProperties("Marker 2")) }); + mapboxMap.addSource(new GeoJsonSource(MARKER_SOURCE, markers)); + + // Add the symbol-layer + mapboxMap.addLayer( + new SymbolLayer(MARKER_LAYER, MARKER_SOURCE) + .withProperties( + iconImage(MARKER_ICON), + iconIgnorePlacement(true), + iconAllowOverlap(true), + iconAnchor(Property.ICON_ANCHOR_BOTTOM), + iconColor(Color.RED), + textField(get("title")), + textFont(new String[] {"DIN Offc Pro Regular", "Arial Unicode MS Regular"}), + textColor(Color.RED), + textAllowOverlap(true), + textIgnorePlacement(true), + textAnchor(Property.TEXT_ANCHOR_TOP), + textSize(10f) + ) + ); + + // Set a click-listener so we can manipulate the map + mapboxMap.addOnMapClickListener(SymbolLayerActivity.this); } @Override @@ -132,13 +151,12 @@ public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap. private void toggleTextFont() { SymbolLayer layer = mapboxMap.getLayerAs(MARKER_LAYER); - - String[] fonts = layer.getTextFont().getValue(); - if (fonts == null || fonts.length == 0 || Arrays.asList(fonts).contains("Arial Unicode MS Regular")) { + if (initialFont) { layer.setProperties(textFont(new String[] {"DIN Offc Pro Bold", "Arial Unicode MS Bold"})); } else { layer.setProperties(textFont(new String[] {"DIN Offc Pro Medium", "Arial Unicode MS Regular"})); } + initialFont = !initialFont; } private JsonObject featureProperties(String title) { @@ -186,6 +204,9 @@ public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap. @Override public void onDestroy() { super.onDestroy(); + if (mapboxMap != null) { + mapboxMap.removeOnMapClickListener(this); + } mapView.onDestroy(); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java index 15da018b0e..3a62e39173 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java @@ -2,10 +2,14 @@ package com.mapbox.mapboxsdk.testapp.activity.textureview; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; +import android.view.ViewGroup; import android.widget.ImageView; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.MapboxMapOptions; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; @@ -30,23 +34,30 @@ public class TextureViewTransparentBackgroundActivity extends AppCompatActivity } private void setupBackground() { - ImageView imageView = (ImageView) findViewById(R.id.imageView); + ImageView imageView = findViewById(R.id.imageView); imageView.setImageResource(R.drawable.water); imageView.setScaleType(ImageView.ScaleType.FIT_XY); } private void setupMapView(Bundle savedInstanceState) { - mapView = (MapView) findViewById(R.id.mapView); - mapView.onCreate(savedInstanceState); - mapView.getMapAsync(map -> { - mapboxMap = map; - - try { - map.setStyleJson(ResourceUtils.readRawResource(getApplicationContext(), R.raw.no_bg_style)); - } catch (IOException exception) { - Timber.e(exception); - } - }); + try { + MapboxMapOptions mapboxMapOptions = new MapboxMapOptions(); + mapboxMapOptions.styleJson(ResourceUtils.readRawResource(this, R.raw.no_bg_style)); + mapboxMapOptions.translucentTextureSurface(true); + mapboxMapOptions.textureMode(true); + mapboxMapOptions.camera(new CameraPosition.Builder() + .zoom(2) + .target(new LatLng(48.507879, 8.363795)) + .build() + ); + + mapView = new MapView(this, mapboxMapOptions); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> mapboxMap = map); + ((ViewGroup) findViewById(R.id.coordinator_layout)).addView(mapView); + } catch (IOException exception) { + Timber.e(exception); + } } @Override @@ -90,5 +101,4 @@ public class TextureViewTransparentBackgroundActivity extends AppCompatActivity super.onLowMemory(); mapView.onLowMemory(); } - }
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbollayer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbollayer.xml index 9b88994f1c..62bcc3b34f 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbollayer.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbollayer.xml @@ -1,15 +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" - android:orientation="vertical"> - - <com.mapbox.mapboxsdk.maps.MapView - android:id="@id/mapView" +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/> + android:orientation="vertical"/> -</RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml index 3b9ee71359..096d44e223 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/coordinator_layout" android:layout_width="match_parent" android:layout_height="match_parent" @@ -13,16 +12,5 @@ android:layout_height="match_parent" android:contentDescription="@null"/> - <com.mapbox.mapboxsdk.maps.MapView - android:id="@+id/mapView" - android:layout_width="match_parent" - android:layout_height="match_parent" - app:mapbox_cameraTargetLat="48.507879" - app:mapbox_cameraTargetLng="8.363795" - app:mapbox_cameraZoom="2" - app:mapbox_renderTextureMode="true" - app:mapbox_renderTextureTranslucentSurface="true" - app:mapbox_styleUrl="mapbox://styles/agerace-globant/cja02de7193b02suvy4gpbt2c"/> - </android.support.design.widget.CoordinatorLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml index 5c77179272..e3d7c8cfa0 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml @@ -66,4 +66,8 @@ android:id="@+id/action_numeric_filter" android:title="@string/apply_numeric_fill_filter" mapbox:showAsAction="never" /> + <item + android:id="@+id/action_bring_water_to_front" + android:title="@string/bring_water_to_front" + mapbox:showAsAction="never" /> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json index 964eefc319..9f2b419497 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json @@ -2,7 +2,7 @@ "version": 8, "name": "Land", "metadata": { - "mapbox:autocomposite": true, + "mapbox:autocomposite": true }, "sources": { "composite": { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml index 6d94c03529..e3cdc06dc1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml @@ -79,6 +79,7 @@ <string name="apply_filtered_fill">Apply filtered fill</string> <string name="apply_filtered_line">Apply filtered line</string> <string name="apply_numeric_fill_filter">Apply numeric fill filter</string> + <string name="bring_water_to_front">Bring water to front</string> <string name="toggle_text_size">Toggle text size</string> <string name="toggle_text_field_contents">Toggle text field contents</string> <string name="toggle_text_font">Toggle text font</string> diff --git a/platform/android/config.cmake b/platform/android/config.cmake index 001a45e613..84c508dd73 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -1,5 +1,4 @@ add_definitions(-DMBGL_USE_GLES2=1) -include(cmake/test-files.cmake) include(cmake/nunicode.cmake) # Build thin archives. @@ -34,56 +33,7 @@ set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWIT ## mbgl core ## macro(mbgl_platform_core) - target_sources(mbgl-core - # Loop - PRIVATE platform/android/src/async_task.cpp - PRIVATE platform/android/src/run_loop.cpp - PRIVATE platform/android/src/run_loop_impl.hpp - PRIVATE platform/android/src/timer.cpp - - # Misc - PRIVATE platform/android/src/text/collator.cpp - PRIVATE platform/android/src/text/collator_jni.hpp - PRIVATE platform/android/src/text/local_glyph_rasterizer.cpp - PRIVATE platform/android/src/text/local_glyph_rasterizer_jni.hpp - PRIVATE platform/android/src/logging_android.cpp - PRIVATE platform/android/src/thread.cpp - PRIVATE platform/default/string_stdlib.cpp - PRIVATE platform/default/bidi.cpp - PRIVATE platform/default/thread_local.cpp - PRIVATE platform/default/unaccent.cpp - PRIVATE platform/default/unaccent.hpp - PRIVATE platform/default/utf.cpp - - # Image handling - PRIVATE platform/default/png_writer.cpp - PRIVATE platform/android/src/bitmap.cpp - PRIVATE platform/android/src/bitmap.hpp - PRIVATE platform/android/src/bitmap_factory.cpp - PRIVATE platform/android/src/bitmap_factory.hpp - PRIVATE platform/android/src/image.cpp - - # Thread pool - PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp - PRIVATE platform/default/mbgl/util/shared_thread_pool.hpp - PRIVATE platform/default/mbgl/util/default_thread_pool.cpp - PRIVATE platform/default/mbgl/util/default_thread_pool.hpp - - # Rendering - PRIVATE platform/android/src/android_renderer_backend.cpp - PRIVATE platform/android/src/android_renderer_backend.hpp - PRIVATE platform/android/src/android_renderer_frontend.cpp - PRIVATE platform/android/src/android_renderer_frontend.hpp - - # Snapshots (core) - PRIVATE platform/default/mbgl/gl/headless_backend.cpp - PRIVATE platform/default/mbgl/gl/headless_backend.hpp - PRIVATE platform/default/mbgl/gl/headless_frontend.cpp - PRIVATE platform/default/mbgl/gl/headless_frontend.hpp - PRIVATE platform/default/mbgl/map/map_snapshotter.cpp - PRIVATE platform/default/mbgl/map/map_snapshotter.hpp - PRIVATE platform/linux/src/headless_backend_egl.cpp - ) + target_sources_from_file(mbgl-core PRIVATE platform/android/core-files.txt) target_include_directories(mbgl-core PUBLIC platform/default @@ -97,6 +47,7 @@ macro(mbgl_platform_core) target_link_libraries(mbgl-core PRIVATE nunicode + PUBLIC expected PUBLIC -llog PUBLIC -landroid PUBLIC -ljnigraphics @@ -110,16 +61,7 @@ endmacro() macro(mbgl_filesource) - target_sources(mbgl-filesource - # File source - PRIVATE platform/android/src/http_file_source.cpp - PRIVATE platform/android/src/asset_manager.hpp - PRIVATE platform/android/src/asset_manager_file_source.cpp - PRIVATE platform/android/src/asset_manager_file_source.hpp - - # Database - PRIVATE platform/default/sqlite3.cpp - ) + target_sources_from_file(mbgl-filesource PRIVATE platform/android/filesource-files.txt) target_add_mason_package(mbgl-filesource PUBLIC sqlite) target_add_mason_package(mbgl-filesource PUBLIC jni.hpp) @@ -132,190 +74,6 @@ macro(mbgl_filesource) ) endmacro() - -## Main library ## - -add_library(mbgl-android STATIC - # Conversion C++ -> Java - platform/android/src/conversion/constant.hpp - platform/android/src/conversion/conversion.hpp - platform/android/src/style/conversion/property_expression.hpp - platform/android/src/style/conversion/property_value.hpp - platform/android/src/style/conversion/types.hpp - platform/android/src/style/conversion/types_string_values.hpp - platform/android/src/map/camera_position.cpp - platform/android/src/map/camera_position.hpp - platform/android/src/map/image.cpp - platform/android/src/map/image.hpp - - # Style conversion Java -> C++ - platform/android/src/style/android_conversion.hpp - platform/android/src/style/value.cpp - platform/android/src/style/value.hpp - platform/android/src/style/conversion/url_or_tileset.hpp - - # Style - platform/android/src/style/transition_options.cpp - platform/android/src/style/transition_options.hpp - platform/android/src/style/layers/background_layer.cpp - platform/android/src/style/layers/background_layer.hpp - platform/android/src/style/layers/circle_layer.cpp - platform/android/src/style/layers/circle_layer.hpp - platform/android/src/style/layers/custom_layer.cpp - platform/android/src/style/layers/custom_layer.hpp - platform/android/src/style/layers/fill_extrusion_layer.cpp - platform/android/src/style/layers/fill_extrusion_layer.hpp - platform/android/src/style/layers/fill_layer.cpp - platform/android/src/style/layers/fill_layer.hpp - platform/android/src/style/layers/heatmap_layer.cpp - platform/android/src/style/layers/heatmap_layer.hpp - platform/android/src/style/layers/hillshade_layer.cpp - platform/android/src/style/layers/hillshade_layer.hpp - platform/android/src/style/layers/layer.cpp - platform/android/src/style/layers/layer.hpp - platform/android/src/style/layers/layers.cpp - platform/android/src/style/layers/layers.hpp - platform/android/src/style/layers/line_layer.cpp - platform/android/src/style/layers/line_layer.hpp - platform/android/src/style/layers/raster_layer.cpp - platform/android/src/style/layers/raster_layer.hpp - platform/android/src/style/layers/symbol_layer.cpp - platform/android/src/style/layers/symbol_layer.hpp - platform/android/src/style/layers/unknown_layer.cpp - platform/android/src/style/layers/unknown_layer.hpp - platform/android/src/style/sources/geojson_source.cpp - platform/android/src/style/sources/geojson_source.hpp - platform/android/src/style/sources/custom_geometry_source.cpp - platform/android/src/style/sources/custom_geometry_source.hpp - platform/android/src/style/sources/source.cpp - platform/android/src/style/sources/source.hpp - platform/android/src/style/sources/raster_source.cpp - platform/android/src/style/sources/raster_source.hpp - platform/android/src/style/sources/unknown_source.cpp - platform/android/src/style/sources/unknown_source.hpp - platform/android/src/style/sources/vector_source.cpp - platform/android/src/style/sources/vector_source.hpp - platform/android/src/style/sources/image_source.hpp - platform/android/src/style/sources/image_source.cpp - platform/android/src/style/sources/raster_dem_source.cpp - platform/android/src/style/sources/raster_dem_source.hpp - platform/android/src/style/position.cpp - platform/android/src/style/position.hpp - platform/android/src/style/light.cpp - platform/android/src/style/light.hpp - - # FileSource holder - platform/android/src/file_source.cpp - platform/android/src/file_source.hpp - - # Connectivity - platform/android/src/connectivity_listener.cpp - platform/android/src/connectivity_listener.hpp - - # Native map - platform/android/src/native_map_view.cpp - platform/android/src/native_map_view.hpp - platform/android/src/map_renderer.cpp - platform/android/src/map_renderer.hpp - platform/android/src/map_renderer_runnable.cpp - platform/android/src/map_renderer_runnable.hpp - - # Java core classes - platform/android/src/java/lang.cpp - platform/android/src/java/lang.hpp - platform/android/src/java/util.cpp - platform/android/src/java/util.hpp - - # Graphics - platform/android/src/graphics/pointf.cpp - platform/android/src/graphics/pointf.hpp - platform/android/src/graphics/rectf.cpp - platform/android/src/graphics/rectf.hpp - - # GeoJSON - platform/android/src/geojson/feature.cpp - platform/android/src/geojson/feature.hpp - platform/android/src/geojson/feature_collection.cpp - platform/android/src/geojson/feature_collection.hpp - platform/android/src/geojson/geometry.cpp - platform/android/src/geojson/geometry.hpp - platform/android/src/geojson/geometry_collection.cpp - platform/android/src/geojson/geometry_collection.hpp - platform/android/src/geojson/line_string.cpp - platform/android/src/geojson/line_string.hpp - platform/android/src/geojson/multi_line_string.cpp - platform/android/src/geojson/multi_line_string.hpp - platform/android/src/geojson/multi_point.cpp - platform/android/src/geojson/multi_point.hpp - platform/android/src/geojson/multi_polygon.cpp - platform/android/src/geojson/multi_polygon.hpp - platform/android/src/geojson/point.cpp - platform/android/src/geojson/point.hpp - platform/android/src/geojson/polygon.cpp - platform/android/src/geojson/polygon.hpp - - # Geometry - platform/android/src/geometry/lat_lng.cpp - platform/android/src/geometry/lat_lng.hpp - platform/android/src/geometry/lat_lng_bounds.cpp - platform/android/src/geometry/lat_lng_bounds.hpp - platform/android/src/geometry/lat_lng_quad.cpp - platform/android/src/geometry/lat_lng_quad.hpp - platform/android/src/geometry/projected_meters.cpp - platform/android/src/geometry/projected_meters.hpp - - # GSon - platform/android/src/gson/json_array.cpp - platform/android/src/gson/json_array.hpp - platform/android/src/gson/json_element.cpp - platform/android/src/gson/json_element.hpp - platform/android/src/gson/json_object.cpp - platform/android/src/gson/json_object.hpp - platform/android/src/gson/json_primitive.cpp - platform/android/src/gson/json_primitive.hpp - - # Annotation - platform/android/src/annotation/marker.cpp - platform/android/src/annotation/marker.hpp - platform/android/src/annotation/polygon.cpp - platform/android/src/annotation/polygon.hpp - platform/android/src/annotation/polyline.cpp - platform/android/src/annotation/polyline.hpp - - # Offline - platform/android/src/offline/offline_manager.cpp - platform/android/src/offline/offline_manager.hpp - platform/android/src/offline/offline_region.cpp - platform/android/src/offline/offline_region.hpp - platform/android/src/offline/offline_region_definition.cpp - platform/android/src/offline/offline_region_definition.hpp - platform/android/src/offline/offline_region_error.cpp - platform/android/src/offline/offline_region_error.hpp - platform/android/src/offline/offline_region_status.cpp - platform/android/src/offline/offline_region_status.hpp - - # Snapshots (SDK) - platform/android/src/snapshotter/map_snapshotter.cpp - platform/android/src/snapshotter/map_snapshotter.hpp - platform/android/src/snapshotter/map_snapshot.cpp - platform/android/src/snapshotter/map_snapshot.hpp - - # Main jni bindings - platform/android/src/attach_env.cpp - platform/android/src/attach_env.hpp - platform/android/src/java_types.cpp - platform/android/src/java_types.hpp - - # Main entry point - platform/android/src/jni.hpp - platform/android/src/jni.cpp -) - -target_link_libraries(mbgl-android - PUBLIC mbgl-filesource - PUBLIC mbgl-core -) - ## Shared library add_library(mapbox-gl SHARED @@ -323,7 +81,8 @@ add_library(mapbox-gl SHARED ) target_link_libraries(mapbox-gl - PRIVATE mbgl-android + PRIVATE mbgl-core + PRIVATE mbgl-filesource ) ## Test library ## @@ -342,7 +101,8 @@ macro(mbgl_platform_test) ) target_link_libraries(mbgl-test - PRIVATE mbgl-android + PRIVATE mbgl-core + PRIVATE mbgl-filesource ) endmacro() @@ -352,6 +112,11 @@ add_library(example-custom-layer SHARED platform/android/src/example_custom_layer.cpp ) +target_include_directories(example-custom-layer + PRIVATE include +) + target_link_libraries(example-custom-layer - PRIVATE mbgl-core + PRIVATE -llog + PRIVATE -lGLESv2 ) diff --git a/platform/android/core-files.txt b/platform/android/core-files.txt new file mode 100644 index 0000000000..cdcffb857f --- /dev/null +++ b/platform/android/core-files.txt @@ -0,0 +1,215 @@ +# Loop +platform/android/src/async_task.cpp +platform/android/src/run_loop.cpp +platform/android/src/run_loop_impl.hpp +platform/android/src/timer.cpp + +# Misc +platform/android/src/text/collator.cpp +platform/android/src/text/collator_jni.hpp +platform/android/src/text/local_glyph_rasterizer.cpp +platform/android/src/text/local_glyph_rasterizer_jni.hpp +platform/android/src/logging_android.cpp +platform/android/src/thread.cpp +platform/default/string_stdlib.cpp +platform/default/bidi.cpp +platform/default/thread_local.cpp +platform/default/unaccent.cpp +platform/default/unaccent.hpp +platform/default/utf.cpp + +# Image handling +platform/default/png_writer.cpp +platform/android/src/bitmap.cpp +platform/android/src/bitmap.hpp +platform/android/src/bitmap_factory.cpp +platform/android/src/bitmap_factory.hpp +platform/android/src/image.cpp + +# Thread pool +platform/default/mbgl/util/shared_thread_pool.cpp +platform/default/mbgl/util/shared_thread_pool.hpp +platform/default/mbgl/util/default_thread_pool.cpp +platform/default/mbgl/util/default_thread_pool.hpp + +# Rendering +platform/android/src/android_renderer_backend.cpp +platform/android/src/android_renderer_backend.hpp +platform/android/src/android_renderer_frontend.cpp +platform/android/src/android_renderer_frontend.hpp + +# Snapshots (core) +platform/default/mbgl/gl/headless_backend.cpp +platform/default/mbgl/gl/headless_backend.hpp +platform/default/mbgl/gl/headless_frontend.cpp +platform/default/mbgl/gl/headless_frontend.hpp +platform/default/mbgl/map/map_snapshotter.cpp +platform/default/mbgl/map/map_snapshotter.hpp +platform/linux/src/headless_backend_egl.cpp + +# Conversion C++ -> Java +platform/android/src/conversion/collection.cpp +platform/android/src/conversion/collection.hpp +platform/android/src/conversion/color.cpp +platform/android/src/conversion/color.hpp +platform/android/src/conversion/constant.cpp +platform/android/src/conversion/constant.hpp +platform/android/src/conversion/conversion.hpp +platform/android/src/geojson/conversion/feature.cpp +platform/android/src/geojson/conversion/feature.hpp +platform/android/src/style/conversion/filter.cpp +platform/android/src/style/conversion/filter.hpp +platform/android/src/style/conversion/position.cpp +platform/android/src/style/conversion/position.hpp +platform/android/src/style/conversion/property_expression.hpp +platform/android/src/style/conversion/property_value.hpp +platform/android/src/style/conversion/transition_options.cpp +platform/android/src/style/conversion/transition_options.hpp +platform/android/src/style/conversion/url_or_tileset.cpp +platform/android/src/style/conversion/url_or_tileset.hpp +platform/android/src/map/camera_position.cpp +platform/android/src/map/camera_position.hpp +platform/android/src/map/image.cpp +platform/android/src/map/image.hpp + +# Style conversion Java -> C++ +platform/android/src/style/android_conversion.hpp +platform/android/src/style/value.cpp +platform/android/src/style/value.hpp +platform/android/src/style/conversion/url_or_tileset.hpp + +# Style +platform/android/src/style/transition_options.cpp +platform/android/src/style/transition_options.hpp +platform/android/src/style/layers/background_layer.cpp +platform/android/src/style/layers/background_layer.hpp +platform/android/src/style/layers/circle_layer.cpp +platform/android/src/style/layers/circle_layer.hpp +platform/android/src/style/layers/custom_layer.cpp +platform/android/src/style/layers/custom_layer.hpp +platform/android/src/style/layers/fill_extrusion_layer.cpp +platform/android/src/style/layers/fill_extrusion_layer.hpp +platform/android/src/style/layers/fill_layer.cpp +platform/android/src/style/layers/fill_layer.hpp +platform/android/src/style/layers/heatmap_layer.cpp +platform/android/src/style/layers/heatmap_layer.hpp +platform/android/src/style/layers/hillshade_layer.cpp +platform/android/src/style/layers/hillshade_layer.hpp +platform/android/src/style/layers/layer.cpp +platform/android/src/style/layers/layer.hpp +platform/android/src/style/layers/layers.cpp +platform/android/src/style/layers/layers.hpp +platform/android/src/style/layers/line_layer.cpp +platform/android/src/style/layers/line_layer.hpp +platform/android/src/style/layers/raster_layer.cpp +platform/android/src/style/layers/raster_layer.hpp +platform/android/src/style/layers/symbol_layer.cpp +platform/android/src/style/layers/symbol_layer.hpp +platform/android/src/style/layers/unknown_layer.cpp +platform/android/src/style/layers/unknown_layer.hpp +platform/android/src/style/sources/geojson_source.cpp +platform/android/src/style/sources/geojson_source.hpp +platform/android/src/style/sources/custom_geometry_source.cpp +platform/android/src/style/sources/custom_geometry_source.hpp +platform/android/src/style/sources/source.cpp +platform/android/src/style/sources/source.hpp +platform/android/src/style/sources/raster_source.cpp +platform/android/src/style/sources/raster_source.hpp +platform/android/src/style/sources/unknown_source.cpp +platform/android/src/style/sources/unknown_source.hpp +platform/android/src/style/sources/vector_source.cpp +platform/android/src/style/sources/vector_source.hpp +platform/android/src/style/sources/image_source.hpp +platform/android/src/style/sources/image_source.cpp +platform/android/src/style/sources/raster_dem_source.cpp +platform/android/src/style/sources/raster_dem_source.hpp +platform/android/src/style/position.cpp +platform/android/src/style/position.hpp +platform/android/src/style/light.cpp +platform/android/src/style/light.hpp + +# Native map +platform/android/src/native_map_view.cpp +platform/android/src/native_map_view.hpp +platform/android/src/map_renderer.cpp +platform/android/src/map_renderer.hpp +platform/android/src/map_renderer_runnable.cpp +platform/android/src/map_renderer_runnable.hpp + +# Java core classes +platform/android/src/java/lang.cpp +platform/android/src/java/lang.hpp +platform/android/src/java/util.cpp +platform/android/src/java/util.hpp + +# Graphics +platform/android/src/graphics/pointf.cpp +platform/android/src/graphics/pointf.hpp +platform/android/src/graphics/rectf.cpp +platform/android/src/graphics/rectf.hpp + +# GeoJSON +platform/android/src/geojson/feature.cpp +platform/android/src/geojson/feature.hpp +platform/android/src/geojson/feature_collection.cpp +platform/android/src/geojson/feature_collection.hpp +platform/android/src/geojson/geometry.cpp +platform/android/src/geojson/geometry.hpp +platform/android/src/geojson/geometry_collection.cpp +platform/android/src/geojson/geometry_collection.hpp +platform/android/src/geojson/line_string.cpp +platform/android/src/geojson/line_string.hpp +platform/android/src/geojson/multi_line_string.cpp +platform/android/src/geojson/multi_line_string.hpp +platform/android/src/geojson/multi_point.cpp +platform/android/src/geojson/multi_point.hpp +platform/android/src/geojson/multi_polygon.cpp +platform/android/src/geojson/multi_polygon.hpp +platform/android/src/geojson/point.cpp +platform/android/src/geojson/point.hpp +platform/android/src/geojson/polygon.cpp +platform/android/src/geojson/polygon.hpp + +# Geometry +platform/android/src/geometry/lat_lng.cpp +platform/android/src/geometry/lat_lng.hpp +platform/android/src/geometry/lat_lng_bounds.cpp +platform/android/src/geometry/lat_lng_bounds.hpp +platform/android/src/geometry/lat_lng_quad.cpp +platform/android/src/geometry/lat_lng_quad.hpp +platform/android/src/geometry/projected_meters.cpp +platform/android/src/geometry/projected_meters.hpp + +# GSon +platform/android/src/gson/json_array.cpp +platform/android/src/gson/json_array.hpp +platform/android/src/gson/json_element.cpp +platform/android/src/gson/json_element.hpp +platform/android/src/gson/json_object.cpp +platform/android/src/gson/json_object.hpp +platform/android/src/gson/json_primitive.cpp +platform/android/src/gson/json_primitive.hpp + +# Annotation +platform/android/src/annotation/marker.cpp +platform/android/src/annotation/marker.hpp +platform/android/src/annotation/polygon.cpp +platform/android/src/annotation/polygon.hpp +platform/android/src/annotation/polyline.cpp +platform/android/src/annotation/polyline.hpp + +# Snapshots (SDK) +platform/android/src/snapshotter/map_snapshotter.cpp +platform/android/src/snapshotter/map_snapshotter.hpp +platform/android/src/snapshotter/map_snapshot.cpp +platform/android/src/snapshotter/map_snapshot.hpp + +# Main jni bindings +platform/android/src/attach_env.cpp +platform/android/src/attach_env.hpp +platform/android/src/java_types.cpp +platform/android/src/java_types.hpp + +# Main entry point +platform/android/src/jni.hpp +platform/android/src/jni.cpp diff --git a/platform/android/filesource-files.txt b/platform/android/filesource-files.txt new file mode 100644 index 0000000000..c88db6c725 --- /dev/null +++ b/platform/android/filesource-files.txt @@ -0,0 +1,28 @@ +# File source +platform/android/src/http_file_source.cpp +platform/android/src/asset_manager.hpp +platform/android/src/asset_manager_file_source.cpp +platform/android/src/asset_manager_file_source.hpp + +# FileSource holder +platform/android/src/file_source.cpp +platform/android/src/file_source.hpp + +# Connectivity +platform/android/src/connectivity_listener.cpp +platform/android/src/connectivity_listener.hpp + +# Offline +platform/android/src/offline/offline_manager.cpp +platform/android/src/offline/offline_manager.hpp +platform/android/src/offline/offline_region.cpp +platform/android/src/offline/offline_region.hpp +platform/android/src/offline/offline_region_definition.cpp +platform/android/src/offline/offline_region_definition.hpp +platform/android/src/offline/offline_region_error.cpp +platform/android/src/offline/offline_region_error.hpp +platform/android/src/offline/offline_region_status.cpp +platform/android/src/offline/offline_region_status.hpp + +# Database +platform/default/sqlite3.cpp diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 8589c18112..303dd2f4b4 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -8,8 +8,8 @@ ext { ] versions = [ - mapboxServices : '3.4.0', - mapboxTelemetry: '3.1.4', + mapboxServices : '3.4.1', + mapboxTelemetry: '3.1.5', mapboxGestures : '0.2.0', supportLib : '27.1.1', espresso : '3.0.2', diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json index 36d8d36e68..e4418bdc53 100644 --- a/platform/android/scripts/exclude-activity-gen.json +++ b/platform/android/scripts/exclude-activity-gen.json @@ -35,5 +35,6 @@ "SymbolGeneratorActivity", "TextureViewTransparentBackgroundActivity", "SimpleMapActivity", - "RenderTestActivity" + "RenderTestActivity", + "SymbolLayerActivity" ] diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index 888b9fca30..0825ef1c26 100755 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -55,6 +55,7 @@ global.propertyType = function propertyType(property) { return 'Boolean'; case 'number': return 'Float'; + case 'formatted': case 'string': return 'String'; case 'enum': @@ -74,6 +75,7 @@ global.propertyJavaType = function propertyType(property) { return 'boolean'; case 'number': return 'float'; + case 'formatted': case 'string': return 'String'; case 'enum': @@ -121,6 +123,7 @@ global.propertyNativeType = function (property) { return 'bool'; case 'number': return 'float'; + case 'formatted': case 'string': return 'std::string'; case 'enum': @@ -155,6 +158,7 @@ global.defaultExpressionJava = function(property) { return 'boolean'; case 'number': return 'number'; + case 'formatted': case 'string': return "string"; case 'enum': @@ -179,6 +183,7 @@ global.defaultValueJava = function(property) { return 'true'; case 'number': return '0.3f'; + case 'formatted': case 'string': return '"' + property['default'] + '"'; case 'enum': @@ -187,6 +192,7 @@ global.defaultValueJava = function(property) { return '"rgba(0, 0, 0, 1)"'; case 'array': switch (property.value) { + case 'formatted': case 'string': return '[' + property['default'] + "]"; case 'number': @@ -301,6 +307,7 @@ global.evaluatedType = function (property) { return 'bool'; case 'number': return 'float'; + case 'formatted': case 'string': return 'std::string'; case 'enum': @@ -363,20 +370,3 @@ writeIfModified( `platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java`, enumPropertyJavaTemplate({properties: enumProperties}) ); - -// De-duplicate enum properties before processing jni property templates -const enumPropertiesDeDup = _(enumProperties).uniqBy(global.propertyNativeType).value(); - -// JNI Enum property conversion templates -const enumPropertyHppTypeStringValueTemplate = ejs.compile(fs.readFileSync('platform/android/src/style/conversion/types_string_values.hpp.ejs', 'utf8'), {strict: true}); -writeIfModified( - `platform/android/src/style/conversion/types_string_values.hpp`, - enumPropertyHppTypeStringValueTemplate({properties: enumPropertiesDeDup}) -); - -// JNI property value types conversion templates -const enumPropertyHppTypeTemplate = ejs.compile(fs.readFileSync('platform/android/src/style/conversion/types.hpp.ejs', 'utf8'), {strict: true}); -writeIfModified( - `platform/android/src/style/conversion/types.hpp`, - enumPropertyHppTypeTemplate({properties: enumPropertiesDeDup}) -); diff --git a/platform/android/src/android_renderer_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp index 2a03d9de9e..8b4a25a4d9 100644 --- a/platform/android/src/android_renderer_frontend.cpp +++ b/platform/android/src/android_renderer_frontend.cpp @@ -4,6 +4,7 @@ #include <mbgl/renderer/renderer.hpp> #include <mbgl/renderer/renderer_observer.hpp> #include <mbgl/storage/file_source.hpp> +#include <mbgl/util/async_task.hpp> #include <mbgl/util/thread.hpp> #include <mbgl/util/run_loop.hpp> @@ -56,7 +57,11 @@ private: AndroidRendererFrontend::AndroidRendererFrontend(MapRenderer& mapRenderer_) : mapRenderer(mapRenderer_) - , mapRunLoop(util::RunLoop::Get()) { + , mapRunLoop(util::RunLoop::Get()) + , updateAsyncTask(std::make_unique<util::AsyncTask>([this]() { + mapRenderer.update(std::move(updateParams)); + mapRenderer.requestRender(); + })) { } AndroidRendererFrontend::~AndroidRendererFrontend() = default; @@ -73,8 +78,8 @@ void AndroidRendererFrontend::setObserver(RendererObserver& observer) { } void AndroidRendererFrontend::update(std::shared_ptr<UpdateParameters> params) { - mapRenderer.update(std::move(params)); - mapRenderer.requestRender(); + updateParams = std::move(params); + updateAsyncTask->send(); } void AndroidRendererFrontend::reduceMemoryUse() { diff --git a/platform/android/src/android_renderer_frontend.hpp b/platform/android/src/android_renderer_frontend.hpp index b61904e388..9bd64e4819 100644 --- a/platform/android/src/android_renderer_frontend.hpp +++ b/platform/android/src/android_renderer_frontend.hpp @@ -18,6 +18,12 @@ namespace mbgl { class RenderedQueryOptions; class SourceQueryOptions; +namespace util { + +class AsyncTask; + +} // namespace util + namespace android { class AndroidRendererFrontend : public RendererFrontend { @@ -44,6 +50,8 @@ public: private: MapRenderer& mapRenderer; util::RunLoop* mapRunLoop; + std::unique_ptr<util::AsyncTask> updateAsyncTask; + std::shared_ptr<UpdateParameters> updateParams; }; } // namespace android diff --git a/platform/android/src/conversion/collection.cpp b/platform/android/src/conversion/collection.cpp new file mode 100644 index 0000000000..27b614e8cd --- /dev/null +++ b/platform/android/src/conversion/collection.cpp @@ -0,0 +1,24 @@ +#include "collection.hpp" +#include "constant.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +std::vector<std::string> toVector(JNIEnv& env, jni::Array<jni::String> array) { + std::size_t len = array.Length(env); + std::vector<std::string> vector; + vector.reserve(len); + + for (std::size_t i = 0; i < len; i++) { + jni::String jstr = array.Get(env, i); + vector.push_back(*convert<std::string, jni::String>(env, jstr)); + jni::DeleteLocalRef(env, jstr); + } + + return vector; +} + +} +} +} diff --git a/platform/android/src/conversion/collection.hpp b/platform/android/src/conversion/collection.hpp index 2b953e73f4..bb8941c984 100644 --- a/platform/android/src/conversion/collection.hpp +++ b/platform/android/src/conversion/collection.hpp @@ -1,9 +1,7 @@ #pragma once #include "conversion.hpp" -#include "constant.hpp" -#include <mbgl/util/optional.hpp> #include <jni/jni.hpp> #include <vector> @@ -12,46 +10,7 @@ namespace mbgl { namespace android { namespace conversion { -/** - * Convert jarray -> ArrayList - */ -template <class T> -inline jni::jobject* toArrayList(JNIEnv& env, jni::jarray<T>& array) { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Arrays")).release(); - static jni::jmethodID* asList = &jni::GetStaticMethodID(env, *javaClass, "asList", "([Ljava/lang/Object;)Ljava/util/List;"); - return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *asList, array)); -} - -// Java -> C++ - - -inline std::vector<std::string> toVector(JNIEnv& env, jni::jarray<jni::jobject>& array) { - std::vector<std::string> vector; - std::size_t len = jni::GetArrayLength(env, array); - vector.reserve(len); - - for (std::size_t i = 0; i < len; i++) { - jni::jstring* jstr = reinterpret_cast<jni::jstring*>(jni::GetObjectArrayElement(env, array, i)); - vector.push_back(*convert<std::string, jni::String>(env, jni::String(jstr))); - jni::DeleteLocalRef(env, jstr); - } - - return vector; -} - -inline std::vector<std::string> toVector(JNIEnv& env, jni::Array<jni::String> array) { - std::size_t len = array.Length(env); - std::vector<std::string> vector; - vector.reserve(len); - - for (std::size_t i = 0; i < len; i++) { - jni::String jstr = array.Get(env, i); - vector.push_back(*convert<std::string, jni::String>(env, jstr)); - jni::DeleteLocalRef(env, jstr); - } - - return vector; -} +std::vector<std::string> toVector(JNIEnv& env, jni::Array<jni::String> array); } } diff --git a/platform/android/src/conversion/color.cpp b/platform/android/src/conversion/color.cpp new file mode 100644 index 0000000000..ce85943e61 --- /dev/null +++ b/platform/android/src/conversion/color.cpp @@ -0,0 +1,17 @@ +#include "color.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +Result<mbgl::Color> Converter<mbgl::Color, int>::operator()(jni::JNIEnv&, const int& color) const { + float r = (color >> 16) & 0xFF; + float g = (color >> 8) & 0xFF; + float b = (color) & 0xFF; + float a = (color >> 24) & 0xFF; + return { mbgl::Color( r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f ) }; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/platform/android/src/conversion/color.hpp b/platform/android/src/conversion/color.hpp index 40aa68d4a9..2b4144b933 100644 --- a/platform/android/src/conversion/color.hpp +++ b/platform/android/src/conversion/color.hpp @@ -10,13 +10,7 @@ namespace conversion { template <> struct Converter<mbgl::Color, int> { - Result<mbgl::Color> operator()(jni::JNIEnv&, const int& color) const { - float r = (color >> 16) & 0xFF; - float g = (color >> 8) & 0xFF; - float b = (color) & 0xFF; - float a = (color >> 24) & 0xFF; - return { mbgl::Color( r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f ) }; - } + Result<mbgl::Color> operator()(jni::JNIEnv&, const int& color) const; }; } // namespace conversion diff --git a/platform/android/src/conversion/constant.cpp b/platform/android/src/conversion/constant.cpp new file mode 100644 index 0000000000..16e8b32943 --- /dev/null +++ b/platform/android/src/conversion/constant.cpp @@ -0,0 +1,70 @@ +#include "constant.hpp" + +#include <sstream> + +namespace mbgl { +namespace android { +namespace conversion { + +Result<jni::jobject*> Converter<jni::jobject*, bool>::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)}; +} + +Result<jni::jobject*> Converter<jni::jobject*, float>::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)}; +} + +Result<jni::jobject*> Converter<jni::jobject*, double>::operator()(jni::JNIEnv& env, const double& value) const { + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Double")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(D)V"); + return {&jni::NewObject(env, *javaClass, *constructor, (jfloat) value)}; +} + +Result<jni::jobject*> Converter<jni::jobject*, std::string>::operator()(jni::JNIEnv& env, const std::string& value) const { + return {jni::Make<jni::String>(env, value).Get()}; +} + +Result<jni::jobject*> Converter<jni::jobject*, Color>::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); +} + +Result<jni::jobject*> Converter<jni::jobject*, std::vector<std::string>>::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; +} + +Result<jni::jobject*> Converter<jni::jobject*, std::vector<float>>::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; +} + +// Java -> C++ + +Result<std::string> Converter<std::string, jni::String>::operator()(jni::JNIEnv& env, const jni::String& value) const { + return { jni::Make<std::string>(env, value) }; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/platform/android/src/conversion/constant.hpp b/platform/android/src/conversion/constant.hpp index f1c72eb5dd..0e665cf56a 100644 --- a/platform/android/src/conversion/constant.hpp +++ b/platform/android/src/conversion/constant.hpp @@ -2,14 +2,14 @@ #include "conversion.hpp" -#include <mbgl/util/optional.hpp> #include <mbgl/util/color.hpp> +#include <mbgl/util/enum.hpp> + #include <jni/jni.hpp> #include <string> #include <array> #include <vector> -#include <sstream> namespace mbgl { namespace android { @@ -17,51 +17,17 @@ 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::jboolean, bool> { - Result<jni::jboolean> operator()(jni::JNIEnv&, const bool& value) const { - return {(jni::jboolean) value}; - } + Result<jni::jobject*> operator()(jni::JNIEnv& env, const bool& value) const; }; 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::jfloat, float> { - Result<jni::jfloat> operator()(jni::JNIEnv&, const float& value) const { - return {(jni::jfloat) value}; - } + Result<jni::jobject*> operator()(jni::JNIEnv& env, const float& value) const; }; - template <> struct Converter<jni::jobject*, double> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const double& value) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Double")).release(); - static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(D)V"); - return {&jni::NewObject(env, *javaClass, *constructor, (jfloat) value)}; - } -}; - -template <> -struct Converter<jni::jdouble, float> { - Result<jni::jdouble> operator()(jni::JNIEnv&, const double& value) const { - return {(jni::jdouble) value}; - } + Result<jni::jobject*> operator()(jni::JNIEnv& env, const double& value) const; }; /** @@ -81,26 +47,12 @@ struct Converter<jni::jobject*, T, typename std::enable_if<std::is_integral<T>:: 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::jstring*, std::string> { - Result<jni::jstring*> operator()(jni::JNIEnv& env, const std::string& value) const { - return {jni::Make<jni::String>(env, value).Get()}; - } + Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::string& value) const; }; 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); - } + Result<jni::jobject*> operator()(jni::JNIEnv& env, const Color& value) const; }; template <std::size_t N> @@ -116,31 +68,18 @@ struct Converter<jni::jobject*, std::array<float, N>> { 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; - } + Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::vector<std::string>& value) const; }; 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); - } + Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::vector<float>& value) const; +}; - return &jarray; +template <class T> +struct Converter<jni::jobject*, T, typename std::enable_if_t<std::is_enum<T>::value>> { + Result<jni::jobject*> operator()(jni::JNIEnv& env, const T& value) const { + return convert<jni::jobject*, std::string>(env, Enum<T>::toString(value)); } }; @@ -148,9 +87,7 @@ struct Converter<jni::jobject*, std::vector<float>> { template <> struct Converter<std::string, jni::String> { - Result<std::string> operator()(jni::JNIEnv& env, const jni::String& value) const { - return { jni::Make<std::string>(env, value) }; - } + Result<std::string> operator()(jni::JNIEnv& env, const jni::String& value) const; }; } // namespace conversion diff --git a/platform/android/src/geojson/conversion/feature.cpp b/platform/android/src/geojson/conversion/feature.cpp new file mode 100644 index 0000000000..3cb6d37b17 --- /dev/null +++ b/platform/android/src/geojson/conversion/feature.cpp @@ -0,0 +1,191 @@ +#include "feature.hpp" + +#include "../../conversion/constant.hpp" +#include "../../conversion/conversion.hpp" +#include "../../jni/local_object.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +/** + * Turn feature identifier into std::string + */ +class FeatureIdVisitor { +public: + + template<class T> + std::string operator()(const T& i) const { + return std::to_string(i); + } + + std::string operator()(const std::string& i) const { + return i; + } + + std::string operator()(const std::nullptr_t&) const { + return ""; + } + +}; + +/** + * Turn properties into Java GSON JsonObject's + */ +class PropertyValueEvaluator { +public: + jni::JNIEnv& env; + + /** + * null + */ + jni::jobject* operator()(const mapbox::geometry::null_value_t &) const { + return (jni::jobject*) nullptr; + } + + /** + * Boolean primitive + */ + jni::jobject* operator()(const bool& value) const { + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Boolean;)V"); + + // Create JsonPrimitive + jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, bool>(env, value)); + jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, *converted); + + return object; + } + + /** + * String primitive + */ + jni::jobject* operator()(const std::string& value) const { + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/String;)V"); + + // Create JsonPrimitive + jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, value)); + jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get()); + + return object; + } + + /** + * Number primitives + */ + template <class Number> + jni::jobject* operator()(const Number& value) const { + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Number;)V"); + + // Create JsonPrimitive + jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, Number>(env, value)); + jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get()); + + return object; + } + + + /** + * Json Array + */ + jni::jobject* operator()(const std::vector<mbgl::Value> &values) const { + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonArray")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");; + static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Lcom/google/gson/JsonElement;)V"); + + // Create json array + jni::jobject* jarray = &jni::NewObject(env, *javaClass, *constructor); + + // Add values + for (const auto &v : values) { + jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(v, *this)); + jni::CallMethod<void>(env, jarray, *add, converted.get()); + } + + return jarray; + } + + /** + * Json Object + */ + jni::jobject* operator()(const std::unordered_map<std::string, mbgl::Value> &value) const { + // TODO: clean up duplication here + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonObject")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");; + static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Ljava/lang/String;Lcom/google/gson/JsonElement;)V"); + + // Create json object + jni::jobject* jsonObject = &jni::NewObject(env, *javaClass, *constructor); + + // Add items + for (auto &item : value) { + jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, *this)); + jni::LocalObject<jni::jobject> key = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, item.first)); + jni::CallMethod<void>(env, jsonObject, *add, key.get(), converted.get()); + } + + return jsonObject; + } +}; + +Result<jni::jobject*> Converter<jni::jobject*, std::unordered_map<std::string, mbgl::Value>>::operator()(jni::JNIEnv& env, const std::unordered_map<std::string, mbgl::Value>& value) const { + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonObject")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");; + static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Ljava/lang/String;Lcom/google/gson/JsonElement;)V"); + + // Create json object + jni::jobject* jsonObject = &jni::NewObject(env, *javaClass, *constructor); + + // Add items + PropertyValueEvaluator evaluator {env}; + for (auto &item : value) { + jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, evaluator)); + jni::LocalObject<jni::jobject> key = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, item.first)); + jni::CallMethod<void>(env, jsonObject, *add, key.get(), converted.get()); + } + + return {jsonObject}; +} + +Result<jni::Object<android::geojson::Feature>> Converter<jni::Object<android::geojson::Feature>, mbgl::Feature>::operator()(jni::JNIEnv& env, const mbgl::Feature& value) const { + + // Convert Id + FeatureIdVisitor idEvaluator; + std::string id = (value.id) ? mapbox::geometry::identifier::visit(value.id.value(), idEvaluator) : ""; + auto jid = jni::Make<jni::String>(env, id); + + // Convert properties + auto properties = jni::Object<gson::JsonObject>(*convert<jni::jobject*>(env, value.properties)); + + // Convert geometry + auto geometry = android::geojson::Geometry::New(env, value.geometry); + + // Create feature + auto feature = android::geojson::Feature::fromGeometry(env, geometry, properties, jid); + + //Cleanup + jni::DeleteLocalRef(env, jid); + jni::DeleteLocalRef(env, geometry); + jni::DeleteLocalRef(env, properties); + + return feature; +} + +Result<jni::Array<jni::Object<android::geojson::Feature>>> Converter<jni::Array<jni::Object<android::geojson::Feature>>, std::vector<mbgl::Feature>>::operator()(jni::JNIEnv& env, const std::vector<mbgl::Feature>& value) const { + using namespace mbgl::android::geojson; + auto features = jni::Array<jni::Object<Feature>>::New(env, value.size(), Feature::javaClass); + + for(size_t i = 0; i < value.size(); i = i + 1) { + auto converted = *convert<jni::Object<android::geojson::Feature>, mbgl::Feature>(env, value.at(i)); + features.Set(env, i, converted); + jni::DeleteLocalRef(env, converted); + } + + return {features}; +} + +} // namespace conversion +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/geojson/conversion/feature.hpp b/platform/android/src/geojson/conversion/feature.hpp index 8fc62a2789..031449cd23 100644 --- a/platform/android/src/geojson/conversion/feature.hpp +++ b/platform/android/src/geojson/conversion/feature.hpp @@ -1,215 +1,31 @@ #pragma once -#include "../../conversion/constant.hpp" #include "../../conversion/conversion.hpp" -#include "geometry.hpp" -#include "../../gson/json_object.hpp" +#include "../feature.hpp" #include <mbgl/util/feature.hpp> -#include <mapbox/variant.hpp> -#include <mapbox/geometry.hpp> - #include <jni/jni.hpp> -#include "../../jni/local_object.hpp" -#include "../feature.hpp" -#include <string> -#include <array> #include <vector> -#include <sstream> - -#include <mbgl/util/logging.hpp> +#include <unordered_map> namespace mbgl { namespace android { namespace conversion { -/** - * Turn feature identifier into std::string - */ -class FeatureIdVisitor { -public: - - template<class T> - std::string operator()(const T& i) const { - return std::to_string(i); - } - - std::string operator()(const std::string& i) const { - return i; - } - - std::string operator()(const std::nullptr_t&) const { - return ""; - } - -}; - -/** - * Turn properties into Java GSON JsonObject's - */ -class PropertyValueEvaluator { -public: - jni::JNIEnv& env; - - /** - * null - */ - jni::jobject* operator()(const mapbox::geometry::null_value_t &) const { - return (jni::jobject*) nullptr; - } - - /** - * Boolean primitive - */ - jni::jobject* operator()(const bool& value) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release(); - static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Boolean;)V"); - - // Create JsonPrimitive - jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, bool>(env, value)); - jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, *converted); - - return object; - } - - /** - * String primitive - */ - jni::jobject* operator()(const std::string& value) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release(); - static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/String;)V"); - - // Create JsonPrimitive - jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, value)); - jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get()); - - return object; - } - - /** - * Number primitives - */ - template <class Number> - jni::jobject* operator()(const Number& value) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release(); - static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Number;)V"); - - // Create JsonPrimitive - jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, Number>(env, value)); - jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get()); - - return object; - } - - - /** - * Json Array - */ - jni::jobject* operator()(const std::vector<mbgl::Value> &values) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonArray")).release(); - static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");; - static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Lcom/google/gson/JsonElement;)V"); - - // Create json array - jni::jobject* jarray = &jni::NewObject(env, *javaClass, *constructor); - - // Add values - for (const auto &v : values) { - jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(v, *this)); - jni::CallMethod<void>(env, jarray, *add, converted.get()); - } - - return jarray; - } - - /** - * Json Object - */ - jni::jobject* operator()(const std::unordered_map<std::string, mbgl::Value> &value) const { - // TODO: clean up duplication here - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonObject")).release(); - static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");; - static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Ljava/lang/String;Lcom/google/gson/JsonElement;)V"); - - // Create json object - jni::jobject* jsonObject = &jni::NewObject(env, *javaClass, *constructor); - - // Add items - for (auto &item : value) { - jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, *this)); - jni::LocalObject<jni::jobject> key = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, item.first)); - jni::CallMethod<void>(env, jsonObject, *add, key.get(), converted.get()); - } - - return jsonObject; - } -}; - template <> struct Converter<jni::jobject*, std::unordered_map<std::string, mbgl::Value>> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::unordered_map<std::string, mbgl::Value>& value) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonObject")).release(); - static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");; - static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Ljava/lang/String;Lcom/google/gson/JsonElement;)V"); - - // Create json object - jni::jobject* jsonObject = &jni::NewObject(env, *javaClass, *constructor); - - // Add items - PropertyValueEvaluator evaluator {env}; - for (auto &item : value) { - jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, evaluator)); - jni::LocalObject<jni::jobject> key = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, item.first)); - jni::CallMethod<void>(env, jsonObject, *add, key.get(), converted.get()); - } - - return {jsonObject}; - } + Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::unordered_map<std::string, mbgl::Value>& value) const; }; - template <> struct Converter<jni::Object<android::geojson::Feature>, mbgl::Feature> { - Result<jni::Object<android::geojson::Feature>> operator()(jni::JNIEnv& env, const mbgl::Feature& value) const { - - // Convert Id - FeatureIdVisitor idEvaluator; - std::string id = (value.id) ? mapbox::geometry::identifier::visit(value.id.value(), idEvaluator) : ""; - auto jid = jni::Make<jni::String>(env, id); - - // Convert properties - auto properties = jni::Object<gson::JsonObject>(*convert<jni::jobject*>(env, value.properties)); - - // Convert geometry - auto geometry = *convert<jni::Object<android::geojson::Geometry>>(env, value.geometry); - - // Create feature - auto feature = android::geojson::Feature::fromGeometry(env, geometry, properties, jid); - - //Cleanup - jni::DeleteLocalRef(env, jid); - jni::DeleteLocalRef(env, geometry); - jni::DeleteLocalRef(env, properties); - - return feature; - } + Result<jni::Object<android::geojson::Feature>> operator()(jni::JNIEnv& env, const mbgl::Feature& value) const; }; template <> struct Converter<jni::Array<jni::Object<android::geojson::Feature>>, std::vector<mbgl::Feature>> { - Result<jni::Array<jni::Object<android::geojson::Feature>>> operator()(jni::JNIEnv& env, const std::vector<mbgl::Feature>& value) const { - using namespace mbgl::android::geojson; - auto features = jni::Array<jni::Object<Feature>>::New(env, value.size(), Feature::javaClass); - - for(size_t i = 0; i < value.size(); i = i + 1) { - auto converted = *convert<jni::Object<android::geojson::Feature>, mbgl::Feature>(env, value.at(i)); - features.Set(env, i, converted); - jni::DeleteLocalRef(env, converted); - } - - return {features}; - } + Result<jni::Array<jni::Object<android::geojson::Feature>>> operator()(jni::JNIEnv& env, const std::vector<mbgl::Feature>& value) const; }; } // namespace conversion diff --git a/platform/android/src/geojson/conversion/geometry.hpp b/platform/android/src/geojson/conversion/geometry.hpp deleted file mode 100644 index 242a68df02..0000000000 --- a/platform/android/src/geojson/conversion/geometry.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include <mapbox/geometry.hpp> -#include "../geometry.hpp" -#include <jni/jni.hpp> - -namespace mbgl { -namespace android { -namespace conversion { - -/** - * mapbox::geometry::geometry<T> -> Java GeoJson Geometry - */ -template <class T> -struct Converter<jni::Object<android::geojson::Geometry>, mapbox::geometry::geometry<T>> { - Result<jni::Object<android::geojson::Geometry>> operator()(jni::JNIEnv& env, const mapbox::geometry::geometry<T>& value) const { - return { android::geojson::Geometry::New(env, value) }; - } -}; - - -} // conversion -} // android -} // mbgl diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index beb2c14eb3..18b966e261 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -180,6 +180,7 @@ void registerNatives(JavaVM *vm) { OfflineRegion::registerNative(env); OfflineRegionDefinition::registerNative(env); OfflineTilePyramidRegionDefinition::registerNative(env); + OfflineGeometryRegionDefinition::registerNative(env); OfflineRegionError::registerNative(env); OfflineRegionStatus::registerNative(env); diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 44b04fc538..8c76332b39 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -30,8 +30,8 @@ // Java -> C++ conversion #include "style/android_conversion.hpp" -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/filter.hpp> +#include <mbgl/style/conversion_impl.hpp> // C++ -> Java conversion #include "conversion/conversion.hpp" @@ -892,7 +892,9 @@ void NativeMapView::removeSource(JNIEnv& env, jni::Object<Source> obj, jlong sou assert(sourcePtr != 0); mbgl::android::Source *source = reinterpret_cast<mbgl::android::Source *>(sourcePtr); - source->removeFromMap(env, obj, *map); + if (source->removeFromMap(env, obj, *map)) { + source->releaseJavaPeer(); + } } void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::Object<Bitmap> bitmap, jni::jfloat scale, jni::jboolean sdf) { diff --git a/platform/android/src/offline/offline_manager.cpp b/platform/android/src/offline/offline_manager.cpp index 4960ae2845..e96ed7e4d2 100644 --- a/platform/android/src/offline/offline_manager.cpp +++ b/platform/android/src/offline/offline_manager.cpp @@ -26,15 +26,18 @@ void OfflineManager::listOfflineRegions(jni::JNIEnv& env_, jni::Object<FileSourc //Keep a shared ptr to a global reference of the callback and file source so they are not GC'd in the meanwhile callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()), jFileSource = std::shared_ptr<jni::jobject>(jFileSource_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()) - ](std::exception_ptr error, mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) mutable { + ](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> regions) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); - if (error) { - OfflineManager::ListOfflineRegionsCallback::onError(*env, jni::Object<ListOfflineRegionsCallback>(*callback), error); - } else if (regions) { - OfflineManager::ListOfflineRegionsCallback::onList(*env, jni::Object<FileSource>(*jFileSource), jni::Object<ListOfflineRegionsCallback>(*callback), std::move(regions)); + if (regions) { + OfflineManager::ListOfflineRegionsCallback::onList( + *env, jni::Object<FileSource>(*jFileSource), + jni::Object<ListOfflineRegionsCallback>(*callback), std::move(*regions)); + } else { + OfflineManager::ListOfflineRegionsCallback::onError( + *env, jni::Object<ListOfflineRegionsCallback>(*callback), regions.error()); } }); } @@ -45,9 +48,7 @@ void OfflineManager::createOfflineRegion(jni::JNIEnv& env_, jni::Array<jni::jbyte> metadata_, jni::Object<CreateOfflineRegionCallback> callback_) { // Convert - - // XXX hardcoded cast for now as we only support OfflineTilePyramidRegionDefinition - auto definition = OfflineTilePyramidRegionDefinition::getDefinition(env_, jni::Object<OfflineTilePyramidRegionDefinition>(*definition_)); + auto definition = OfflineRegionDefinition::getDefinition(env_, definition_); mbgl::OfflineRegionMetadata metadata; if (metadata_) { @@ -59,19 +60,19 @@ void OfflineManager::createOfflineRegion(jni::JNIEnv& env_, //Keep a shared ptr to a global reference of the callback and file source so they are not GC'd in the meanwhile callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()), jFileSource = std::shared_ptr<jni::jobject>(jFileSource_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()) - ](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegion> region) mutable { + ](mbgl::expected<mbgl::OfflineRegion, std::exception_ptr> region) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); - if (error) { - OfflineManager::CreateOfflineRegionCallback::onError(*env, jni::Object<CreateOfflineRegionCallback>(*callback), error); - } else if (region) { + if (region) { OfflineManager::CreateOfflineRegionCallback::onCreate( *env, jni::Object<FileSource>(*jFileSource), - jni::Object<CreateOfflineRegionCallback>(*callback), std::move(region) + jni::Object<CreateOfflineRegionCallback>(*callback), std::move(*region) ); + } else { + OfflineManager::CreateOfflineRegionCallback::onError(*env, jni::Object<CreateOfflineRegionCallback>(*callback), region.error()); } }); } @@ -149,7 +150,7 @@ void OfflineManager::CreateOfflineRegionCallback::onCreate(jni::JNIEnv& env, jni::Object<FileSource> jFileSource, jni::Object<OfflineManager::CreateOfflineRegionCallback> callback, mbgl::optional<mbgl::OfflineRegion> region) { - //Convert the region to java peer object + // Convert the region to java peer object auto jregion = OfflineRegion::New(env, jFileSource, std::move(*region)); // Trigger callback diff --git a/platform/android/src/offline/offline_region.cpp b/platform/android/src/offline/offline_region.cpp index 27de76fb00..5ed37eda73 100644 --- a/platform/android/src/offline/offline_region.cpp +++ b/platform/android/src/offline/offline_region.cpp @@ -107,14 +107,14 @@ void OfflineRegion::getOfflineRegionStatus(jni::JNIEnv& env_, jni::Object<Offlin fileSource.getOfflineRegionStatus(*region, [ //Ensure the object is not gc'd in the meanwhile callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()) - ](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegionStatus> status) mutable { + ](mbgl::expected<mbgl::OfflineRegionStatus, std::exception_ptr> status) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); - if (error) { - OfflineRegionStatusCallback::onError(*env, jni::Object<OfflineRegionStatusCallback>(*callback), error); - } else if (status) { - OfflineRegionStatusCallback::onStatus(*env, jni::Object<OfflineRegionStatusCallback>(*callback), std::move(status)); + if (status) { + OfflineRegionStatusCallback::onStatus(*env, jni::Object<OfflineRegionStatusCallback>(*callback), std::move(*status)); + } else { + OfflineRegionStatusCallback::onError(*env, jni::Object<OfflineRegionStatusCallback>(*callback), status.error()); } }); } @@ -144,14 +144,14 @@ void OfflineRegion::updateOfflineRegionMetadata(jni::JNIEnv& env_, jni::Array<jn fileSource.updateOfflineMetadata(region->getID(), metadata, [ //Ensure the object is not gc'd in the meanwhile callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()) - ](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegionMetadata> data) mutable { + ](mbgl::expected<mbgl::OfflineRegionMetadata, std::exception_ptr> data) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); - if (error) { - OfflineRegionUpdateMetadataCallback::onError(*env, jni::Object<OfflineRegionUpdateMetadataCallback>(*callback), error); - } else if (data) { - OfflineRegionUpdateMetadataCallback::onUpdate(*env, jni::Object<OfflineRegionUpdateMetadataCallback>(*callback), std::move(data)); + if (data) { + OfflineRegionUpdateMetadataCallback::onUpdate(*env, jni::Object<OfflineRegionUpdateMetadataCallback>(*callback), std::move(*data)); + } else { + OfflineRegionUpdateMetadataCallback::onError(*env, jni::Object<OfflineRegionUpdateMetadataCallback>(*callback), data.error()); } }); } @@ -159,7 +159,14 @@ void OfflineRegion::updateOfflineRegionMetadata(jni::JNIEnv& env_, jni::Array<jn jni::Object<OfflineRegion> OfflineRegion::New(jni::JNIEnv& env, jni::Object<FileSource> jFileSource, mbgl::OfflineRegion region) { // Definition - auto definition = jni::Object<OfflineRegionDefinition>(*OfflineTilePyramidRegionDefinition::New(env, region.getDefinition())); + auto definition = region.getDefinition().match( + [&](const mbgl::OfflineTilePyramidRegionDefinition def) { + return jni::Object<OfflineRegionDefinition>( + *OfflineTilePyramidRegionDefinition::New(env, def)); + }, [&](const mbgl::OfflineGeometryRegionDefinition def) { + return jni::Object<OfflineRegionDefinition>( + *OfflineGeometryRegionDefinition::New(env, def)); + }); // Metadata auto metadata = OfflineRegion::metadata(env, region.getMetadata()); diff --git a/platform/android/src/offline/offline_region_definition.cpp b/platform/android/src/offline/offline_region_definition.cpp index 66a9bdf99d..a856672902 100644 --- a/platform/android/src/offline/offline_region_definition.cpp +++ b/platform/android/src/offline/offline_region_definition.cpp @@ -1,6 +1,9 @@ #include "offline_region_definition.hpp" #include "../geometry/lat_lng_bounds.hpp" +#include "../geojson/geometry.hpp" + +#include <exception> namespace mbgl { namespace android { @@ -13,9 +16,21 @@ void OfflineRegionDefinition::registerNative(jni::JNIEnv& env) { javaClass = *jni::Class<OfflineRegionDefinition>::Find(env).NewGlobalRef(env).release(); } +mbgl::OfflineRegionDefinition OfflineRegionDefinition::getDefinition(JNIEnv& env, + jni::Object<OfflineRegionDefinition> jDefinition) { + + if (jDefinition.IsInstanceOf(env, OfflineTilePyramidRegionDefinition::javaClass)) { + return OfflineTilePyramidRegionDefinition::getDefinition(env, jni::Object<OfflineTilePyramidRegionDefinition>(*jDefinition)); + } else if (jDefinition.IsInstanceOf(env, OfflineGeometryRegionDefinition::javaClass)) { + return OfflineGeometryRegionDefinition::getDefinition(env, jni::Object<OfflineGeometryRegionDefinition>(*jDefinition)); + } + + throw std::runtime_error("Unknown offline region definition java class"); +} + // OfflineTilePyramidRegionDefinition // -jni::Object<OfflineTilePyramidRegionDefinition> OfflineTilePyramidRegionDefinition::New(jni::JNIEnv& env, mbgl::OfflineTilePyramidRegionDefinition definition) { +jni::Object<OfflineTilePyramidRegionDefinition> OfflineTilePyramidRegionDefinition::New(jni::JNIEnv& env, const mbgl::OfflineTilePyramidRegionDefinition& definition) { //Convert objects auto styleURL = jni::Make<jni::String>(env, definition.styleURL); @@ -65,5 +80,56 @@ void OfflineTilePyramidRegionDefinition::registerNative(jni::JNIEnv& env) { javaClass = *jni::Class<OfflineTilePyramidRegionDefinition>::Find(env).NewGlobalRef(env).release(); } +// OfflineGeometryRegionDefinition // + +jni::Object<OfflineGeometryRegionDefinition> OfflineGeometryRegionDefinition::New(jni::JNIEnv& env, const mbgl::OfflineGeometryRegionDefinition& definition) { + //Convert objects + auto styleURL = jni::Make<jni::String>(env, definition.styleURL); + auto geometry = geojson::Geometry::New(env, definition.geometry); + + static auto constructor = javaClass.GetConstructor<jni::String, jni::Object<geojson::Geometry>, jni::jdouble, jni::jdouble, jni::jfloat>(env); + auto jdefinition = javaClass.New(env, constructor, styleURL, geometry, definition.minZoom, definition.maxZoom, definition.pixelRatio); + + //Delete References + jni::DeleteLocalRef(env, styleURL); + jni::DeleteLocalRef(env, geometry); + + return jdefinition; +} + +mbgl::OfflineGeometryRegionDefinition OfflineGeometryRegionDefinition::getDefinition(jni::JNIEnv& env, jni::Object<OfflineGeometryRegionDefinition> jDefinition) { + // Field references + static auto styleURLF = javaClass.GetField<jni::String>(env, "styleURL"); + static auto geometryF = javaClass.GetField<jni::Object<geojson::Geometry>>(env, "geometry"); + static auto minZoomF = javaClass.GetField<jni::jdouble>(env, "minZoom"); + static auto maxZoomF = javaClass.GetField<jni::jdouble>(env, "maxZoom"); + static auto pixelRatioF = javaClass.GetField<jni::jfloat>(env, "pixelRatio"); + + // Get objects + auto jStyleURL = jDefinition.Get(env, styleURLF); + auto jGeometry = jDefinition.Get(env, geometryF); + + // Create definition + mbgl::OfflineGeometryRegionDefinition definition( + jni::Make<std::string>(env, jStyleURL), + geojson::Geometry::convert(env, jGeometry), + jDefinition.Get(env, minZoomF), + jDefinition.Get(env, maxZoomF), + jDefinition.Get(env, pixelRatioF) + ); + + // Delete references + jni::DeleteLocalRef(env, jStyleURL); + jni::DeleteLocalRef(env, jGeometry); + + return definition; +} + +jni::Class<OfflineGeometryRegionDefinition> OfflineGeometryRegionDefinition::javaClass; + +void OfflineGeometryRegionDefinition::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineGeometryRegionDefinition>::Find(env).NewGlobalRef(env).release(); +} + } // namespace android } // namespace mbgl diff --git a/platform/android/src/offline/offline_region_definition.hpp b/platform/android/src/offline/offline_region_definition.hpp index 2ca82a4d96..a9dfb54634 100644 --- a/platform/android/src/offline/offline_region_definition.hpp +++ b/platform/android/src/offline/offline_region_definition.hpp @@ -14,13 +14,14 @@ public: static void registerNative(jni::JNIEnv&); + static mbgl::OfflineRegionDefinition getDefinition(JNIEnv& env, jni::Object<OfflineRegionDefinition> jDefinition); }; class OfflineTilePyramidRegionDefinition: public OfflineRegionDefinition { public: static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition"; }; - static jni::Object<OfflineTilePyramidRegionDefinition> New(jni::JNIEnv&, mbgl::OfflineTilePyramidRegionDefinition); + static jni::Object<OfflineTilePyramidRegionDefinition> New(jni::JNIEnv&, const mbgl::OfflineTilePyramidRegionDefinition&); static mbgl::OfflineTilePyramidRegionDefinition getDefinition(jni::JNIEnv&, jni::Object<OfflineTilePyramidRegionDefinition>); @@ -30,5 +31,19 @@ public: }; +class OfflineGeometryRegionDefinition: public OfflineRegionDefinition { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineGeometryRegionDefinition"; }; + + static jni::Object<OfflineGeometryRegionDefinition> New(jni::JNIEnv&, const mbgl::OfflineGeometryRegionDefinition&); + + static mbgl::OfflineGeometryRegionDefinition getDefinition(jni::JNIEnv&, jni::Object<OfflineGeometryRegionDefinition>); + + static jni::Class<OfflineGeometryRegionDefinition> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + } // namespace android } // namespace mbgl diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp index 510a9f8444..8559720b2f 100644 --- a/platform/android/src/style/android_conversion.hpp +++ b/platform/android/src/style/android_conversion.hpp @@ -5,8 +5,8 @@ #include <mbgl/util/feature.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/optional.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <jni/jni.hpp> diff --git a/platform/android/src/style/conversion/filter.cpp b/platform/android/src/style/conversion/filter.cpp new file mode 100644 index 0000000000..4eac0cf82b --- /dev/null +++ b/platform/android/src/style/conversion/filter.cpp @@ -0,0 +1,26 @@ +#include "filter.hpp" +#include "../android_conversion.hpp" + +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/filter.hpp> + +namespace mbgl { +namespace android { +namespace conversion { + +optional<mbgl::style::Filter> toFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) { + mbgl::optional<mbgl::style::Filter> filter; + if (jfilter) { + mbgl::style::conversion::Error error; + auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(Value(env, jfilter), error); + if (!converted) { + mbgl::Log::Error(mbgl::Event::JNI, "Error converting filter: " + error.message); + } + filter = std::move(*converted); + } + return filter; +} + +} // namespace conversion +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/style/conversion/filter.hpp b/platform/android/src/style/conversion/filter.hpp index c154e88e7c..df482de8f3 100644 --- a/platform/android/src/style/conversion/filter.hpp +++ b/platform/android/src/style/conversion/filter.hpp @@ -1,30 +1,15 @@ #pragma once -#include "../android_conversion.hpp" -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/filter.hpp> +#include <mbgl/style/filter.hpp> +#include <mbgl/util/optional.hpp> #include <jni/jni.hpp> -#include <tuple> -#include <map> - namespace mbgl { namespace android { namespace conversion { -inline optional<mbgl::style::Filter> toFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) { - mbgl::optional<mbgl::style::Filter> filter; - if (jfilter) { - mbgl::style::conversion::Error error; - auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(Value(env, jfilter), error); - if (!converted) { - mbgl::Log::Error(mbgl::Event::JNI, "Error converting filter: " + error.message); - } - filter = std::move(*converted); - } - return filter; -} +optional<mbgl::style::Filter> toFilter(jni::JNIEnv&, jni::Array<jni::Object<>>); } // namespace conversion } // namespace android diff --git a/platform/android/src/style/conversion/latlngquad.hpp b/platform/android/src/style/conversion/latlngquad.hpp deleted file mode 100644 index 9d1a83e164..0000000000 --- a/platform/android/src/style/conversion/latlngquad.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include <mapbox/geojson.hpp> -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/geojson.hpp> -#include <jni/jni.hpp> - -namespace mbgl { -namespace style { -namespace conversion { - -template <> -optional<std::array<LatLng, 4>> Converter<std::array<LatLng, 4>>::operator()(const mbgl::android::Value& value, Error& error) const { - if (value.isNull() || !value.isArray()) { - error = { "value cannot be converted to LatLng array" }; - return {}; - } - - return convert<GeoJSON>(value.toString(), error); -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/platform/android/src/style/conversion/position.cpp b/platform/android/src/style/conversion/position.cpp new file mode 100644 index 0000000000..9b3925914e --- /dev/null +++ b/platform/android/src/style/conversion/position.cpp @@ -0,0 +1,24 @@ +#include "position.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +Result<jni::Object<Position>> Converter<jni::Object<Position>, mbgl::style::Position>::operator()(jni::JNIEnv &env, const mbgl::style::Position &value) const { + std::array<float, 3> cartPosition = value.getSpherical(); + return Position::fromPosition(env, cartPosition[0], cartPosition[1], cartPosition[2]); +} + +Result<mbgl::style::Position> Converter<mbgl::style::Position, jni::Object<Position>>::operator()(jni::JNIEnv &env, const jni::Object<Position> &value) const { + float radialCoordinate = Position::getRadialCoordinate(env, value); + float azimuthalAngle = Position::getAzimuthalAngle(env, value); + float polarAngle = Position::getPolarAngle(env, value); + std::array<float, 3> cartPosition {{radialCoordinate, azimuthalAngle, polarAngle}}; + mbgl::style::Position position{}; + position.set(cartPosition); + return position; +} + +} +} +} diff --git a/platform/android/src/style/conversion/position.hpp b/platform/android/src/style/conversion/position.hpp index f32a892c0c..2ef4bf4395 100644 --- a/platform/android/src/style/conversion/position.hpp +++ b/platform/android/src/style/conversion/position.hpp @@ -1,37 +1,25 @@ #pragma once #include "../../conversion/conversion.hpp" +#include "../position.hpp" -#include <jni/jni.hpp> #include <mbgl/style/position.hpp> -#include "../../jni/local_object.hpp" -#include "../position.hpp" +#include <jni/jni.hpp> namespace mbgl { namespace android { namespace conversion { -template<> +template <> struct Converter<jni::Object<Position>, mbgl::style::Position> { - Result<jni::Object<Position>> operator()(jni::JNIEnv &env, const mbgl::style::Position &value) const { - std::array<float, 3> cartPosition = value.getSpherical(); - return Position::fromPosition(env, cartPosition[0], cartPosition[1], cartPosition[2]); - } + Result<jni::Object<Position>> operator()(jni::JNIEnv &env, const mbgl::style::Position &value) const; }; -template<> +template <> struct Converter<mbgl::style::Position, jni::Object<Position>> { - Result<mbgl::style::Position> operator()(jni::JNIEnv &env, const jni::Object<Position> &value) const { - float radialCoordinate = Position::getRadialCoordinate(env, value); - float azimuthalAngle = Position::getAzimuthalAngle(env, value); - float polarAngle = Position::getPolarAngle(env, value); - std::array<float, 3> cartPosition {{radialCoordinate, azimuthalAngle, polarAngle}}; - mbgl::style::Position position{}; - position.set(cartPosition); - return position; - } + Result<mbgl::style::Position> operator()(jni::JNIEnv &env, const jni::Object<Position> &value) const; }; } } -}
\ No newline at end of file +} diff --git a/platform/android/src/style/conversion/property_expression.hpp b/platform/android/src/style/conversion/property_expression.hpp index ae9d4ea41c..08429960cb 100644 --- a/platform/android/src/style/conversion/property_expression.hpp +++ b/platform/android/src/style/conversion/property_expression.hpp @@ -1,16 +1,11 @@ #pragma once -#include <mbgl/style/property_value.hpp> #include "../../conversion/conversion.hpp" -#include "../../conversion/constant.hpp" -#include "types.hpp" -#include "../../java/lang.hpp" - -#include <jni/jni.hpp> #include "../../gson/json_element.hpp" -#include <tuple> -#include <map> +#include <mbgl/style/property_expression.hpp> + +#include <jni/jni.hpp> namespace mbgl { namespace android { @@ -18,11 +13,8 @@ namespace conversion { template <class T> struct Converter<jni::Object<android::gson::JsonElement>, mbgl::style::PropertyExpression<T>> { - Result<jni::Object<android::gson::JsonElement>> operator()(jni::JNIEnv& env, const mbgl::style::PropertyExpression<T>& value) const { - // Convert expressions - mbgl::Value expressionValue = value.getExpression().serialize(); - return gson::JsonElement::New(env, expressionValue); + return gson::JsonElement::New(env, value.getExpression().serialize()); } }; diff --git a/platform/android/src/style/conversion/property_value.hpp b/platform/android/src/style/conversion/property_value.hpp index 256647cddf..8150285c85 100644 --- a/platform/android/src/style/conversion/property_value.hpp +++ b/platform/android/src/style/conversion/property_value.hpp @@ -2,10 +2,10 @@ #include <mbgl/style/color_ramp_property_value.hpp> #include <mbgl/style/property_value.hpp> + #include "../../conversion/conversion.hpp" #include "../../conversion/constant.hpp" #include "property_expression.hpp" -#include "types.hpp" namespace mbgl { namespace android { @@ -17,25 +17,22 @@ namespace conversion { template <typename T> class PropertyValueEvaluator { public: - PropertyValueEvaluator(jni::JNIEnv& _env) : env(_env) {} jni::jobject* operator()(const mbgl::style::Undefined) const { return nullptr; } - jni::jobject* operator()(const T &value) const { - Result<jni::jobject*> result = convert<jni::jobject*>(env, value); - return *result; + jni::jobject* operator()(const T& value) const { + return *convert<jni::jobject*>(env, value); } - jni::jobject* operator()(const mbgl::style::PropertyExpression<T> &value) const { - return *convert<jni::Object<android::gson::JsonElement>, mbgl::style::PropertyExpression<T>>(env, value); + jni::jobject* operator()(const mbgl::style::PropertyExpression<T>& value) const { + return *convert<jni::Object<android::gson::JsonElement>>(env, value); } private: jni::JNIEnv& env; - }; /** @@ -43,7 +40,6 @@ private: */ 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 { PropertyValueEvaluator<T> evaluator(env); return value.evaluate(evaluator); @@ -55,8 +51,7 @@ struct Converter<jni::jobject*, mbgl::style::PropertyValue<T>> { */ template <> struct Converter<jni::jobject*, mbgl::style::ColorRampPropertyValue> { - - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::ColorRampPropertyValue value) const { + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::ColorRampPropertyValue& value) const { PropertyValueEvaluator<mbgl::style::ColorRampPropertyValue> evaluator(env); return *convert<jni::jobject*>(env, value.evaluate(evaluator)); } diff --git a/platform/android/src/style/conversion/transition_options.cpp b/platform/android/src/style/conversion/transition_options.cpp new file mode 100644 index 0000000000..313333ad17 --- /dev/null +++ b/platform/android/src/style/conversion/transition_options.cpp @@ -0,0 +1,16 @@ +#include "transition_options.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +Result<jni::Object<TransitionOptions>> Converter<jni::Object<TransitionOptions>, mbgl::style::TransitionOptions>::operator()(jni::JNIEnv& env, const mbgl::style::TransitionOptions& value) const { + return TransitionOptions::fromTransitionOptions(env, + std::chrono::duration_cast<std::chrono::milliseconds>(value.duration.value_or(mbgl::Duration::zero())).count(), + std::chrono::duration_cast<std::chrono::milliseconds>(value.delay.value_or(mbgl::Duration::zero())).count() + ); +} + +} +} +} diff --git a/platform/android/src/style/conversion/transition_options.hpp b/platform/android/src/style/conversion/transition_options.hpp index ae65a32194..6630456d37 100644 --- a/platform/android/src/style/conversion/transition_options.hpp +++ b/platform/android/src/style/conversion/transition_options.hpp @@ -1,11 +1,11 @@ #pragma once #include "../../conversion/conversion.hpp" +#include "../transition_options.hpp" -#include <jni/jni.hpp> #include <mbgl/style/transition_options.hpp> -#include "../../jni/local_object.hpp" -#include "../transition_options.hpp" + +#include <jni/jni.hpp> namespace mbgl { namespace android { @@ -13,18 +13,9 @@ namespace conversion { template<> struct Converter<jni::Object<TransitionOptions>, mbgl::style::TransitionOptions> { - Result<jni::Object<TransitionOptions>> operator()(jni::JNIEnv &env, const mbgl::style::TransitionOptions &value) const { - - // Convert duration - jlong duration = std::chrono::duration_cast<std::chrono::milliseconds>(value.duration.value_or(mbgl::Duration::zero())).count(); - // Convert delay - jlong delay = std::chrono::duration_cast<std::chrono::milliseconds>(value.delay.value_or(mbgl::Duration::zero())).count(); - - // Create transition options - return TransitionOptions::fromTransitionOptions(env, duration, delay); - } + Result<jni::Object<TransitionOptions>> operator()(jni::JNIEnv&, const mbgl::style::TransitionOptions&) const; }; } } -}
\ No newline at end of file +} diff --git a/platform/android/src/style/conversion/types.hpp b/platform/android/src/style/conversion/types.hpp deleted file mode 100644 index e87782fad0..0000000000 --- a/platform/android/src/style/conversion/types.hpp +++ /dev/null @@ -1,119 +0,0 @@ -// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. -#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::SymbolAnchorType> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::SymbolAnchorType& value) const { - return convert<jni::jobject*, std::string>(env, toString(value)); - } -}; - -template <> -struct Converter<jni::jobject*, mbgl::style::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::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)); - } -}; - -template <> -struct Converter<jni::jobject*, mbgl::style::RasterResamplingType> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::RasterResamplingType& value) const { - return convert<jni::jobject*, std::string>(env, toString(value)); - } -}; - -template <> -struct Converter<jni::jobject*, mbgl::style::HillshadeIlluminationAnchorType> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::HillshadeIlluminationAnchorType& value) const { - return convert<jni::jobject*, std::string>(env, toString(value)); - } -}; - -template <> -struct Converter<jni::jobject*, mbgl::style::LightAnchorType> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::LightAnchorType& value) const { - return convert<jni::jobject*, std::string>(env, toString(value)); - } -}; - - -} // namespace conversion -} // namespace android -} // namespace mbgl diff --git a/platform/android/src/style/conversion/types.hpp.ejs b/platform/android/src/style/conversion/types.hpp.ejs deleted file mode 100644 index 3cd4764015..0000000000 --- a/platform/android/src/style/conversion/types.hpp.ejs +++ /dev/null @@ -1,40 +0,0 @@ -<% - const properties = locals.properties; --%> -// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. -#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 diff --git a/platform/android/src/style/conversion/types_string_values.hpp b/platform/android/src/style/conversion/types_string_values.hpp deleted file mode 100644 index 9f6696d181..0000000000 --- a/platform/android/src/style/conversion/types_string_values.hpp +++ /dev/null @@ -1,257 +0,0 @@ -// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. -#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; - case mbgl::style::SymbolPlacementType::LineCenter: - return "line-center"; - 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; - case mbgl::style::AlignmentType::Auto: - return "auto"; - 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::Width: - return "width"; - break; - case mbgl::style::IconTextFitType::Height: - return "height"; - break; - case mbgl::style::IconTextFitType::Both: - return "both"; - break; - default: - throw std::runtime_error("Not implemented"); - } - } - - // icon-anchor - inline std::string toString(mbgl::style::SymbolAnchorType value) { - switch (value) { - case mbgl::style::SymbolAnchorType::Center: - return "center"; - break; - case mbgl::style::SymbolAnchorType::Left: - return "left"; - break; - case mbgl::style::SymbolAnchorType::Right: - return "right"; - break; - case mbgl::style::SymbolAnchorType::Top: - return "top"; - break; - case mbgl::style::SymbolAnchorType::Bottom: - return "bottom"; - break; - case mbgl::style::SymbolAnchorType::TopLeft: - return "top-left"; - break; - case mbgl::style::SymbolAnchorType::TopRight: - return "top-right"; - break; - case mbgl::style::SymbolAnchorType::BottomLeft: - return "bottom-left"; - break; - case mbgl::style::SymbolAnchorType::BottomRight: - return "bottom-right"; - 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-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"); - } - } - - // raster-resampling - inline std::string toString(mbgl::style::RasterResamplingType value) { - switch (value) { - case mbgl::style::RasterResamplingType::Linear: - return "linear"; - break; - case mbgl::style::RasterResamplingType::Nearest: - return "nearest"; - break; - default: - throw std::runtime_error("Not implemented"); - } - } - - // hillshade-illumination-anchor - inline std::string toString(mbgl::style::HillshadeIlluminationAnchorType value) { - switch (value) { - case mbgl::style::HillshadeIlluminationAnchorType::Map: - return "map"; - break; - case mbgl::style::HillshadeIlluminationAnchorType::Viewport: - return "viewport"; - break; - default: - throw std::runtime_error("Not implemented"); - } - } - - // anchor - inline std::string toString(mbgl::style::LightAnchorType value) { - switch (value) { - case mbgl::style::LightAnchorType::Map: - return "map"; - break; - case mbgl::style::LightAnchorType::Viewport: - return "viewport"; - break; - default: - throw std::runtime_error("Not implemented"); - } - } - - -} // namespace conversion -} // namespace android -} // namespace mbgl diff --git a/platform/android/src/style/conversion/types_string_values.hpp.ejs b/platform/android/src/style/conversion/types_string_values.hpp.ejs deleted file mode 100644 index bf52919741..0000000000 --- a/platform/android/src/style/conversion/types_string_values.hpp.ejs +++ /dev/null @@ -1,48 +0,0 @@ -<% - const properties = locals.properties; --%> -// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. -#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 in 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 diff --git a/platform/android/src/style/conversion/url_or_tileset.cpp b/platform/android/src/style/conversion/url_or_tileset.cpp new file mode 100644 index 0000000000..2ec5856751 --- /dev/null +++ b/platform/android/src/style/conversion/url_or_tileset.cpp @@ -0,0 +1,30 @@ +#include "url_or_tileset.hpp" +#include "../android_conversion.hpp" + +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/tileset.hpp> + +namespace mbgl { +namespace android { + +// This conversion is expected not to fail because it's used only in contexts where +// the value was originally a String or TileSet object on the Java side. If it fails +// to convert, it's a bug in our serialization or Java-side static typing. +variant<std::string, Tileset> convertURLOrTileset(mbgl::android::Value&& value) { + using namespace mbgl::style::conversion; + + const Convertible convertible(std::move(value)); + if (isObject(convertible)) { + Error error; + optional<Tileset> tileset = convert<Tileset>(convertible, error); + if (!tileset) { + throw std::logic_error(error.message); + } + return { *tileset }; + } else { + return { *toString(convertible) }; + } +} + +} +} diff --git a/platform/android/src/style/conversion/url_or_tileset.hpp b/platform/android/src/style/conversion/url_or_tileset.hpp index 92c1182a63..f42a9b9a2a 100644 --- a/platform/android/src/style/conversion/url_or_tileset.hpp +++ b/platform/android/src/style/conversion/url_or_tileset.hpp @@ -1,37 +1,16 @@ #pragma once -#include <mbgl/util/optional.hpp> #include <mbgl/util/variant.hpp> - #include <mbgl/util/tileset.hpp> -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/tileset.hpp> - -#include <jni/jni.hpp> #include <string> +#include "../value.hpp" + namespace mbgl { namespace android { -// This conversion is expected not to fail because it's used only in contexts where -// the value was originally a String or TileSet object on the Java side. If it fails -// to convert, it's a bug in our serialization or Java-side static typing. -inline variant<std::string, Tileset> convertURLOrTileset(mbgl::android::Value&& value) { - using namespace mbgl::style::conversion; - - const Convertible convertible(std::move(value)); - if (isObject(convertible)) { - Error error; - optional<Tileset> tileset = convert<Tileset>(convertible, error); - if (!tileset) { - throw std::logic_error(error.message); - } - return { *tileset }; - } else { - return { *toString(convertible) }; - } -} +variant<std::string, Tileset> convertURLOrTileset(mbgl::android::Value&& value); } } diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index c7a6bcd3a3..6c08893411 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -18,10 +18,10 @@ #include <mbgl/util/logging.hpp> // Java -> C++ conversion -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/filter.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/source.hpp> +#include <mbgl/style/conversion_impl.hpp> // C++ -> Java conversion #include "../conversion/property_value.hpp" @@ -91,7 +91,7 @@ namespace android { void Layer::setLayoutProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) { // Convert and set property - optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setLayoutProperty(layer, jni::Make<std::string>(env, jname), Value(env, jvalue)); + optional<mbgl::style::conversion::Error> error = layer.setLayoutProperty(jni::Make<std::string>(env, jname), Value(env, jvalue)); if (error) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message); return; @@ -100,7 +100,7 @@ namespace android { void Layer::setPaintProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) { // Convert and set property - optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), Value(env, jvalue)); + optional<mbgl::style::conversion::Error> error = layer.setPaintProperty(jni::Make<std::string>(env, jname), Value(env, jvalue)); if (error) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message); return; diff --git a/platform/android/src/style/sources/custom_geometry_source.cpp b/platform/android/src/style/sources/custom_geometry_source.cpp index b38405a3b1..9c51f70ab5 100644 --- a/platform/android/src/style/sources/custom_geometry_source.cpp +++ b/platform/android/src/style/sources/custom_geometry_source.cpp @@ -54,7 +54,9 @@ namespace android { : Source(env, coreSource, createJavaPeer(env), frontend) { } - CustomGeometrySource::~CustomGeometrySource() = default; + CustomGeometrySource::~CustomGeometrySource() { + releaseThreads(); + } void CustomGeometrySource::fetchTile (const mbgl::CanonicalTileID& tileID) { android::UniqueEnv _env = android::AttachEnv(); @@ -78,6 +80,28 @@ namespace android { peer.Call(*_env, cancelTile, (int)tileID.z, (int)tileID.x, (int)tileID.y); }; + void CustomGeometrySource::startThreads() { + android::UniqueEnv _env = android::AttachEnv(); + + static auto startThreads = javaClass.GetMethod<void ()>(*_env, "startThreads"); + + assert(javaPeer); + + auto peer = jni::Cast(*_env, *javaPeer, javaClass); + peer.Call(*_env, startThreads); + } + + void CustomGeometrySource::releaseThreads() { + android::UniqueEnv _env = android::AttachEnv(); + + static auto releaseThreads = javaClass.GetMethod<void ()>(*_env, "releaseThreads"); + + assert(javaPeer); + + auto peer = jni::Cast(*_env, *javaPeer, javaClass); + peer.Call(*_env, releaseThreads); + }; + void CustomGeometrySource::setTileData(jni::JNIEnv& env, jni::jint z, jni::jint x, @@ -120,6 +144,19 @@ namespace android { return jni::Object<Source>(CustomGeometrySource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)).Get()); } + void CustomGeometrySource::addToMap(JNIEnv& env, jni::Object<Source> obj, mbgl::Map& map, AndroidRendererFrontend& frontend) { + Source::addToMap(env, obj, map, frontend); + startThreads(); + } + + bool CustomGeometrySource::removeFromMap(JNIEnv& env, jni::Object<Source> source, mbgl::Map& map) { + bool successfullyRemoved = Source::removeFromMap(env, source, map); + if (successfullyRemoved) { + releaseThreads(); + } + return successfullyRemoved; + } + void CustomGeometrySource::registerNative(jni::JNIEnv& env) { // Lookup the class CustomGeometrySource::javaClass = *jni::Class<CustomGeometrySource>::Find(env).NewGlobalRef(env).release(); diff --git a/platform/android/src/style/sources/custom_geometry_source.hpp b/platform/android/src/style/sources/custom_geometry_source.hpp index 1dc1c07b4f..c38926a5b9 100644 --- a/platform/android/src/style/sources/custom_geometry_source.hpp +++ b/platform/android/src/style/sources/custom_geometry_source.hpp @@ -28,8 +28,13 @@ public: ~CustomGeometrySource(); + bool removeFromMap(JNIEnv&, jni::Object<Source>, mbgl::Map&) override; + void addToMap(JNIEnv&, jni::Object<Source>, mbgl::Map&, AndroidRendererFrontend&) override; + void fetchTile(const mbgl::CanonicalTileID& tileID); void cancelTile(const mbgl::CanonicalTileID& tileID); + void startThreads(); + void releaseThreads(); void setTileData(jni::JNIEnv& env, jni::jint z, jni::jint x, jni::jint y, jni::Object<geojson::FeatureCollection> jf); void invalidateTile(jni::JNIEnv& env, jni::jint z, jni::jint x, jni::jint y); diff --git a/platform/android/src/style/sources/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp index 6d9ab9e22c..e526231763 100644 --- a/platform/android/src/style/sources/geojson_source.cpp +++ b/platform/android/src/style/sources/geojson_source.cpp @@ -5,9 +5,9 @@ // Java -> C++ conversion #include "../android_conversion.hpp" #include "../conversion/filter.hpp" -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/geojson.hpp> #include <mbgl/style/conversion/geojson_options.hpp> +#include <mbgl/style/conversion_impl.hpp> // C++ -> Java conversion #include "../../conversion/conversion.hpp" @@ -16,7 +16,13 @@ #include "../conversion/url_or_tileset.hpp" #include <string> +#include <mbgl/util/shared_thread_pool.hpp> +// GeoJSONSource uses a "coalescing" model for high frequency asynchronous data update calls, +// which in practice means, that any update that started processing is going to finish +// and the last scheduled update is going to finish as well. Any updates scheduled during processing can be canceled. +// Conversion from Java features to core ones is done on a worker thread and once finished, +// the ownership of the converted features is returned to the calling thread. namespace mbgl { namespace android { @@ -40,60 +46,39 @@ namespace android { : Source(env, std::make_unique<mbgl::style::GeoJSONSource>( jni::Make<std::string>(env, sourceId), convertGeoJSONOptions(env, options)) - ) { + ), converter(std::make_unique<Actor<FeatureConverter>>(*sharedThreadPool())) { } GeoJSONSource::GeoJSONSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend& frontend) - : Source(env, coreSource, createJavaPeer(env), frontend) { + : Source(env, coreSource, createJavaPeer(env), frontend) + , converter(std::make_unique<Actor<FeatureConverter>>(*sharedThreadPool())) { } GeoJSONSource::~GeoJSONSource() = default; - void GeoJSONSource::setGeoJSONString(jni::JNIEnv& env, jni::String json) { - using namespace mbgl::style::conversion; + void GeoJSONSource::setGeoJSONString(jni::JNIEnv& env, jni::String jString) { - // Convert the jni object - Error error; - optional<GeoJSON> converted = convert<GeoJSON>(mbgl::android::Value(env, json), error); - if(!converted) { - mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + error.message); - return; - } + std::shared_ptr<std::string> json = std::make_shared<std::string>(jni::Make<std::string>(env, jString)); - // Update the core source - source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setGeoJSON(*converted); + Update::Converter converterFn = [this, json](ActorRef<Callback> _callback) { + converter->self().invoke(&FeatureConverter::convertJson, json, _callback); + }; + + setAsync(converterFn); } void GeoJSONSource::setFeatureCollection(jni::JNIEnv& env, jni::Object<geojson::FeatureCollection> jFeatures) { - using namespace mbgl::android::geojson; - - // Convert the jni object - auto features = FeatureCollection::convert(env, jFeatures); - - // Update the core source - source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setGeoJSON(GeoJSON(features)); + setCollectionAsync(env, jFeatures); } void GeoJSONSource::setFeature(jni::JNIEnv& env, jni::Object<geojson::Feature> jFeature) { - using namespace mbgl::android::geojson; - - // Convert the jni object - auto feature = Feature::convert(env, jFeature); - - // Update the core source - source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setGeoJSON(GeoJSON(feature)); + setCollectionAsync(env, jFeature); } void GeoJSONSource::setGeometry(jni::JNIEnv& env, jni::Object<geojson::Geometry> jGeometry) { - using namespace mbgl::android::geojson; - - // Convert the jni object - auto geometry = Geometry::convert(env, jGeometry); - - // Update the core source - source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setGeoJSON(GeoJSON(geometry)); + setCollectionAsync(env, jGeometry); } void GeoJSONSource::setURL(jni::JNIEnv& env, jni::String url) { @@ -125,6 +110,50 @@ namespace android { return jni::Object<Source>(GeoJSONSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)).Get()); } + template <class JNIType> + void GeoJSONSource::setCollectionAsync(jni::JNIEnv& env, jni::Object<JNIType> jObject) { + + std::shared_ptr<jni::jobject> object = std::shared_ptr<jni::jobject>(jObject.NewGlobalRef(env).release()->Get(), GenericGlobalRefDeleter()); + + Update::Converter converterFn = [this, object](ActorRef<Callback> _callback) { + converter->self().invoke(&FeatureConverter::convertObject<JNIType>, jni::Object<JNIType>(*object), _callback); + }; + + setAsync(converterFn); + } + + void GeoJSONSource::setAsync(Update::Converter converterFn) { + awaitingUpdate = std::make_unique<Update>( + std::move(converterFn), + std::make_unique<Actor<Callback>>( + *Scheduler::GetCurrent(), + [this](GeoJSON geoJSON) { + // conversion from Java features to core ones finished + android::UniqueEnv _env = android::AttachEnv(); + + // Update the core source + source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setGeoJSON(geoJSON); + + // if there is an awaiting update, execute it, otherwise, release resources + if (awaitingUpdate) { + update = std::move(awaitingUpdate); + update->converterFn(update->callback->self()); + } else { + update.reset(); + } + }) + ); + + // If another update is running, wait + if (update) { + return; + } + + // no updates are being processed, execute this one + update = std::move(awaitingUpdate); + update->converterFn(update->callback->self()); + } + void GeoJSONSource::registerNative(jni::JNIEnv& env) { // Lookup the class GeoJSONSource::javaClass = *jni::Class<GeoJSONSource>::Find(env).NewGlobalRef(env).release(); @@ -147,5 +176,36 @@ namespace android { ); } + void FeatureConverter::convertJson(std::shared_ptr<std::string> json, + ActorRef<Callback> callback) { + using namespace mbgl::style::conversion; + + android::UniqueEnv _env = android::AttachEnv(); + + // Convert the jni object + Error error; + optional<GeoJSON> converted = parseGeoJSON(*json, error); + if(!converted) { + mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + error.message); + return; + } + + callback.invoke(&Callback::operator(), *converted); + } + + template<class JNIType> + void FeatureConverter::convertObject(jni::Object<JNIType> jObject, ActorRef<Callback> callback) { + using namespace mbgl::android::geojson; + + android::UniqueEnv _env = android::AttachEnv(); + // Convert the jni object + auto geometry = JNIType::convert(*_env, jObject); + callback.invoke(&Callback::operator(), GeoJSON(geometry)); + } + + Update::Update(Converter _converterFn, std::unique_ptr<Actor<Callback>> _callback) + : converterFn(std::move(_converterFn)) + , callback(std::move(_callback)) {} + } // namespace android } // namespace mbgl diff --git a/platform/android/src/style/sources/geojson_source.hpp b/platform/android/src/style/sources/geojson_source.hpp index c46519b04a..b9c360c67c 100644 --- a/platform/android/src/style/sources/geojson_source.hpp +++ b/platform/android/src/style/sources/geojson_source.hpp @@ -10,6 +10,24 @@ namespace mbgl { namespace android { +using Callback = std::function<void (GeoJSON)>; + +struct FeatureConverter { + void convertJson(std::shared_ptr<std::string>, ActorRef<Callback>); + + template <class JNIType> + void convertObject(jni::Object<JNIType>, ActorRef<Callback>); +}; + +struct Update { + using Converter = std::function<void (ActorRef<Callback>)>; + Converter converterFn; + + std::unique_ptr<Actor<Callback>> callback; + + Update(Converter, std::unique_ptr<Actor<Callback>>); +}; + class GeoJSONSource : public Source { public: @@ -35,13 +53,21 @@ public: void setURL(jni::JNIEnv&, jni::String); + jni::String getURL(jni::JNIEnv&); + jni::Array<jni::Object<geojson::Feature>> querySourceFeatures(jni::JNIEnv&, jni::Array<jni::Object<>> jfilter); - jni::String getURL(jni::JNIEnv&); - private: jni::Object<Source> createJavaPeer(jni::JNIEnv&); + std::unique_ptr<Update> awaitingUpdate; + std::unique_ptr<Update> update; + std::unique_ptr<Actor<FeatureConverter>> converter; + + template <class JNIType> + void setCollectionAsync(jni::JNIEnv&, jni::Object<JNIType>); + + void setAsync(Update::Converter); }; // class GeoJSONSource diff --git a/platform/android/src/style/sources/image_source.cpp b/platform/android/src/style/sources/image_source.cpp index 249387ea51..278564485d 100644 --- a/platform/android/src/style/sources/image_source.cpp +++ b/platform/android/src/style/sources/image_source.cpp @@ -5,7 +5,7 @@ // C++ -> Java conversion #include "../../conversion/conversion.hpp" -#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/premultiply.hpp> #include "../../bitmap.hpp" diff --git a/platform/android/src/style/sources/source.cpp b/platform/android/src/style/sources/source.cpp index 413530a5ec..d2e2426c0b 100644 --- a/platform/android/src/style/sources/source.cpp +++ b/platform/android/src/style/sources/source.cpp @@ -7,8 +7,8 @@ #include <mbgl/util/logging.hpp> // Java -> C++ conversion -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/source.hpp> +#include <mbgl/style/conversion_impl.hpp> // C++ -> Java conversion #include "../conversion/property_value.hpp" @@ -51,7 +51,7 @@ namespace android { if (!coreSource.peer.has_value()) { coreSource.peer = createSourcePeer(env, coreSource, frontend); } - return *mbgl::util::any_cast<std::unique_ptr<Source>>(&coreSource.peer)->get()->javaPeer; + return *coreSource.peer.get<std::unique_ptr<Source>>()->javaPeer; } Source::Source(jni::JNIEnv& env, mbgl::style::Source& coreSource, jni::Object<Source> obj, AndroidRendererFrontend& frontend) @@ -109,7 +109,7 @@ namespace android { rendererFrontend = &frontend; } - void Source::removeFromMap(JNIEnv&, jni::Object<Source>, mbgl::Map& map) { + bool Source::removeFromMap(JNIEnv&, jni::Object<Source>, mbgl::Map& map) { // Cannot remove if not attached yet if (ownedSource) { throw std::runtime_error("Cannot remove detached source"); @@ -119,13 +119,18 @@ namespace android { ownedSource = map.getStyle().removeSource(source.getID()); // The source may not be removed if any layers still reference it + return ownedSource != nullptr; + } + + void Source::releaseJavaPeer() { + // We can't release the peer if the source was not removed from the map if (!ownedSource) { return; } // Release the peer relationships. These will be re-established when the source is added to a map assert(ownedSource->peer.has_value()); - util::any_cast<std::unique_ptr<Source>>(&(ownedSource->peer))->release(); + ownedSource->peer.get<std::unique_ptr<Source>>().release(); ownedSource->peer.reset(); // Release the strong reference to the java peer diff --git a/platform/android/src/style/sources/source.hpp b/platform/android/src/style/sources/source.hpp index 718f60b381..6b906eb9c0 100644 --- a/platform/android/src/style/sources/source.hpp +++ b/platform/android/src/style/sources/source.hpp @@ -35,9 +35,11 @@ public: virtual ~Source(); - void addToMap(JNIEnv&, jni::Object<Source>, mbgl::Map&, AndroidRendererFrontend&); + virtual void addToMap(JNIEnv&, jni::Object<Source>, mbgl::Map&, AndroidRendererFrontend&); - void removeFromMap(JNIEnv&, jni::Object<Source>, mbgl::Map&); + virtual bool removeFromMap(JNIEnv&, jni::Object<Source>, mbgl::Map&); + + void releaseJavaPeer(); jni::String getId(jni::JNIEnv&); diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index b8112d6fec..dd07ae9e76 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -57,7 +57,7 @@ may be shorter than on a desktop computer. Some of your users may use the Larger Dynamic Type and Accessibility Text features to increase the size of all text on the device. You can use the [runtime styling API](#manipulating-the-style-at-runtime) to adjust your style’s -font and icon sizes accordingly. + font and icon sizes accordingly. <% } -%> Design sprite images and choose font weights that look crisp on both @@ -148,7 +148,7 @@ represented at runtime by an `MGLStyle` object, which provides access to various layers, respectively. <% if (iOS) { -%> For more information about the capabilities exposed by the runtime styling API, -see “[Runtime Styling](runtime-styling.html)”. +see “[Runtime Styling](https://www.mapbox.com/ios-sdk/maps/overview/runtime-styling/)”. <% } -%> The names of runtime styling classes and properties on <%- os %> are generally @@ -222,6 +222,7 @@ In style JSON | In the SDK `cluster` | `MGLShapeSourceOptionClustered` `clusterRadius` | `MGLShapeSourceOptionClusterRadius` `clusterMaxZoom` | `MGLShapeSourceOptionMaximumZoomLevelForClustering` +`lineMetrics` | `MGLShapeSourceOptionLineDistanceMetrics` To create a shape source from local GeoJSON data, first [convert the GeoJSON data into a shape](working-with-geojson-data.html#converting-geojson-data-into-shape-objects), diff --git a/platform/darwin/docs/guides/Migrating to Expressions.md.ejs b/platform/darwin/docs/guides/Migrating to Expressions.md.ejs index addbf6940e..3ea5b33d73 100644 --- a/platform/darwin/docs/guides/Migrating to Expressions.md.ejs +++ b/platform/darwin/docs/guides/Migrating to Expressions.md.ejs @@ -12,7 +12,7 @@ # Migrating from Style Functions to Expressions -[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source. +[Runtime Styling](https://www.mapbox.com/ios-sdk/maps/overview/runtime-styling/) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source. With Mapbox Maps SDK for <%- iOS ? 'iOS v4.0.0' : 'macOS v0.7.0' %>, style functions have been replaced with expressions. These provide even more tools for developers who want to style their maps dynamically. This guide outlines some tips for migrating from style functions to expressions, and offers an overview of some things that developers can do with expressions. @@ -91,7 +91,7 @@ Current syntax: If you previously used an interpolation base greater than `0` (other than `1`), you can use `MGLExpressionInterpolationMode.exponential` as the curve type for `+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` or `'exponential'` as the curve type for [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code). The `parameters` argument takes that interpolation base. This interpolates between values exponentially, creating an accelerated ramp effect. -Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key. +Here’s a visualization from Mapbox Studio (see [Mapbox Studio and iOS](https://www.mapbox.com/ios-sdk/maps/overview/mapbox-studio/)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key. <img src="img/data-driven-styling/exponential-function.png" height=344/> <img src="img/data-driven-styling/exponential-function-1.png" height=344/> diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 0e5bf89fd4..e89a4e29a3 100755 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -102,6 +102,7 @@ global.objCTestValue = function (property, layerType, arraysAsStructs, indent) { return property.default ? '@"false"' : '@"true"'; case 'number': return '@"1"'; + case 'formatted': case 'string': return `@"'${_.startCase(propertyName)}'"`; case 'enum': @@ -146,6 +147,7 @@ global.mbglTestValue = function (property, layerType) { return property.default ? 'false' : 'true'; case 'number': return '1.0'; + case 'formatted': case 'string': return `"${_.startCase(propertyName)}"`; case 'enum': { @@ -218,6 +220,7 @@ global.testHelperMessage = function (property, layerType, isFunction) { return 'testBool' + fnSuffix; case 'number': return 'testNumber' + fnSuffix; + case 'formatted': case 'string': return 'testString' + fnSuffix; case 'enum': @@ -385,6 +388,7 @@ global.describeType = function (property) { return 'Boolean'; case 'number': return 'numeric'; + case 'formatted': case 'string': return 'string'; case 'enum': @@ -428,6 +432,7 @@ global.describeValue = function (value, property, layerType) { return value ? '`YES`' : '`NO`'; case 'number': return 'the float ' + '`' + formatNumber(value) + '`'; + case 'formatted': case 'string': if (value === '') { return 'the empty string'; @@ -509,6 +514,7 @@ global.propertyType = function (property) { return 'NSNumber *'; case 'number': return 'NSNumber *'; + case 'formatted': case 'string': return 'NSString *'; case 'enum': @@ -539,7 +545,8 @@ global.isInterpolatable = function (property) { const type = property.type === 'array' ? property.value : property.type; return type !== 'boolean' && type !== 'enum' && - type !== 'string'; + type !== 'string' && + type !== 'formatted'; }; global.valueTransformerArguments = function (property) { @@ -549,6 +556,7 @@ global.valueTransformerArguments = function (property) { return ['bool', objCType]; case 'number': return ['float', objCType]; + case 'formatted': case 'string': return ['std::string', objCType]; case 'enum': @@ -582,6 +590,7 @@ global.mbglType = function(property) { return 'bool'; case 'number': return 'float'; + case 'formatted': case 'string': return 'std::string'; case 'enum': { diff --git a/platform/darwin/src/MGLComputedShapeSource.h b/platform/darwin/src/MGLComputedShapeSource.h index faf8871fc7..84dc4801a7 100644 --- a/platform/darwin/src/MGLComputedShapeSource.h +++ b/platform/darwin/src/MGLComputedShapeSource.h @@ -32,6 +32,8 @@ FOUNDATION_EXTERN MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionWrap */ FOUNDATION_EXTERN MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLInvalidDatasourceException; + /** Data source for `MGLComputedShapeSource`. This protocol defines two optional methods for fetching data, one based on tile coordinates, and one based on a bounding box. Classes that implement this diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm index 04734d0ef5..0493131922 100644 --- a/platform/darwin/src/MGLComputedShapeSource.mm +++ b/platform/darwin/src/MGLComputedShapeSource.mm @@ -4,12 +4,15 @@ #import "MGLSource_Private.h" #import "MGLShape_Private.h" #import "MGLGeometry_Private.h" +#import "MGLShapeCollection.h" #include <mbgl/map/map.hpp> #include <mbgl/style/sources/custom_geometry_source.hpp> #include <mbgl/tile/tile_id.hpp> #include <mbgl/util/geojson.hpp> +const MGLExceptionName MGLInvalidDatasourceException = @"MGLInvalidDatasourceException"; + const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates = @"MGLShapeSourceOptionWrapsCoordinates"; const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates = @"MGLShapeSourceOptionClipsCoordinates"; @@ -129,6 +132,14 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi mbgl::FeatureCollection featureCollection; featureCollection.reserve(data.count); for (MGLShape <MGLFeature> * feature in data) { + if ([feature isMemberOfClass:[MGLShapeCollection class]]) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSLog(@"MGLShapeCollection initialized with MGLFeatures will not retain attributes." + @"Use MGLShapeCollectionFeature to retain attributes instead." + @"This will be logged only once."); + }); + } mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>(); featureCollection.push_back(geoJsonObject); } @@ -194,6 +205,14 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi for (MGLShape <MGLFeature> * feature in features) { mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>(); featureCollection.push_back(geoJsonObject); + if ([feature isMemberOfClass:[MGLShapeCollection class]]) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSLog(@"MGLShapeCollection initialized with MGLFeatures will not retain attributes." + @"Use MGLShapeCollectionFeature to retain attributes instead." + @"This will be logged only once."); + }); + } } const auto geojson = mbgl::GeoJSON{featureCollection}; static_cast<mbgl::style::CustomGeometrySource *>(self.rawSource)->setTileData(tileID, geojson); @@ -205,10 +224,12 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi self.dataSourceImplementsFeaturesForTile = [dataSource respondsToSelector:@selector(featuresInTileAtX:y:zoomLevel:)]; self.dataSourceImplementsFeaturesForBounds = [dataSource respondsToSelector:@selector(featuresInCoordinateBounds:zoomLevel:)]; - if(!self.dataSourceImplementsFeaturesForBounds && !self.dataSourceImplementsFeaturesForTile) { - [NSException raise:@"Invalid Datasource" format:@"Datasource does not implement any MGLComputedShapeSourceDataSource methods"]; - } else if(self.dataSourceImplementsFeaturesForBounds && self.dataSourceImplementsFeaturesForTile) { - [NSException raise:@"Invalid Datasource" format:@"Datasource implements multiple MGLComputedShapeSourceDataSource methods"]; + if (!self.dataSourceImplementsFeaturesForBounds && !self.dataSourceImplementsFeaturesForTile) { + [NSException raise:MGLInvalidDatasourceException + format:@"Datasource does not implement any MGLComputedShapeSourceDataSource methods"]; + } else if (self.dataSourceImplementsFeaturesForBounds && self.dataSourceImplementsFeaturesForTile) { + [NSException raise:MGLInvalidDatasourceException + format:@"Datasource implements multiple MGLComputedShapeSourceDataSource methods"]; } _dataSource = dataSource; diff --git a/platform/darwin/src/MGLConversion.h b/platform/darwin/src/MGLConversion.h index 92a6720e6a..9057ed7824 100644 --- a/platform/darwin/src/MGLConversion.h +++ b/platform/darwin/src/MGLConversion.h @@ -1,4 +1,4 @@ -#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion_impl.hpp> NS_ASSUME_NONNULL_BEGIN diff --git a/platform/darwin/src/MGLForegroundStyleLayer.mm b/platform/darwin/src/MGLForegroundStyleLayer.mm index 6888f89b92..76700d6f77 100644 --- a/platform/darwin/src/MGLForegroundStyleLayer.mm +++ b/platform/darwin/src/MGLForegroundStyleLayer.mm @@ -3,7 +3,7 @@ @implementation MGLForegroundStyleLayer - (NSString *)sourceIdentifier { - [NSException raise:@"MGLAbstractClassException" + [NSException raise:MGLAbstractClassException format:@"MGLForegroundStyleLayer is an abstract class"]; return nil; } diff --git a/platform/darwin/src/MGLOfflinePack.h b/platform/darwin/src/MGLOfflinePack.h index dfc47bf1c8..3d22f74e72 100644 --- a/platform/darwin/src/MGLOfflinePack.h +++ b/platform/darwin/src/MGLOfflinePack.h @@ -1,10 +1,13 @@ #import <Foundation/Foundation.h> #import "MGLFoundation.h" +#import "MGLTypes.h" #import "MGLOfflineRegion.h" NS_ASSUME_NONNULL_BEGIN +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLInvalidOfflinePackException; + /** The state an offline pack is currently in. */ diff --git a/platform/darwin/src/MGLOfflinePack.mm b/platform/darwin/src/MGLOfflinePack.mm index 4653021a58..bafb976585 100644 --- a/platform/darwin/src/MGLOfflinePack.mm +++ b/platform/darwin/src/MGLOfflinePack.mm @@ -3,11 +3,16 @@ #import "MGLOfflineStorage_Private.h" #import "MGLOfflineRegion_Private.h" #import "MGLTilePyramidOfflineRegion.h" +#import "MGLTilePyramidOfflineRegion_Private.h" +#import "MGLShapeOfflineRegion.h" +#import "MGLShapeOfflineRegion_Private.h" #import "NSValue+MGLAdditions.h" #include <mbgl/storage/default_file_source.hpp> +const MGLExceptionName MGLInvalidOfflinePackException = @"MGLInvalidOfflinePackException"; + /** Assert that the current offline pack is valid. @@ -17,7 +22,7 @@ #define MGLAssertOfflinePackIsValid() \ do { \ if (_state == MGLOfflinePackStateInvalid) { \ - [NSException raise:@"Invalid offline pack" \ + [NSException raise:MGLInvalidOfflinePackException \ format: \ @"-[MGLOfflineStorage removePack:withCompletionHandler:] has been called " \ @"on this instance of MGLOfflinePack, rendering it invalid. It is an " \ @@ -25,6 +30,12 @@ } \ } while (NO); +@interface MGLTilePyramidOfflineRegion () <MGLOfflineRegion_Private, MGLTilePyramidOfflineRegion_Private> +@end + +@interface MGLShapeOfflineRegion () <MGLOfflineRegion_Private, MGLShapeOfflineRegion_Private> +@end + class MBGLOfflineRegionObserver : public mbgl::OfflineRegionObserver { public: MBGLOfflineRegionObserver(MGLOfflinePack *pack_) : pack(pack_) {} @@ -76,7 +87,17 @@ private: const mbgl::OfflineRegionDefinition ®ionDefinition = _mbglOfflineRegion->getDefinition(); NSAssert([MGLTilePyramidOfflineRegion conformsToProtocol:@protocol(MGLOfflineRegion_Private)], @"MGLTilePyramidOfflineRegion should conform to MGLOfflineRegion_Private."); - return [(id <MGLOfflineRegion_Private>)[MGLTilePyramidOfflineRegion alloc] initWithOfflineRegionDefinition:regionDefinition]; + NSAssert([MGLShapeOfflineRegion conformsToProtocol:@protocol(MGLOfflineRegion_Private)], @"MGLShapeOfflineRegion should conform to MGLOfflineRegion_Private."); + + + + return regionDefinition.match( + [&] (const mbgl::OfflineTilePyramidRegionDefinition def){ + return (id <MGLOfflineRegion>)[[MGLTilePyramidOfflineRegion alloc] initWithOfflineRegionDefinition:def]; + }, + [&] (const mbgl::OfflineGeometryRegionDefinition& def){ + return (id <MGLOfflineRegion>)[[MGLShapeOfflineRegion alloc] initWithOfflineRegionDefinition:def]; + }); } - (NSData *)context { @@ -139,7 +160,7 @@ private: mbgl::DefaultFileSource *mbglFileSource = [[MGLOfflineStorage sharedOfflineStorage] mbglFileSource]; __weak MGLOfflinePack *weakSelf = self; - mbglFileSource->getOfflineRegionStatus(*_mbglOfflineRegion, [&, weakSelf](__unused std::exception_ptr exception, mbgl::optional<mbgl::OfflineRegionStatus> status) { + mbglFileSource->getOfflineRegionStatus(*_mbglOfflineRegion, [&, weakSelf](mbgl::expected<mbgl::OfflineRegionStatus, std::exception_ptr> status) { if (status) { mbgl::OfflineRegionStatus checkedStatus = *status; dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/platform/darwin/src/MGLOfflineRegion.h b/platform/darwin/src/MGLOfflineRegion.h index fe0ab6cb7f..3e0f485e2c 100644 --- a/platform/darwin/src/MGLOfflineRegion.h +++ b/platform/darwin/src/MGLOfflineRegion.h @@ -4,12 +4,21 @@ NS_ASSUME_NONNULL_BEGIN /** An object conforming to the `MGLOfflineRegion` protocol determines which - resources are required by an `MGLOfflinePack` object. At present, only - instances of `MGLTilePyramidOfflineRegion` may be used as `MGLOfflinePack` - regions, but additional conforming implementations may be added in the future. + resources are required by an `MGLOfflinePack` object. */ @protocol MGLOfflineRegion <NSObject> +/** + URL of the style whose resources are required for offline viewing. + + In addition to the JSON stylesheet, different styles may require different font + glyphs, sprite sheets, and other resources. + + The URL may be a full HTTP or HTTPS URL or a Mapbox URL indicating the style’s + map ID (`mapbox://styles/{user}/{style}`). + */ +@property (nonatomic, readonly) NSURL *styleURL; + @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLOfflineRegion_Private.h b/platform/darwin/src/MGLOfflineRegion_Private.h index b1dec8dd64..c1f3fd5200 100644 --- a/platform/darwin/src/MGLOfflineRegion_Private.h +++ b/platform/darwin/src/MGLOfflineRegion_Private.h @@ -9,15 +9,6 @@ NS_ASSUME_NONNULL_BEGIN @protocol MGLOfflineRegion_Private <MGLOfflineRegion> /** - Initializes and returns an offline region backed by the given C++ region - definition object. - - @param definition A reference to an offline region definition backing the - offline region. - */ -- (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineRegionDefinition &)definition; - -/** Creates and returns a C++ offline region definition corresponding to the receiver. */ diff --git a/platform/darwin/src/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h index 250efd23a6..f8ea6e7453 100644 --- a/platform/darwin/src/MGLOfflineStorage.h +++ b/platform/darwin/src/MGLOfflineStorage.h @@ -105,6 +105,8 @@ FOUNDATION_EXTERN MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserI FOUNDATION_EXTERN MGL_EXPORT NSString * const MGLOfflinePackMaximumCountUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyMaximumCount"))); +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLUnsupportedRegionTypeException; + /** A block to be called once an offline pack has been completely created and added. diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index f4e454534d..93a6da36c4 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -30,6 +30,8 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress = @"Progress"; const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError = @"Error"; const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"MaximumCount"; +const MGLExceptionName MGLUnsupportedRegionTypeException = @"MGLUnsupportedRegionTypeException"; + @interface MGLOfflineStorage () @property (nonatomic, strong, readwrite) NSMutableArray<MGLOfflinePack *> *packs; @@ -278,24 +280,24 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu - (void)_addPackForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(MGLOfflinePackAdditionCompletionHandler)completion { if (![region conformsToProtocol:@protocol(MGLOfflineRegion_Private)]) { - [NSException raise:@"Unsupported region type" format: - @"Regions of type %@ are unsupported.", NSStringFromClass([region class])]; + [NSException raise:MGLUnsupportedRegionTypeException + format:@"Regions of type %@ are unsupported.", NSStringFromClass([region class])]; return; } - const mbgl::OfflineTilePyramidRegionDefinition regionDefinition = [(id <MGLOfflineRegion_Private>)region offlineRegionDefinition]; + const mbgl::OfflineRegionDefinition regionDefinition = [(id <MGLOfflineRegion_Private>)region offlineRegionDefinition]; mbgl::OfflineRegionMetadata metadata(context.length); [context getBytes:&metadata[0] length:metadata.size()]; - self.mbglFileSource->createOfflineRegion(regionDefinition, metadata, [&, completion](std::exception_ptr exception, mbgl::optional<mbgl::OfflineRegion> mbglOfflineRegion) { + self.mbglFileSource->createOfflineRegion(regionDefinition, metadata, [&, completion](mbgl::expected<mbgl::OfflineRegion, std::exception_ptr> mbglOfflineRegion) { NSError *error; - if (exception) { - NSString *errorDescription = @(mbgl::util::toString(exception).c_str()); + if (!mbglOfflineRegion) { + NSString *errorDescription = @(mbgl::util::toString(mbglOfflineRegion.error()).c_str()); error = [NSError errorWithDomain:MGLErrorDomain code:-1 userInfo:errorDescription ? @{ NSLocalizedDescriptionKey: errorDescription, } : nil]; } if (completion) { - MGLOfflinePack *pack = mbglOfflineRegion ? [[MGLOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(*mbglOfflineRegion))] : nil; + MGLOfflinePack *pack = mbglOfflineRegion ? [[MGLOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(mbglOfflineRegion.value()))] : nil; dispatch_async(dispatch_get_main_queue(), [&, completion, error, pack](void) { completion(pack, error); }); @@ -345,17 +347,17 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu } - (void)getPacksWithCompletionHandler:(void (^)(NSArray<MGLOfflinePack *> *packs, NSError * _Nullable error))completion { - self.mbglFileSource->listOfflineRegions([&, completion](std::exception_ptr exception, mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) { + self.mbglFileSource->listOfflineRegions([&, completion](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> result) { NSError *error; - if (exception) { + NSMutableArray *packs; + if (!result) { error = [NSError errorWithDomain:MGLErrorDomain code:-1 userInfo:@{ - NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()), + NSLocalizedDescriptionKey: @(mbgl::util::toString(result.error()).c_str()), }]; - } - NSMutableArray *packs; - if (regions) { - packs = [NSMutableArray arrayWithCapacity:regions->size()]; - for (mbgl::OfflineRegion ®ion : *regions) { + } else { + auto& regions = result.value(); + packs = [NSMutableArray arrayWithCapacity:regions.size()]; + for (auto ®ion : regions) { MGLOfflinePack *pack = [[MGLOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(region))]; [packs addObject:pack]; } diff --git a/platform/darwin/src/MGLShape.mm b/platform/darwin/src/MGLShape.mm index e76e06c7e4..59643dcb6a 100644 --- a/platform/darwin/src/MGLShape.mm +++ b/platform/darwin/src/MGLShape.mm @@ -40,7 +40,7 @@ bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D r } - (mbgl::Geometry<double>)geometryObject { - [NSException raise:@"MGLAbstractClassException" + [NSException raise:MGLAbstractClassException format:@"MGLShape is an abstract class"]; return mbgl::Point<double>(); } @@ -103,9 +103,8 @@ bool operator==(const CLLocationCoordinate2D lhs, const CLLocationCoordinate2D r - (CLLocationCoordinate2D)coordinate { - [[NSException exceptionWithName:@"MGLAbstractClassException" - reason:@"MGLShape is an abstract class" - userInfo:nil] raise]; + [NSException raise:MGLAbstractClassException + format:@"MGLShape is an abstract class"]; return kCLLocationCoordinate2DInvalid; } diff --git a/platform/darwin/src/MGLShapeCollection.mm b/platform/darwin/src/MGLShapeCollection.mm index 74e78a764a..5db1ee335c 100644 --- a/platform/darwin/src/MGLShapeCollection.mm +++ b/platform/darwin/src/MGLShapeCollection.mm @@ -1,6 +1,7 @@ #import "MGLShapeCollection.h" #import "MGLShape_Private.h" +#import "MGLFeature.h" #import <mbgl/style/conversion/geojson.hpp> diff --git a/platform/darwin/src/MGLShapeOfflineRegion.h b/platform/darwin/src/MGLShapeOfflineRegion.h new file mode 100644 index 0000000000..ac54dc137b --- /dev/null +++ b/platform/darwin/src/MGLShapeOfflineRegion.h @@ -0,0 +1,72 @@ +#import <Foundation/Foundation.h> + +#import "MGLFoundation.h" +#import "MGLOfflineRegion.h" +#import "MGLShape.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + An offline region defined by a style URL, geographic shape, and + range of zoom levels. + + This class requires fewer resources than MGLTilePyramidOfflineRegion + for irregularly shaped regions. + */ +MGL_EXPORT +@interface MGLShapeOfflineRegion : NSObject <MGLOfflineRegion, NSSecureCoding, NSCopying> + +/** + The shape for the geographic region covered by the downloaded + tiles. + */ +@property (nonatomic, readonly) MGLShape *shape; + +/** + The minimum zoom level for which to download tiles and other resources. + + For more information about zoom levels, `-[MGLMapView zoomLevel]`. + */ +@property (nonatomic, readonly) double minimumZoomLevel; + +/** + The maximum zoom level for which to download tiles and other resources. + + For more information about zoom levels, `-[MGLMapView zoomLevel]`. + */ +@property (nonatomic, readonly) double maximumZoomLevel; + +- (instancetype)init NS_UNAVAILABLE; + +/** + Initializes a newly created offline region with the given style URL, geometry, + and range of zoom levels. + + This is the designated initializer for `MGLShapeOfflineRegion`. + + @param styleURL URL of the map style for which to download resources. The URL + may be a full HTTP or HTTPS URL or a Mapbox URL indicating the style’s map + ID (`mapbox://styles/{user}/{style}`). Specify `nil` for the default style. + Relative file URLs cannot be used as offline style URLs. To download the + online resources required by a local style, specify a URL to an online copy + of the style. + @param shape The shape of the geographic region to be covered by + the downloaded tiles. + @param minimumZoomLevel The minimum zoom level to be covered by the downloaded + tiles. This parameter should be set to at least 0 but no greater than the + value of the `maximumZoomLevel` parameter. For each required tile source, if + this parameter is set to a value less than the tile source’s minimum zoom + level, the download covers zoom levels down to the tile source’s minimum + zoom level. + @param maximumZoomLevel The maximum zoom level to be covered by the downloaded + tiles. This parameter should be set to at least the value of the + `minimumZoomLevel` parameter. For each required tile source, if this + parameter is set to a value greater than the tile source’s minimum zoom + level, the download covers zoom levels up to the tile source’s maximum zoom + level. + */ +- (instancetype)initWithStyleURL:(nullable NSURL *)styleURL shape:(MGLShape *)shape fromZoomLevel:(double)minimumZoomLevel toZoomLevel:(double)maximumZoomLevel NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLShapeOfflineRegion.mm b/platform/darwin/src/MGLShapeOfflineRegion.mm new file mode 100644 index 0000000000..e1393f1199 --- /dev/null +++ b/platform/darwin/src/MGLShapeOfflineRegion.mm @@ -0,0 +1,120 @@ +#import "MGLShapeOfflineRegion.h" + +#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR + #import <Cocoa/Cocoa.h> +#else + #import <UIKit/UIKit.h> +#endif + +#import "MGLOfflineRegion_Private.h" +#import "MGLShapeOfflineRegion_Private.h" +#import "MGLFeature_Private.h" +#import "MGLShape_Private.h" +#import "MGLStyle.h" + +@interface MGLShapeOfflineRegion () <MGLOfflineRegion_Private, MGLShapeOfflineRegion_Private> + +@end + +@implementation MGLShapeOfflineRegion { + NSURL *_styleURL; +} + +@synthesize styleURL = _styleURL; + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (instancetype)init { + [NSException raise:@"Method unavailable" + format: + @"-[MGLShapeOfflineRegion init] is unavailable. " + @"Use -initWithStyleURL:shape:fromZoomLevel:toZoomLevel: instead."]; + return nil; +} + +- (instancetype)initWithStyleURL:(NSURL *)styleURL shape:(MGLShape *)shape fromZoomLevel:(double)minimumZoomLevel toZoomLevel:(double)maximumZoomLevel { + if (self = [super init]) { + if (!styleURL) { + styleURL = [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion]; + } + + if (!styleURL.scheme) { + [NSException raise:@"Invalid style URL" format: + @"%@ does not support setting a relative file URL as the style URL. " + @"To download the online resources required by this style, " + @"specify a URL to an online copy of this style. " + @"For Mapbox-hosted styles, use the mapbox: scheme.", + NSStringFromClass([self class])]; + } + + _styleURL = styleURL; + _shape = shape; + _minimumZoomLevel = minimumZoomLevel; + _maximumZoomLevel = maximumZoomLevel; + } + return self; +} + +- (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineGeometryRegionDefinition &)definition { + NSURL *styleURL = [NSURL URLWithString:@(definition.styleURL.c_str())]; + MGLShape *shape = MGLShapeFromGeoJSON(definition.geometry); + return [self initWithStyleURL:styleURL shape:shape fromZoomLevel:definition.minZoom toZoomLevel:definition.maxZoom]; +} + +- (const mbgl::OfflineRegionDefinition)offlineRegionDefinition { +#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR + const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale]; +#elif TARGET_OS_MAC + const float scaleFactor = [NSScreen mainScreen].backingScaleFactor; +#endif + return mbgl::OfflineGeometryRegionDefinition(_styleURL.absoluteString.UTF8String, + _shape.geometryObject, + _minimumZoomLevel, _maximumZoomLevel, + scaleFactor); +} + +- (nullable instancetype)initWithCoder:(NSCoder *)coder { + NSURL *styleURL = [coder decodeObjectForKey:@"styleURL"]; + MGLShape * shape = [coder decodeObjectForKey:@"shape"]; + double minimumZoomLevel = [coder decodeDoubleForKey:@"minimumZoomLevel"]; + double maximumZoomLevel = [coder decodeDoubleForKey:@"maximumZoomLevel"]; + + return [self initWithStyleURL:styleURL shape:shape fromZoomLevel:minimumZoomLevel toZoomLevel:maximumZoomLevel]; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeObject:_styleURL forKey:@"styleURL"]; + [coder encodeObject:_shape forKey:@"shape"]; + [coder encodeDouble:_maximumZoomLevel forKey:@"maximumZoomLevel"]; + [coder encodeDouble:_minimumZoomLevel forKey:@"minimumZoomLevel"]; +} + +- (id)copyWithZone:(nullable NSZone *)zone { + return [[[self class] allocWithZone:zone] initWithStyleURL:_styleURL shape:_shape fromZoomLevel:_minimumZoomLevel toZoomLevel:_maximumZoomLevel]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } + if (![other isKindOfClass:[self class]]) { + return NO; + } + + MGLShapeOfflineRegion *otherRegion = other; + return (_minimumZoomLevel == otherRegion->_minimumZoomLevel + && _maximumZoomLevel == otherRegion->_maximumZoomLevel + && _shape.geometryObject == otherRegion->_shape.geometryObject + && [_styleURL isEqual:otherRegion->_styleURL]); +} + +- (NSUInteger)hash { + return (_styleURL.hash + + _shape.hash + + @(_minimumZoomLevel).hash + @(_maximumZoomLevel).hash); +} + +@end diff --git a/platform/darwin/src/MGLShapeOfflineRegion_Private.h b/platform/darwin/src/MGLShapeOfflineRegion_Private.h new file mode 100644 index 0000000000..2ab44ad405 --- /dev/null +++ b/platform/darwin/src/MGLShapeOfflineRegion_Private.h @@ -0,0 +1,22 @@ +#import <Foundation/Foundation.h> + +#import "MGLOfflineRegion.h" + +#include <mbgl/storage/offline.hpp> + +NS_ASSUME_NONNULL_BEGIN + +@protocol MGLShapeOfflineRegion_Private <MGLOfflineRegion> + +/** + Initializes and returns an offline region backed by the given C++ region + definition object. + + @param definition A reference to an offline region definition backing the + offline region. + */ +- (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineGeometryRegionDefinition &)definition; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h index e5c62515e5..c80c329cbc 100644 --- a/platform/darwin/src/MGLShapeSource.h +++ b/platform/darwin/src/MGLShapeSource.h @@ -96,6 +96,18 @@ FOUNDATION_EXTERN MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuff FOUNDATION_EXTERN MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance; /** + An `NSNumber` object containing a Boolean enabling or disabling calculating line distance metrics. + + Set this property to `YES` in order for the `MGLLineStyleLayer.lineGradient` property to have its intended effect. + The default value is `NO`. + + This option corresponds to the + <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson-lineMetrics"><code>lineMetrics</code></a> + source property in the Mapbox Style Specification. + */ +FOUNDATION_EXTERN MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionLineDistanceMetrics; + +/** `MGLShapeSource` is a map content source that supplies vector shapes to be shown on the map. The shapes may be instances of `MGLShape` or `MGLFeature`, or they may be defined by local or external diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm index 1425269012..c960f2a4a7 100644 --- a/platform/darwin/src/MGLShapeSource.mm +++ b/platform/darwin/src/MGLShapeSource.mm @@ -20,6 +20,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSour const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering"; const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel"; const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance"; +const MGLShapeSourceOption MGLShapeSourceOptionLineDistanceMetrics = @"MGLShapeSourceOptionLineDistanceMetrics"; mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShapeSourceOption, id> *options) { auto geoJSONOptions = mbgl::style::GeoJSONOptions(); @@ -80,6 +81,14 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShap geoJSONOptions.cluster = value.boolValue; } + if (NSNumber *value = options[MGLShapeSourceOptionLineDistanceMetrics]) { + if (![value isKindOfClass:[NSNumber class]]) { + [NSException raise:NSInvalidArgumentException + format:@"MGLShapeSourceOptionLineDistanceMetrics must be an NSNumber."]; + } + geoJSONOptions.lineMetrics = value.boolValue; + } + return geoJSONOptions; } @@ -105,6 +114,14 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShap auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options); auto source = std::make_unique<mbgl::style::GeoJSONSource>(identifier.UTF8String, geoJSONOptions); if (self = [super initWithPendingSource:std::move(source)]) { + if ([shape isMemberOfClass:[MGLShapeCollection class]]) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSLog(@"MGLShapeCollection initialized with MGLFeatures will not retain attributes." + @"Use MGLShapeCollectionFeature to retain attributes instead." + @"This will be logged only once."); + }); + } self.shape = shape; } return self; diff --git a/platform/darwin/src/MGLSource.mm b/platform/darwin/src/MGLSource.mm index 6d57e14e8c..a32e223782 100644 --- a/platform/darwin/src/MGLSource.mm +++ b/platform/darwin/src/MGLSource.mm @@ -48,7 +48,7 @@ - (void)addToMapView:(MGLMapView *)mapView { if (_pendingSource == nullptr) { - [NSException raise:@"MGLRedundantSourceException" + [NSException raise:MGLRedundantSourceException format:@"This instance %@ was already added to %@. Adding the same source instance " \ "to the style more than once is invalid.", self, mapView.style]; } diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index 814a09ed21..7b62432d36 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -31,6 +31,12 @@ NS_ASSUME_NONNULL_BEGIN */ static MGL_EXPORT const NSInteger MGLStyleDefaultVersion = 10; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLInvalidStyleURLException; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLRedundantLayerException; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLRedundantLayerIdentifierException; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLRedundantSourceException; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLRedundantSourceIdentifierException; + /** The proxy object for the current map style. diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 3f9bfbf8ca..88499cf9bb 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -55,6 +55,12 @@ #import "NSImage+MGLAdditions.h" #endif +const MGLExceptionName MGLInvalidStyleURLException = @"MGLInvalidStyleURLException"; +const MGLExceptionName MGLRedundantLayerException = @"MGLRedundantLayerException"; +const MGLExceptionName MGLRedundantLayerIdentifierException = @"MGLRedundantLayerIdentifierException"; +const MGLExceptionName MGLRedundantSourceException = @"MGLRedundantSourceException"; +const MGLExceptionName MGLRedundantSourceIdentifierException = @"MGLRedundantSourceIdentifierException"; + /** Model class for localization changes. */ @@ -177,7 +183,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, } - (MGLSource *)sourceFromMBGLSource:(mbgl::style::Source *)rawSource { - if (MGLSource *source = rawSource->peer.has_value() ? mbgl::util::any_cast<SourceWrapper>(rawSource->peer).source : nil) { + if (MGLSource *source = rawSource->peer.has_value() ? rawSource->peer.get<SourceWrapper>().source : nil) { return source; } @@ -210,7 +216,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, try { [source addToMapView:self.mapView]; } catch (std::runtime_error & err) { - [NSException raise:@"MGLRedundantSourceIdentifierException" format:@"%s", err.what()]; + [NSException raise:MGLRedundantSourceIdentifierException format:@"%s", err.what()]; } } @@ -313,14 +319,14 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, MGLStyleLayer *sibling = layers.size() ? [self layerFromMBGLLayer:layers.at(0)] : nil; [styleLayer addToStyle:self belowLayer:sibling]; } catch (const std::runtime_error & err) { - [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; + [NSException raise:MGLRedundantLayerIdentifierException format:@"%s", err.what()]; } } else { try { MGLStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(index)]; [styleLayer addToStyle:self belowLayer:sibling]; } catch (std::runtime_error & err) { - [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; + [NSException raise:MGLRedundantLayerIdentifierException format:@"%s", err.what()]; } } } @@ -341,7 +347,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, { NSParameterAssert(rawLayer); - if (MGLStyleLayer *layer = rawLayer->peer.has_value() ? mbgl::util::any_cast<LayerWrapper>(&(rawLayer->peer))->layer : nil) { + if (MGLStyleLayer *layer = rawLayer->peer.has_value() ? rawLayer->peer.get<LayerWrapper>().layer : nil) { return layer; } @@ -402,7 +408,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, try { [layer addToStyle:self belowLayer:nil]; } catch (std::runtime_error & err) { - [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; + [NSException raise:MGLRedundantLayerIdentifierException format:@"%s", err.what()]; } [self didChangeValueForKey:@"layers"]; } @@ -431,7 +437,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, try { [layer addToStyle:self belowLayer:sibling]; } catch (std::runtime_error & err) { - [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; + [NSException raise:MGLRedundantLayerIdentifierException format:@"%s", err.what()]; } [self didChangeValueForKey:@"layers"]; } @@ -473,14 +479,14 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, try { [layer addToStyle:self belowLayer:nil]; } catch (std::runtime_error & err) { - [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; + [NSException raise:MGLRedundantLayerIdentifierException format:@"%s", err.what()]; } } else { MGLStyleLayer *sibling = [self layerFromMBGLLayer:layers.at(index + 1)]; try { [layer addToStyle:self belowLayer:sibling]; } catch (std::runtime_error & err) { - [NSException raise:@"MGLRedundantLayerIdentifierException" format:@"%s", err.what()]; + [NSException raise:MGLRedundantLayerIdentifierException format:@"%s", err.what()]; } } [self didChangeValueForKey:@"layers"]; diff --git a/platform/darwin/src/MGLStyleLayer.h b/platform/darwin/src/MGLStyleLayer.h index b610a27607..60634946cb 100644 --- a/platform/darwin/src/MGLStyleLayer.h +++ b/platform/darwin/src/MGLStyleLayer.h @@ -5,6 +5,8 @@ NS_ASSUME_NONNULL_BEGIN +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLInvalidStyleLayerException; + /** `MGLStyleLayer` is an abstract base class for style layers. A style layer manages the layout and appearance of content at a specific z-index in a style. diff --git a/platform/darwin/src/MGLStyleLayer.mm b/platform/darwin/src/MGLStyleLayer.mm index 6400b8fcbf..05885bc63e 100644 --- a/platform/darwin/src/MGLStyleLayer.mm +++ b/platform/darwin/src/MGLStyleLayer.mm @@ -4,6 +4,8 @@ #include <mbgl/style/style.hpp> #include <mbgl/style/layer.hpp> +const MGLExceptionName MGLInvalidStyleLayerException = @"MGLInvalidStyleLayerException"; + @interface MGLStyleLayer () @property (nonatomic, readonly) mbgl::style::Layer *rawLayer; @@ -33,7 +35,7 @@ - (void)addToStyle:(MGLStyle *)style belowLayer:(MGLStyleLayer *)otherLayer { if (_pendingLayer == nullptr) { - [NSException raise:@"MGLRedundantLayerException" + [NSException raise:MGLRedundantLayerException format:@"This instance %@ was already added to %@. Adding the same layer instance " \ "to the style more than once is invalid.", self, style]; } diff --git a/platform/darwin/src/MGLStyleLayer_Private.h b/platform/darwin/src/MGLStyleLayer_Private.h index 9bee013c3d..ea43c680e0 100644 --- a/platform/darwin/src/MGLStyleLayer_Private.h +++ b/platform/darwin/src/MGLStyleLayer_Private.h @@ -24,7 +24,7 @@ struct LayerWrapper { #define MGLAssertStyleLayerIsValid() \ do { \ if (!self.rawLayer) { \ - [NSException raise:@"Invalid style layer" \ + [NSException raise:MGLInvalidStyleLayerException \ format: \ @"-[MGLStyle removeLayer:] has been called " \ @"with this instance but another style layer instance was added with the same identifer. It is an " \ diff --git a/platform/darwin/src/MGLTilePyramidOfflineRegion.h b/platform/darwin/src/MGLTilePyramidOfflineRegion.h index 31e5a41920..4fbb68dbc6 100644 --- a/platform/darwin/src/MGLTilePyramidOfflineRegion.h +++ b/platform/darwin/src/MGLTilePyramidOfflineRegion.h @@ -9,22 +9,14 @@ NS_ASSUME_NONNULL_BEGIN /** An offline region defined by a style URL, geographic coordinate bounds, and range of zoom levels. + + To minimize the resources required by an irregularly shaped offline region, + use the MGLShapeOfflineRegion class instead. */ MGL_EXPORT @interface MGLTilePyramidOfflineRegion : NSObject <MGLOfflineRegion, NSSecureCoding, NSCopying> /** - URL of the style whose resources are required for offline viewing. - - In addition to the JSON stylesheet, different styles may require different font - glyphs, sprite sheets, and other resources. - - The URL may be a full HTTP or HTTPS URL or a Mapbox URL indicating the style’s - map ID (`mapbox://styles/{user}/{style}`). - */ -@property (nonatomic, readonly) NSURL *styleURL; - -/** The coordinate bounds for the geographic region covered by the downloaded tiles. */ diff --git a/platform/darwin/src/MGLTilePyramidOfflineRegion.mm b/platform/darwin/src/MGLTilePyramidOfflineRegion.mm index e0d56484bf..0766d224da 100644 --- a/platform/darwin/src/MGLTilePyramidOfflineRegion.mm +++ b/platform/darwin/src/MGLTilePyramidOfflineRegion.mm @@ -5,10 +5,11 @@ #endif #import "MGLOfflineRegion_Private.h" +#import "MGLTilePyramidOfflineRegion_Private.h" #import "MGLGeometry_Private.h" #import "MGLStyle.h" -@interface MGLTilePyramidOfflineRegion () <MGLOfflineRegion_Private> +@interface MGLTilePyramidOfflineRegion () <MGLOfflineRegion_Private, MGLTilePyramidOfflineRegion_Private> @end @@ -23,8 +24,7 @@ } - (instancetype)init { - [NSException raise:@"Method unavailable" - format: + [NSException raise:NSGenericException format: @"-[MGLTilePyramidOfflineRegion init] is unavailable. " @"Use -initWithStyleURL:bounds:fromZoomLevel:toZoomLevel: instead."]; return nil; @@ -37,7 +37,7 @@ } if (!styleURL.scheme) { - [NSException raise:@"Invalid style URL" format: + [NSException raise:MGLInvalidStyleURLException format: @"%@ does not support setting a relative file URL as the style URL. " @"To download the online resources required by this style, " @"specify a URL to an online copy of this style. " @@ -53,7 +53,7 @@ return self; } -- (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineRegionDefinition &)definition { +- (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineTilePyramidRegionDefinition &)definition { NSURL *styleURL = [NSURL URLWithString:@(definition.styleURL.c_str())]; MGLCoordinateBounds bounds = MGLCoordinateBoundsFromLatLngBounds(definition.bounds); return [self initWithStyleURL:styleURL bounds:bounds fromZoomLevel:definition.minZoom toZoomLevel:definition.maxZoom]; diff --git a/platform/darwin/src/MGLTilePyramidOfflineRegion_Private.h b/platform/darwin/src/MGLTilePyramidOfflineRegion_Private.h new file mode 100644 index 0000000000..90d8e05477 --- /dev/null +++ b/platform/darwin/src/MGLTilePyramidOfflineRegion_Private.h @@ -0,0 +1,22 @@ +#import <Foundation/Foundation.h> + +#import "MGLOfflineRegion.h" + +#include <mbgl/storage/offline.hpp> + +NS_ASSUME_NONNULL_BEGIN + +@protocol MGLTilePyramidOfflineRegion_Private <MGLOfflineRegion> + +/** + Initializes and returns an offline region backed by the given C++ region + definition object. + + @param definition A reference to an offline region definition backing the + offline region. + */ +- (instancetype)initWithOfflineRegionDefinition:(const mbgl::OfflineTilePyramidRegionDefinition &)definition; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm index 2fafc6fb51..eef3b33430 100644 --- a/platform/darwin/src/MGLTileSource.mm +++ b/platform/darwin/src/MGLTileSource.mm @@ -25,7 +25,7 @@ const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOption @implementation MGLTileSource - (NSURL *)configurationURL { - [NSException raise:@"MGLAbstractClassException" + [NSException raise:MGLAbstractClassException format:@"MGLTileSource is an abstract class"]; return nil; } @@ -41,7 +41,7 @@ const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOption } - (NSString *)attributionHTMLString { - [NSException raise:@"MGLAbstractClassException" + [NSException raise:MGLAbstractClassException format:@"MGLTileSource is an abstract class"]; return nil; } diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h index 3abc116ad7..1c90d7968b 100644 --- a/platform/darwin/src/MGLTypes.h +++ b/platform/darwin/src/MGLTypes.h @@ -23,12 +23,13 @@ NS_ASSUME_NONNULL_BEGIN -#ifndef NS_STRING_ENUM - #define NS_STRING_ENUM - #define NS_EXTENSIBLE_STRING_ENUM - typedef NSString *NSErrorDomain; - typedef NSString *NSNotificationName; -#endif +typedef NSString *MGLExceptionName NS_TYPED_EXTENSIBLE_ENUM; + +/** + :nodoc: Generic exceptions used across multiple disparate classes. Exceptions + that are unique to a class or class-cluster should be defined in those headers. + */ +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLAbstractClassException; /** Indicates an error occurred in the Mapbox SDK. */ FOUNDATION_EXTERN MGL_EXPORT NSErrorDomain const MGLErrorDomain; diff --git a/platform/darwin/src/MGLTypes.m b/platform/darwin/src/MGLTypes.m index 01e9a1467c..89f88a9c1d 100644 --- a/platform/darwin/src/MGLTypes.m +++ b/platform/darwin/src/MGLTypes.m @@ -1,3 +1,5 @@ #import "MGLTypes.h" +const MGLExceptionName MGLAbstractClassException = @"MGLAbstractClassException"; + NSString * const MGLErrorDomain = @"MGLErrorDomain"; diff --git a/platform/darwin/src/MGLVectorStyleLayer.m b/platform/darwin/src/MGLVectorStyleLayer.m index da6da0ea7f..23f3556e0b 100644 --- a/platform/darwin/src/MGLVectorStyleLayer.m +++ b/platform/darwin/src/MGLVectorStyleLayer.m @@ -3,12 +3,12 @@ @implementation MGLVectorStyleLayer - (void)setPredicate:(NSPredicate *)predicate { - [NSException raise:@"MGLAbstractClassException" + [NSException raise:MGLAbstractClassException format:@"MGLVectorStyleLayer is an abstract class"]; } - (NSPredicate *)predicate { - [NSException raise:@"MGLAbstractClassException" + [NSException raise:MGLAbstractClassException format:@"MGLVectorStyleLayer is an abstract class"]; return nil; } diff --git a/platform/darwin/src/NSBundle+MGLAdditions.h b/platform/darwin/src/NSBundle+MGLAdditions.h index 86dc27f22c..dcafefedec 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.h +++ b/platform/darwin/src/NSBundle+MGLAdditions.h @@ -27,6 +27,8 @@ NS_ASSUME_NONNULL_BEGIN #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \ [[NSBundle mgl_frameworkBundle] localizedStringForKey:(key) value:(val) table:(tbl)] +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLBundleNotFoundException; + @interface NSBundle (MGLAdditions) /// Returns the bundle containing the SDK’s classes and Info.plist file. diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m index 37f78963d3..d472e40b1f 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.m +++ b/platform/darwin/src/NSBundle+MGLAdditions.m @@ -2,6 +2,8 @@ #import "MGLAccountManager.h" +const MGLExceptionName MGLBundleNotFoundException = @"MGLBundleNotFoundException"; + @implementation NSBundle (MGLAdditions) + (instancetype)mgl_frameworkBundle { @@ -14,8 +16,8 @@ if (bundlePath) { bundle = [self bundleWithPath:bundlePath]; } else { - [NSException raise:@"MGLBundleNotFoundException" format: - @"The Mapbox framework bundle could not be found. If using the Mapbox Maps SDK for iOS as a static framework, make sure that Mapbox.bundle is copied into the root of the app bundle."]; + [NSException raise:MGLBundleNotFoundException + format:@"The Mapbox framework bundle could not be found. If using the Mapbox Maps SDK for iOS as a static framework, make sure that Mapbox.bundle is copied into the root of the app bundle."]; } } diff --git a/platform/darwin/test/MGLOfflinePackTests.m b/platform/darwin/test/MGLOfflinePackTests.m index f58f306e5d..a33665ff0a 100644 --- a/platform/darwin/test/MGLOfflinePackTests.m +++ b/platform/darwin/test/MGLOfflinePackTests.m @@ -12,10 +12,10 @@ XCTAssertEqual(invalidPack.state, MGLOfflinePackStateInvalid, @"Offline pack should be invalid when initialized independently of MGLOfflineStorage."); - XCTAssertThrowsSpecificNamed(invalidPack.region, NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when accessing its region."); - XCTAssertThrowsSpecificNamed(invalidPack.context, NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when accessing its context."); - XCTAssertThrowsSpecificNamed([invalidPack resume], NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when being resumed."); - XCTAssertThrowsSpecificNamed([invalidPack suspend], NSException, @"Invalid offline pack", @"Invalid offline pack should raise an exception when being suspended."); + XCTAssertThrowsSpecificNamed(invalidPack.region, NSException, MGLInvalidOfflinePackException, @"Invalid offline pack should raise an exception when accessing its region."); + XCTAssertThrowsSpecificNamed(invalidPack.context, NSException, MGLInvalidOfflinePackException, @"Invalid offline pack should raise an exception when accessing its context."); + XCTAssertThrowsSpecificNamed([invalidPack resume], NSException, MGLInvalidOfflinePackException, @"Invalid offline pack should raise an exception when being resumed."); + XCTAssertThrowsSpecificNamed([invalidPack suspend], NSException, MGLInvalidOfflinePackException, @"Invalid offline pack should raise an exception when being suspended."); } - (void)testProgressBoxing { diff --git a/platform/darwin/test/MGLOfflineRegionTests.m b/platform/darwin/test/MGLOfflineRegionTests.m index ff079fe798..eac6da9b54 100644 --- a/platform/darwin/test/MGLOfflineRegionTests.m +++ b/platform/darwin/test/MGLOfflineRegionTests.m @@ -14,10 +14,10 @@ XCTAssertEqualObjects(region.styleURL, [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion], @"Streets isn’t the default style."); NSURL *localURL = [NSURL URLWithString:@"beautiful.style"]; - XCTAssertThrowsSpecificNamed([[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:localURL bounds:bounds fromZoomLevel:0 toZoomLevel:DBL_MAX], NSException, @"Invalid style URL", @"No exception raised when initializing region with a local file URL as the style URL."); + XCTAssertThrowsSpecificNamed([[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:localURL bounds:bounds fromZoomLevel:0 toZoomLevel:DBL_MAX], NSException, MGLInvalidStyleURLException, @"No exception raised when initializing region with a local file URL as the style URL."); } -- (void)testEquality { +- (void)testTilePyramidRegionEquality { MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(kCLLocationCoordinate2DInvalid, kCLLocationCoordinate2DInvalid); MGLTilePyramidOfflineRegion *original = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:[MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion] bounds:bounds fromZoomLevel:5 toZoomLevel:10]; MGLTilePyramidOfflineRegion *copy = [original copy]; @@ -29,4 +29,20 @@ XCTAssertEqual(original.maximumZoomLevel, original.maximumZoomLevel, @"Maximum zoom level has changed."); } +- (void)testGeometryRegionEquality { + NSString *geojson = @"{\"type\": \"Point\", \"coordinates\": [-3.8671874999999996, 52.482780222078226] }"; + NSError *error; + MGLShape *shape = [MGLShape shapeWithData: [geojson dataUsingEncoding:NSUTF8StringEncoding] encoding: NSUTF8StringEncoding error:&error]; + XCTAssertNil(error); + + MGLShapeOfflineRegion *original = [[MGLShapeOfflineRegion alloc] initWithStyleURL:[MGLStyle lightStyleURLWithVersion:MGLStyleDefaultVersion] shape:shape fromZoomLevel:5 toZoomLevel:10]; + MGLShapeOfflineRegion *copy = [original copy]; + XCTAssertEqualObjects(original, copy, @"Shape region should be equal to its copy."); + + XCTAssertEqualObjects(original.styleURL, copy.styleURL, @"Style URL has changed."); + XCTAssertEqualObjects(original.shape, copy.shape, @"Geometry has changed."); + XCTAssertEqual(original.minimumZoomLevel, original.minimumZoomLevel, @"Minimum zoom level has changed."); + XCTAssertEqual(original.maximumZoomLevel, original.maximumZoomLevel, @"Maximum zoom level has changed."); +} + @end diff --git a/platform/darwin/test/MGLOfflineStorageTests.mm b/platform/darwin/test/MGLOfflineStorageTests.mm index 28c6633028..e9e2467f21 100644 --- a/platform/darwin/test/MGLOfflineStorageTests.mm +++ b/platform/darwin/test/MGLOfflineStorageTests.mm @@ -36,7 +36,7 @@ XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage], [MGLOfflineStorage sharedOfflineStorage], @"There should only be one shared offline storage object."); } -- (void)testAddPack { +- (void)testAddPackForBounds { NSUInteger countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count; NSURL *styleURL = [MGLStyle lightStyleURLWithVersion:8]; @@ -109,6 +109,78 @@ [self waitForExpectationsWithTimeout:1 handler:nil]; } +- (void)testAddPackForGeometry { + NSUInteger countOfPacks = [MGLOfflineStorage sharedOfflineStorage].packs.count; + + NSURL *styleURL = [MGLStyle lightStyleURLWithVersion:8]; + double zoomLevel = 20; + NSString *geojson = @"{ \"type\": \"Polygon\", \"coordinates\": [ [ [ 5.1299285888671875, 52.10365839097971 ], [ 5.103063583374023, 52.110037078604236 ], [ 5.080232620239258, 52.09548601177304 ], [ 5.106925964355469, 52.07987524347506 ], [ 5.1299285888671875, 52.10365839097971 ] ] ]}"; + NSError *error; + MGLShape *shape = [MGLShape shapeWithData: [geojson dataUsingEncoding:NSUTF8StringEncoding] encoding: NSUTF8StringEncoding error:&error]; + XCTAssertNil(error); + MGLShapeOfflineRegion *region = [[MGLShapeOfflineRegion alloc] initWithStyleURL:styleURL shape:shape fromZoomLevel:zoomLevel toZoomLevel:zoomLevel]; + + + NSString *nameKey = @"Name"; + NSString *name = @"Utrecht centrum"; + + NSData *context = [NSKeyedArchiver archivedDataWithRootObject:@{nameKey: name}]; + + __block MGLOfflinePack *pack; + [self keyValueObservingExpectationForObject:[MGLOfflineStorage sharedOfflineStorage] keyPath:@"packs" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) { + const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]); + NSIndexSet *indices = change[NSKeyValueChangeIndexesKey]; + return changeKind == NSKeyValueChangeInsertion && indices.count == 1; + }]; + XCTestExpectation *additionCompletionHandlerExpectation = [self expectationWithDescription:@"add pack completion handler"]; + [[MGLOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack * _Nullable completionHandlerPack, NSError * _Nullable error) { + XCTAssertNotNil(completionHandlerPack, @"Added pack should exist."); + XCTAssertEqual(completionHandlerPack.state, MGLOfflinePackStateInactive, @"New pack should initially have inactive state."); + pack = completionHandlerPack; + [additionCompletionHandlerExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:2 handler:nil]; + + XCTAssertEqual([MGLOfflineStorage sharedOfflineStorage].packs.count, countOfPacks + 1, @"Added pack should have been added to the canonical collection of packs owned by the shared offline storage object. This assertion can fail if this test is run before -testAAALoadPacks."); + + XCTAssertEqual(pack, [MGLOfflineStorage sharedOfflineStorage].packs.lastObject, @"Pack should be appended to end of packs array."); + + XCTAssertEqualObjects(pack.region, region, @"Added pack’s region has changed."); + + NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context]; + XCTAssert([userInfo isKindOfClass:[NSDictionary class]], @"Context of offline pack isn’t a dictionary."); + XCTAssert([userInfo[nameKey] isKindOfClass:[NSString class]], @"Name of offline pack isn’t a string."); + XCTAssertEqualObjects(userInfo[nameKey], name, @"Name of offline pack has changed."); + + XCTAssertEqual(pack.state, MGLOfflinePackStateInactive, @"New pack should initially have inactive state."); + + [self keyValueObservingExpectationForObject:pack keyPath:@"state" handler:^BOOL(id _Nonnull observedObject, NSDictionary * _Nonnull change) { + const auto changeKind = static_cast<NSKeyValueChange>([change[NSKeyValueChangeKindKey] unsignedLongValue]); + const auto state = static_cast<MGLOfflinePackState>([change[NSKeyValueChangeNewKey] longValue]); + return changeKind == NSKeyValueChangeSetting && state == MGLOfflinePackStateInactive; + }]; + [self expectationForNotification:MGLOfflinePackProgressChangedNotification object:pack handler:^BOOL(NSNotification * _Nonnull notification) { + MGLOfflinePack *notificationPack = notification.object; + XCTAssert([notificationPack isKindOfClass:[MGLOfflinePack class]], @"Object of notification should be an MGLOfflinePack."); + + NSDictionary *userInfo = notification.userInfo; + XCTAssertNotNil(userInfo, @"Progress change notification should have a userInfo dictionary."); + + NSNumber *stateNumber = userInfo[MGLOfflinePackUserInfoKeyState]; + XCTAssert([stateNumber isKindOfClass:[NSNumber class]], @"Progress change notification’s state should be an NSNumber."); + XCTAssertEqual(stateNumber.integerValue, pack.state, @"State in a progress change notification should match the pack’s state."); + + NSValue *progressValue = userInfo[MGLOfflinePackUserInfoKeyProgress]; + XCTAssert([progressValue isKindOfClass:[NSValue class]], @"Progress change notification’s progress should be an NSValue."); + XCTAssertEqualObjects(progressValue, [NSValue valueWithMGLOfflinePackProgress:pack.progress], @"Progress change notification’s progress should match pack’s progress."); + + return notificationPack == pack && pack.state == MGLOfflinePackStateInactive; + }]; + [pack requestProgress]; + [self waitForExpectationsWithTimeout:1 handler:nil]; + pack = nil; +} + - (void)testBackupExclusion { NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask diff --git a/platform/darwin/test/MGLShapeSourceTests.mm b/platform/darwin/test/MGLShapeSourceTests.mm index d3f9a599e2..c4f791f958 100644 --- a/platform/darwin/test/MGLShapeSourceTests.mm +++ b/platform/darwin/test/MGLShapeSourceTests.mm @@ -18,7 +18,8 @@ MGLShapeSourceOptionMaximumZoomLevelForClustering: @98, MGLShapeSourceOptionMaximumZoomLevel: @99, MGLShapeSourceOptionBuffer: @1976, - MGLShapeSourceOptionSimplificationTolerance: @0.42}; + MGLShapeSourceOptionSimplificationTolerance: @0.42, + MGLShapeSourceOptionLineDistanceMetrics: @YES}; auto mbglOptions = MGLGeoJSONOptionsFromDictionary(options); XCTAssertTrue(mbglOptions.cluster); @@ -27,6 +28,7 @@ XCTAssertEqual(mbglOptions.maxzoom, 99); XCTAssertEqual(mbglOptions.buffer, 1976); XCTAssertEqual(mbglOptions.tolerance, 0.42); + XCTAssertTrue(mbglOptions.lineMetrics); options = @{MGLShapeSourceOptionClustered: @"number 1"}; XCTAssertThrows(MGLGeoJSONOptionsFromDictionary(options)); diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index 32243c1bec..f2a02963fc 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -156,15 +156,15 @@ - (void)testAddingSourcesTwice { MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"shapeSource" shape:nil options:nil]; [self.style addSource:shapeSource]; - XCTAssertThrowsSpecificNamed([self.style addSource:shapeSource], NSException, @"MGLRedundantSourceException"); + XCTAssertThrowsSpecificNamed([self.style addSource:shapeSource], NSException, MGLRedundantSourceException); MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"rasterTileSource" configurationURL:[NSURL URLWithString:@".json"] tileSize:42]; [self.style addSource:rasterTileSource]; - XCTAssertThrowsSpecificNamed([self.style addSource:rasterTileSource], NSException, @"MGLRedundantSourceException"); + XCTAssertThrowsSpecificNamed([self.style addSource:rasterTileSource], NSException, MGLRedundantSourceException); MGLVectorTileSource *vectorTileSource = [[MGLVectorTileSource alloc] initWithIdentifier:@"vectorTileSource" configurationURL:[NSURL URLWithString:@".json"]]; [self.style addSource:vectorTileSource]; - XCTAssertThrowsSpecificNamed([self.style addSource:vectorTileSource], NSException, @"MGLRedundantSourceException"); + XCTAssertThrowsSpecificNamed([self.style addSource:vectorTileSource], NSException, MGLRedundantSourceException); } - (void)testAddingSourcesWithDuplicateIdentifiers { @@ -172,7 +172,7 @@ MGLVectorTileSource *source2 = [[MGLVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]]; [self.style addSource: source1]; - XCTAssertThrowsSpecificNamed([self.style addSource: source2], NSException, @"MGLRedundantSourceIdentifierException"); + XCTAssertThrowsSpecificNamed([self.style addSource: source2], NSException, MGLRedundantSourceIdentifierException); } - (void)testRemovingSourcesBeforeAddingThem { @@ -265,27 +265,27 @@ MGLBackgroundStyleLayer *backgroundLayer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"backgroundLayer"]; [self.style addLayer:backgroundLayer]; - XCTAssertThrowsSpecificNamed([self.style addLayer:backgroundLayer], NSException, @"MGLRedundantLayerException"); + XCTAssertThrowsSpecificNamed([self.style addLayer:backgroundLayer], NSException, MGLRedundantLayerException); MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"circleLayer" source:source]; [self.style addLayer:circleLayer]; - XCTAssertThrowsSpecificNamed([self.style addLayer:circleLayer], NSException, @"MGLRedundantLayerException"); + XCTAssertThrowsSpecificNamed([self.style addLayer:circleLayer], NSException, MGLRedundantLayerException); MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:source]; [self.style addLayer:fillLayer]; - XCTAssertThrowsSpecificNamed([self.style addLayer:fillLayer], NSException, @"MGLRedundantLayerException"); + XCTAssertThrowsSpecificNamed([self.style addLayer:fillLayer], NSException, MGLRedundantLayerException); MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"lineLayer" source:source]; [self.style addLayer:lineLayer]; - XCTAssertThrowsSpecificNamed([self.style addLayer:lineLayer], NSException, @"MGLRedundantLayerException"); + XCTAssertThrowsSpecificNamed([self.style addLayer:lineLayer], NSException, MGLRedundantLayerException); MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"rasterLayer" source:source]; [self.style addLayer:rasterLayer]; - XCTAssertThrowsSpecificNamed([self.style addLayer:rasterLayer], NSException, @"MGLRedundantLayerException"); + XCTAssertThrowsSpecificNamed([self.style addLayer:rasterLayer], NSException, MGLRedundantLayerException); MGLSymbolStyleLayer *symbolLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"symbolLayer" source:source]; [self.style addLayer:symbolLayer]; - XCTAssertThrowsSpecificNamed([self.style addLayer:symbolLayer], NSException, @"MGLRedundantLayerException"); + XCTAssertThrowsSpecificNamed([self.style addLayer:symbolLayer], NSException, MGLRedundantLayerException); } - (void)testAddingLayersWithDuplicateIdentifiers { diff --git a/platform/default/asset_file_source.cpp b/platform/default/asset_file_source.cpp index 3063bf88a0..7988654ae5 100644 --- a/platform/default/asset_file_source.cpp +++ b/platform/default/asset_file_source.cpp @@ -75,7 +75,7 @@ std::unique_ptr<AsyncRequest> AssetFileSource::request(const Resource& resource, } bool AssetFileSource::acceptsURL(const std::string& url) { - return std::equal(assetProtocol.begin(), assetProtocol.end(), url.begin()); + return 0 == url.rfind(assetProtocol, 0); } } // namespace mbgl diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp index f070121497..93f10eea72 100644 --- a/platform/default/default_file_source.cpp +++ b/platform/default/default_file_source.cpp @@ -45,58 +45,45 @@ public: onlineFileSource.setResourceTransform(std::move(transform)); } - void listRegions(std::function<void (std::exception_ptr, optional<std::vector<OfflineRegion>>)> callback) { - try { - callback({}, offlineDatabase->listRegions()); - } catch (...) { - callback(std::current_exception(), {}); - } + void listRegions(std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) { + callback(offlineDatabase->listRegions()); } void createRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata, - std::function<void (std::exception_ptr, optional<OfflineRegion>)> callback) { - try { - callback({}, offlineDatabase->createRegion(definition, metadata)); - } catch (...) { - callback(std::current_exception(), {}); - } + std::function<void (expected<OfflineRegion, std::exception_ptr>)> callback) { + callback(offlineDatabase->createRegion(definition, metadata)); } void updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata, - std::function<void (std::exception_ptr, optional<OfflineRegionMetadata>)> callback) { - try { - callback({}, offlineDatabase->updateMetadata(regionID, metadata)); - } catch (...) { - callback(std::current_exception(), {}); - } + std::function<void (expected<OfflineRegionMetadata, std::exception_ptr>)> callback) { + callback(offlineDatabase->updateMetadata(regionID, metadata)); } - void getRegionStatus(int64_t regionID, std::function<void (std::exception_ptr, optional<OfflineRegionStatus>)> callback) { - try { - callback({}, getDownload(regionID).getStatus()); - } catch (...) { - callback(std::current_exception(), {}); + void getRegionStatus(int64_t regionID, std::function<void (expected<OfflineRegionStatus, std::exception_ptr>)> callback) { + if (auto download = getDownload(regionID)) { + callback(download.value()->getStatus()); + } else { + callback(unexpected<std::exception_ptr>(download.error())); } } void deleteRegion(OfflineRegion&& region, std::function<void (std::exception_ptr)> callback) { - try { - downloads.erase(region.getID()); - offlineDatabase->deleteRegion(std::move(region)); - callback({}); - } catch (...) { - callback(std::current_exception()); - } + downloads.erase(region.getID()); + callback(offlineDatabase->deleteRegion(std::move(region))); } void setRegionObserver(int64_t regionID, std::unique_ptr<OfflineRegionObserver> observer) { - getDownload(regionID).setObserver(std::move(observer)); + if (auto download = getDownload(regionID)) { + download.value()->setObserver(std::move(observer)); + } } void setRegionDownloadState(int64_t regionID, OfflineRegionDownloadState state) { - getDownload(regionID).setState(state); + if (auto download = getDownload(regionID)) { + download.value()->setState(state); + } } void request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) { @@ -181,13 +168,18 @@ public: } private: - OfflineDownload& getDownload(int64_t regionID) { + expected<OfflineDownload*, std::exception_ptr> getDownload(int64_t regionID) { auto it = downloads.find(regionID); if (it != downloads.end()) { - return *it->second; + return it->second.get(); + } + auto definition = offlineDatabase->getRegionDefinition(regionID); + if (!definition) { + return unexpected<std::exception_ptr>(definition.error()); } - return *downloads.emplace(regionID, - std::make_unique<OfflineDownload>(regionID, offlineDatabase->getRegionDefinition(regionID), *offlineDatabase, onlineFileSource)).first->second; + auto download = std::make_unique<OfflineDownload>(regionID, std::move(definition.value()), + *offlineDatabase, onlineFileSource); + return downloads.emplace(regionID, std::move(download)).first->second.get(); } // shared so that destruction is done on the creating thread @@ -256,19 +248,19 @@ std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resourc return std::move(req); } -void DefaultFileSource::listOfflineRegions(std::function<void (std::exception_ptr, optional<std::vector<OfflineRegion>>)> callback) { +void DefaultFileSource::listOfflineRegions(std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) { impl->actor().invoke(&Impl::listRegions, callback); } void DefaultFileSource::createOfflineRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata, - std::function<void (std::exception_ptr, optional<OfflineRegion>)> callback) { + std::function<void (expected<OfflineRegion, std::exception_ptr>)> callback) { impl->actor().invoke(&Impl::createRegion, definition, metadata, callback); } void DefaultFileSource::updateOfflineMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata, - std::function<void (std::exception_ptr, optional<OfflineRegionMetadata>)> callback) { + std::function<void (expected<OfflineRegionMetadata, std::exception_ptr>)> callback) { impl->actor().invoke(&Impl::updateMetadata, regionID, metadata, callback); } @@ -284,7 +276,7 @@ void DefaultFileSource::setOfflineRegionDownloadState(OfflineRegion& region, Off impl->actor().invoke(&Impl::setRegionDownloadState, region.getID(), state); } -void DefaultFileSource::getOfflineRegionStatus(OfflineRegion& region, std::function<void (std::exception_ptr, optional<OfflineRegionStatus>)> callback) const { +void DefaultFileSource::getOfflineRegionStatus(OfflineRegion& region, std::function<void (expected<OfflineRegionStatus, std::exception_ptr>)> callback) const { impl->actor().invoke(&Impl::getRegionStatus, region.getID(), callback); } diff --git a/platform/default/local_file_source.cpp b/platform/default/local_file_source.cpp index 0635e86d80..1b7b7b9278 100644 --- a/platform/default/local_file_source.cpp +++ b/platform/default/local_file_source.cpp @@ -75,7 +75,7 @@ std::unique_ptr<AsyncRequest> LocalFileSource::request(const Resource& resource, } bool LocalFileSource::acceptsURL(const std::string& url) { - return std::equal(fileProtocol.begin(), fileProtocol.end(), url.begin()); + return 0 == url.rfind(fileProtocol, 0); } } // namespace mbgl diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp index 598a0b182b..e1ec0acb31 100644 --- a/platform/default/mbgl/storage/offline.cpp +++ b/platform/default/mbgl/storage/offline.cpp @@ -1,8 +1,10 @@ #include <mbgl/storage/offline.hpp> -#include <mbgl/util/tile_cover.hpp> #include <mbgl/util/tileset.hpp> #include <mbgl/util/projection.hpp> +#include <mapbox/geojson.hpp> +#include <mapbox/geojson/rapidjson.hpp> + #include <rapidjson/document.h> #include <rapidjson/stringbuffer.h> #include <rapidjson/writer.h> @@ -11,6 +13,8 @@ namespace mbgl { +// OfflineTilePyramidRegionDefinition + OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition( std::string styleURL_, LatLngBounds bounds_, double minZoom_, double maxZoom_, float pixelRatio_) : styleURL(std::move(styleURL_)), @@ -24,87 +28,100 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition( } } -std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { - const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); - std::vector<CanonicalTileID> result; - - for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { - for (const auto& tile : util::tileCover(bounds, z)) { - result.emplace_back(tile.canonical); - } - } - - return result; -} +// OfflineGeometryRegionDefinition -uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { - - const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); - unsigned long result = 0;; - for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { - result += util::tileCount(bounds, z); +OfflineGeometryRegionDefinition::OfflineGeometryRegionDefinition(std::string styleURL_, Geometry<double> geometry_, double minZoom_, double maxZoom_, float pixelRatio_) + : styleURL(styleURL_) + , geometry(std::move(geometry_)) + , minZoom(minZoom_) + , maxZoom(maxZoom_) + , pixelRatio(pixelRatio_) { + if (minZoom < 0 || maxZoom < 0 || maxZoom < minZoom || pixelRatio < 0 || + !std::isfinite(minZoom) || std::isnan(maxZoom) || !std::isfinite(pixelRatio)) { + throw std::invalid_argument("Invalid offline region definition"); } - - return result; -} - -Range<uint8_t> OfflineTilePyramidRegionDefinition::coveringZoomRange(style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) const { - double minZ = std::max<double>(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min); - double maxZ = std::min<double>(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max); - - assert(minZ >= 0); - assert(maxZ >= 0); - assert(minZ < std::numeric_limits<uint8_t>::max()); - assert(maxZ < std::numeric_limits<uint8_t>::max()); - return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) }; } OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) { rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc; doc.Parse<0>(region.c_str()); - if (doc.HasParseError() || - !doc.HasMember("style_url") || !doc["style_url"].IsString() || - !doc.HasMember("bounds") || !doc["bounds"].IsArray() || doc["bounds"].Size() != 4 || - !doc["bounds"][0].IsDouble() || !doc["bounds"][1].IsDouble() || - !doc["bounds"][2].IsDouble() || !doc["bounds"][3].IsDouble() || - !doc.HasMember("min_zoom") || !doc["min_zoom"].IsDouble() || - (doc.HasMember("max_zoom") && !doc["max_zoom"].IsDouble()) || - !doc.HasMember("pixel_ratio") || !doc["pixel_ratio"].IsDouble()) { + // validation + + auto hasValidBounds = [&] { + return doc.HasMember("bounds") && doc["bounds"].IsArray() && doc["bounds"].Size() == 4 + && doc["bounds"][0].IsDouble() && doc["bounds"][1].IsDouble() + && doc["bounds"][2].IsDouble() && doc["bounds"][3].IsDouble(); + }; + + auto hasValidGeometry = [&] { + return doc.HasMember("geometry") && doc["geometry"].IsObject(); + }; + + if (doc.HasParseError() + || !doc.HasMember("style_url") || !doc["style_url"].IsString() + || !(hasValidBounds() || hasValidGeometry()) + || !doc.HasMember("min_zoom") || !doc["min_zoom"].IsDouble() + || (doc.HasMember("max_zoom") && !doc["max_zoom"].IsDouble()) + || !doc.HasMember("pixel_ratio") || !doc["pixel_ratio"].IsDouble()) { throw std::runtime_error("Malformed offline region definition"); } + // Common properties + std::string styleURL { doc["style_url"].GetString(), doc["style_url"].GetStringLength() }; - LatLngBounds bounds = LatLngBounds::hull( - LatLng(doc["bounds"][0].GetDouble(), doc["bounds"][1].GetDouble()), - LatLng(doc["bounds"][2].GetDouble(), doc["bounds"][3].GetDouble())); double minZoom = doc["min_zoom"].GetDouble(); double maxZoom = doc.HasMember("max_zoom") ? doc["max_zoom"].GetDouble() : INFINITY; float pixelRatio = doc["pixel_ratio"].GetDouble(); + + if (doc.HasMember("bounds")) { + return OfflineTilePyramidRegionDefinition{ + styleURL, + LatLngBounds::hull( + LatLng(doc["bounds"][0].GetDouble(), doc["bounds"][1].GetDouble()), + LatLng(doc["bounds"][2].GetDouble(), doc["bounds"][3].GetDouble())), + minZoom, maxZoom, pixelRatio }; + } else { + return OfflineGeometryRegionDefinition{ + styleURL, + mapbox::geojson::convert<Geometry<double>>(doc["geometry"].GetObject()), + minZoom, maxZoom, pixelRatio }; + }; - return { styleURL, bounds, minZoom, maxZoom, pixelRatio }; } std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region) { rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc; doc.SetObject(); - doc.AddMember("style_url", rapidjson::StringRef(region.styleURL.data(), region.styleURL.length()), doc.GetAllocator()); + // Encode common properties + region.match([&](auto& _region) { + doc.AddMember("style_url", rapidjson::StringRef(_region.styleURL.data(), _region.styleURL.length()), doc.GetAllocator()); + doc.AddMember("min_zoom", _region.minZoom, doc.GetAllocator()); + if (std::isfinite(_region.maxZoom)) { + doc.AddMember("max_zoom", _region.maxZoom, doc.GetAllocator()); + } - rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(rapidjson::kArrayType); - bounds.PushBack(region.bounds.south(), doc.GetAllocator()); - bounds.PushBack(region.bounds.west(), doc.GetAllocator()); - bounds.PushBack(region.bounds.north(), doc.GetAllocator()); - bounds.PushBack(region.bounds.east(), doc.GetAllocator()); - doc.AddMember("bounds", bounds, doc.GetAllocator()); + doc.AddMember("pixel_ratio", _region.pixelRatio, doc.GetAllocator()); + }); - doc.AddMember("min_zoom", region.minZoom, doc.GetAllocator()); - if (std::isfinite(region.maxZoom)) { - doc.AddMember("max_zoom", region.maxZoom, doc.GetAllocator()); - } + // Encode specific properties + region.match( + [&] (const OfflineTilePyramidRegionDefinition& _region) { + rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator> bounds(rapidjson::kArrayType); + bounds.PushBack(_region.bounds.south(), doc.GetAllocator()); + bounds.PushBack(_region.bounds.west(), doc.GetAllocator()); + bounds.PushBack(_region.bounds.north(), doc.GetAllocator()); + bounds.PushBack(_region.bounds.east(), doc.GetAllocator()); + doc.AddMember("bounds", bounds, doc.GetAllocator()); + + }, + [&] (const OfflineGeometryRegionDefinition& _region) { + doc.AddMember("geometry", mapbox::geojson::convert(_region.geometry, doc.GetAllocator()), doc.GetAllocator()); - doc.AddMember("pixel_ratio", region.pixelRatio, doc.GetAllocator()); + } + ); rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); @@ -113,6 +130,9 @@ std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition& region) return buffer.GetString(); } + +// OfflineRegion + OfflineRegion::OfflineRegion(int64_t id_, OfflineRegionDefinition definition_, OfflineRegionMetadata metadata_) @@ -135,5 +155,4 @@ const OfflineRegionMetadata& OfflineRegion::getMetadata() const { int64_t OfflineRegion::getID() const { return id; } - } // namespace mbgl diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index 8f7f0965f4..79bc3c8f27 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -15,7 +15,14 @@ namespace mbgl { OfflineDatabase::OfflineDatabase(std::string path_, uint64_t maximumCacheSize_) : path(std::move(path_)), maximumCacheSize(maximumCacheSize_) { - ensureSchema(); + try { + initialize(); + } catch (const util::IOException& ex) { + handleError(ex, "open database"); + } catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "open database"); + } + // Assume that we can't open the database right now and work with an empty database object. } OfflineDatabase::~OfflineDatabase() { @@ -24,87 +31,74 @@ OfflineDatabase::~OfflineDatabase() { try { statements.clear(); db.reset(); - } catch (mapbox::sqlite::Exception& ex) { - Log::Error(Event::Database, (int)ex.code, ex.what()); + } catch (const util::IOException& ex) { + handleError(ex, "close database"); + } catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "close database"); } } -void OfflineDatabase::ensureSchema() { - auto result = mapbox::sqlite::Database::tryOpen(path, mapbox::sqlite::ReadWriteCreate); - if (result.is<mapbox::sqlite::Exception>()) { - const auto& ex = result.get<mapbox::sqlite::Exception>(); - if (ex.code == mapbox::sqlite::ResultCode::NotADB) { - // Corrupted; blow it away. - removeExisting(); - result = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadWriteCreate); - } else { - Log::Error(Event::Database, "Unexpected error connecting to database: %s", ex.what()); - throw ex; - } +void OfflineDatabase::initialize() { + assert(!db); + assert(statements.empty()); + + db = std::make_unique<mapbox::sqlite::Database>( + mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadWriteCreate)); + db->setBusyTimeout(Milliseconds::max()); + db->exec("PRAGMA foreign_keys = ON"); + + const auto userVersion = getPragma<int64_t>("PRAGMA user_version"); + switch (userVersion) { + case 0: + case 1: + // Newly created database, or old cache-only database; remove old table if it exists. + removeOldCacheTable(); + createSchema(); + return; + case 2: + migrateToVersion3(); + // fall through + case 3: + // Removed migration, see below. + // fall through + case 4: + migrateToVersion5(); + // fall through + case 5: + migrateToVersion6(); + // fall through + case 6: + // Happy path; we're done + return; + default: + // Downgrade: delete the database and try to reinitialize. + removeExisting(); + initialize(); } +} - try { - assert(result.is<mapbox::sqlite::Database>()); - db = std::make_unique<mapbox::sqlite::Database>(std::move(result.get<mapbox::sqlite::Database>())); - db->setBusyTimeout(Milliseconds::max()); - db->exec("PRAGMA foreign_keys = ON"); - - switch (userVersion()) { - case 0: - case 1: - // Newly created database, or old cache-only database; remove old table if it exists. - removeOldCacheTable(); - break; - case 2: - migrateToVersion3(); - // fall through - case 3: - case 4: - migrateToVersion5(); - // fall through - case 5: - migrateToVersion6(); - // fall through - case 6: - // happy path; we're done - return; - default: - // downgrade, delete the database - removeExisting(); - break; - } - } catch (const mapbox::sqlite::Exception& ex) { - // Unfortunately, SQLITE_NOTADB is not always reported upon opening the database. - // Apparently sometimes it is delayed until the first read operation. - if (ex.code == mapbox::sqlite::ResultCode::NotADB) { +void OfflineDatabase::handleError(const mapbox::sqlite::Exception& ex, const char* action) { + if (ex.code == mapbox::sqlite::ResultCode::NotADB || + ex.code == mapbox::sqlite::ResultCode::Corrupt || + (ex.code == mapbox::sqlite::ResultCode::ReadOnly && + ex.extendedCode == mapbox::sqlite::ExtendedResultCode::ReadOnlyDBMoved)) { + // The database was corruped, moved away, or deleted. We're going to start fresh with a + // clean slate for the next operation. + Log::Error(Event::Database, static_cast<int>(ex.code), "Can't %s: %s", action, ex.what()); + try { removeExisting(); - } else { - throw; - } - } - - try { - // When downgrading the database, or when the database is corrupt, we've deleted the old database handle, - // so we need to reopen it. - if (!db) { - db = std::make_unique<mapbox::sqlite::Database>(mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadWriteCreate)); - db->setBusyTimeout(Milliseconds::max()); - db->exec("PRAGMA foreign_keys = ON"); + } catch (const util::IOException& ioEx) { + handleError(ioEx, action); } - - db->exec("PRAGMA auto_vacuum = INCREMENTAL"); - db->exec("PRAGMA journal_mode = DELETE"); - db->exec("PRAGMA synchronous = FULL"); - db->exec(offlineDatabaseSchema); - db->exec("PRAGMA user_version = 6"); - } catch (...) { - Log::Error(Event::Database, "Unexpected error creating database schema: %s", util::toString(std::current_exception()).c_str()); - throw; + } else { + // We treat the error as temporary, and pretend we have an inaccessible DB. + Log::Warning(Event::Database, static_cast<int>(ex.code), "Can't %s: %s", action, ex.what()); } } -int OfflineDatabase::userVersion() { - return static_cast<int>(getPragma<int64_t>("PRAGMA user_version")); +void OfflineDatabase::handleError(const util::IOException& ex, const char* action) { + // We failed to delete the database file. + Log::Error(Event::Database, ex.code, "Can't %s: %s", action, ex.what()); } void OfflineDatabase::removeExisting() { @@ -113,19 +107,28 @@ void OfflineDatabase::removeExisting() { statements.clear(); db.reset(); - try { - util::deleteFile(path); - } catch (util::IOException& ex) { - Log::Error(Event::Database, ex.code, ex.what()); - } + util::deleteFile(path); } void OfflineDatabase::removeOldCacheTable() { + assert(db); db->exec("DROP TABLE IF EXISTS http_cache"); db->exec("VACUUM"); } +void OfflineDatabase::createSchema() { + assert(db); + db->exec("PRAGMA auto_vacuum = INCREMENTAL"); + db->exec("PRAGMA journal_mode = DELETE"); + db->exec("PRAGMA synchronous = FULL"); + mapbox::sqlite::Transaction transaction(*db); + db->exec(offlineDatabaseSchema); + db->exec("PRAGMA user_version = 6"); + transaction.commit(); +} + void OfflineDatabase::migrateToVersion3() { + assert(db); db->exec("PRAGMA auto_vacuum = INCREMENTAL"); db->exec("VACUUM"); db->exec("PRAGMA user_version = 3"); @@ -138,12 +141,14 @@ void OfflineDatabase::migrateToVersion3() { // See: https://github.com/mapbox/mapbox-gl-native/pull/6320 void OfflineDatabase::migrateToVersion5() { + assert(db); db->exec("PRAGMA journal_mode = DELETE"); db->exec("PRAGMA synchronous = FULL"); db->exec("PRAGMA user_version = 5"); } void OfflineDatabase::migrateToVersion6() { + assert(db); mapbox::sqlite::Transaction transaction(*db); db->exec("ALTER TABLE resources ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0"); db->exec("ALTER TABLE tiles ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0"); @@ -152,6 +157,9 @@ void OfflineDatabase::migrateToVersion6() { } mapbox::sqlite::Statement& OfflineDatabase::getStatement(const char* sql) { + if (!db) { + initialize(); + } auto it = statements.find(sql); if (it == statements.end()) { it = statements.emplace(sql, std::make_unique<mapbox::sqlite::Statement>(*db, sql)).first; @@ -159,9 +167,15 @@ mapbox::sqlite::Statement& OfflineDatabase::getStatement(const char* sql) { return *it->second; } -optional<Response> OfflineDatabase::get(const Resource& resource) { +optional<Response> OfflineDatabase::get(const Resource& resource) try { auto result = getInternal(resource); - return result ? result->first : optional<Response>(); + return result ? optional<Response>{ result->first } : nullopt; +} catch (const util::IOException& ex) { + handleError(ex, "read resource"); + return nullopt; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "read resource"); + return nullopt; } optional<std::pair<Response, uint64_t>> OfflineDatabase::getInternal(const Resource& resource) { @@ -182,11 +196,17 @@ optional<int64_t> OfflineDatabase::hasInternal(const Resource& resource) { } } -std::pair<bool, uint64_t> OfflineDatabase::put(const Resource& resource, const Response& response) { +std::pair<bool, uint64_t> OfflineDatabase::put(const Resource& resource, const Response& response) try { + if (!db) { + initialize(); + } mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate); auto result = putInternal(resource, response, true); transaction.commit(); return result; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "write resource"); + return { false, 0 }; } std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource, const Response& response, bool evict_) { @@ -227,11 +247,19 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource, optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resource& resource) { // Update accessed timestamp used for LRU eviction. - { + try { mapbox::sqlite::Query accessedQuery{ getStatement("UPDATE resources SET accessed = ?1 WHERE url = ?2") }; accessedQuery.bind(1, util::now()); accessedQuery.bind(2, resource.url); accessedQuery.run(); + } catch (const mapbox::sqlite::Exception& ex) { + if (ex.code == mapbox::sqlite::ResultCode::NotADB || + ex.code == mapbox::sqlite::ResultCode::Corrupt) { + throw; + } + + // If we don't have any indication that the database is corrupt, continue as usual. + Log::Warning(Event::Database, static_cast<int>(ex.code), "Can't update timestamp: %s", ex.what()); } // clang-format off @@ -245,7 +273,7 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou query.bind(1, resource.url); if (!query.run()) { - return {}; + return nullopt; } Response response; @@ -274,7 +302,7 @@ optional<int64_t> OfflineDatabase::hasResource(const Resource& resource) { mapbox::sqlite::Query query{ getStatement("SELECT length(data) FROM resources WHERE url = ?") }; query.bind(1, resource.url); if (!query.run()) { - return {}; + return nullopt; } return query.get<optional<int64_t>>(0); @@ -366,7 +394,8 @@ bool OfflineDatabase::putResource(const Resource& resource, } optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource::TileData& tile) { - { + // Update accessed timestamp used for LRU eviction. + try { // clang-format off mapbox::sqlite::Query accessedQuery{ getStatement( "UPDATE tiles " @@ -385,6 +414,13 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource: accessedQuery.bind(5, tile.y); accessedQuery.bind(6, tile.z); accessedQuery.run(); + } catch (const mapbox::sqlite::Exception& ex) { + if (ex.code == mapbox::sqlite::ResultCode::NotADB || ex.code == mapbox::sqlite::ResultCode::Corrupt) { + throw; + } + + // If we don't have any indication that the database is corrupt, continue as usual. + Log::Warning(Event::Database, static_cast<int>(ex.code), "Can't update timestamp: %s", ex.what()); } // clang-format off @@ -406,7 +442,7 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource: query.bind(5, tile.z); if (!query.run()) { - return {}; + return nullopt; } Response response; @@ -450,7 +486,7 @@ optional<int64_t> OfflineDatabase::hasTile(const Resource::TileData& tile) { size.bind(5, tile.z); if (!size.run()) { - return {}; + return nullopt; } return size.get<optional<int64_t>>(0); @@ -559,23 +595,33 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, return true; } -std::vector<OfflineRegion> OfflineDatabase::listRegions() { +expected<OfflineRegions, std::exception_ptr> OfflineDatabase::listRegions() try { mapbox::sqlite::Query query{ getStatement("SELECT id, definition, description FROM regions") }; - - std::vector<OfflineRegion> result; - + OfflineRegions result; while (query.run()) { - result.push_back(OfflineRegion( - query.get<int64_t>(0), - decodeOfflineRegionDefinition(query.get<std::string>(1)), - query.get<std::vector<uint8_t>>(2))); + const auto id = query.get<int64_t>(0); + const auto definition = query.get<std::string>(1); + const auto description = query.get<std::vector<uint8_t>>(2); + try { + // Construct, then move because this constructor is private. + OfflineRegion region(id, decodeOfflineRegionDefinition(definition), description); + result.emplace_back(std::move(region)); + } catch (const std::exception& ex) { + // Catch errors from malformed offline region definitions + // and skip them. + Log::Error(Event::General, "%s", ex.what()); + } } - - return result; + // Explicit move to avoid triggering the copy constructor. + return { std::move(result) }; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "list regions"); + return unexpected<std::exception_ptr>(std::current_exception()); } -OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& definition, - const OfflineRegionMetadata& metadata) { +expected<OfflineRegion, std::exception_ptr> +OfflineDatabase::createRegion(const OfflineRegionDefinition& definition, + const OfflineRegionMetadata& metadata) try { // clang-format off mapbox::sqlite::Query query{ getStatement( "INSERT INTO regions (definition, description) " @@ -585,11 +631,14 @@ OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& defin query.bind(1, encodeOfflineRegionDefinition(definition)); query.bindBlob(2, metadata); query.run(); - return OfflineRegion(query.lastInsertRowId(), definition, metadata); +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "create region"); + return unexpected<std::exception_ptr>(std::current_exception()); } -OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) { +expected<OfflineRegionMetadata, std::exception_ptr> +OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) try { // clang-format off mapbox::sqlite::Query query{ getStatement( "UPDATE regions SET description = ?1 " @@ -600,9 +649,12 @@ OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, co query.run(); return metadata; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "update region metadata"); + return unexpected<std::exception_ptr>(std::current_exception()); } -void OfflineDatabase::deleteRegion(OfflineRegion&& region) { +std::exception_ptr OfflineDatabase::deleteRegion(OfflineRegion&& region) try { { mapbox::sqlite::Query query{ getStatement("DELETE FROM regions WHERE id = ?") }; query.bind(1, region.getID()); @@ -610,13 +662,18 @@ void OfflineDatabase::deleteRegion(OfflineRegion&& region) { } evict(0); + assert(db); db->exec("PRAGMA incremental_vacuum"); // Ensure that the cached offlineTileCount value is recalculated. offlineMapboxTileCount = {}; + return nullptr; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "delete region"); + return std::current_exception(); } -optional<std::pair<Response, uint64_t>> OfflineDatabase::getRegionResource(int64_t regionID, const Resource& resource) { +optional<std::pair<Response, uint64_t>> OfflineDatabase::getRegionResource(int64_t regionID, const Resource& resource) try { auto response = getInternal(resource); if (response) { @@ -624,9 +681,12 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getRegionResource(int64 } return response; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "read region resource"); + return nullopt; } -optional<int64_t> OfflineDatabase::hasRegionResource(int64_t regionID, const Resource& resource) { +optional<int64_t> OfflineDatabase::hasRegionResource(int64_t regionID, const Resource& resource) try { auto response = hasInternal(resource); if (response) { @@ -634,29 +694,52 @@ optional<int64_t> OfflineDatabase::hasRegionResource(int64_t regionID, const Res } return response; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "query region resource"); + return nullopt; } -uint64_t OfflineDatabase::putRegionResource(int64_t regionID, const Resource& resource, const Response& response) { +uint64_t OfflineDatabase::putRegionResource(int64_t regionID, + const Resource& resource, + const Response& response) try { + if (!db) { + initialize(); + } mapbox::sqlite::Transaction transaction(*db); auto size = putRegionResourceInternal(regionID, resource, response); transaction.commit(); return size; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "write region resource"); + return 0; } -void OfflineDatabase::putRegionResources(int64_t regionID, const std::list<std::tuple<Resource, Response>>& resources, OfflineRegionStatus& status) { +void OfflineDatabase::putRegionResources(int64_t regionID, + const std::list<std::tuple<Resource, Response>>& resources, + OfflineRegionStatus& status) try { + if (!db) { + initialize(); + } mapbox::sqlite::Transaction transaction(*db); + // Accumulate all statistics locally first before adding them to the OfflineRegionStatus object + // to ensure correctness when the transaction fails. + uint64_t completedResourceCount = 0; + uint64_t completedResourceSize = 0; + uint64_t completedTileCount = 0; + uint64_t completedTileSize = 0; + for (const auto& elem : resources) { const auto& resource = std::get<0>(elem); const auto& response = std::get<1>(elem); try { uint64_t resourceSize = putRegionResourceInternal(regionID, resource, response); - status.completedResourceCount++; - status.completedResourceSize += resourceSize; + completedResourceCount++; + completedResourceSize += resourceSize; if (resource.kind == Resource::Kind::Tile) { - status.completedTileCount += 1; - status.completedTileSize += resourceSize; + completedTileCount += 1; + completedTileSize += resourceSize; } } catch (const MapboxTileLimitExceededException&) { // Commit the rest of the batch and retrow @@ -667,6 +750,13 @@ void OfflineDatabase::putRegionResources(int64_t regionID, const std::list<std:: // Commit the completed batch transaction.commit(); + + status.completedResourceCount += completedResourceCount; + status.completedResourceSize += completedResourceSize; + status.completedTileCount += completedTileCount; + status.completedTileSize += completedTileSize; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "write region resources"); } uint64_t OfflineDatabase::putRegionResourceInternal(int64_t regionID, const Resource& resource, const Response& response) { @@ -755,7 +845,7 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { mapbox::sqlite::Query selectQuery{ getStatement( "SELECT region_id " "FROM region_resources, resources " - "WHERE region_id != ?1 " + "WHERE region_id != ?1 " " AND resources.url = ?2 " "LIMIT 1 ") }; // clang-format on @@ -766,15 +856,18 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { } } -OfflineRegionDefinition OfflineDatabase::getRegionDefinition(int64_t regionID) { +expected<OfflineRegionDefinition, std::exception_ptr> OfflineDatabase::getRegionDefinition(int64_t regionID) try { mapbox::sqlite::Query query{ getStatement("SELECT definition FROM regions WHERE id = ?1") }; query.bind(1, regionID); query.run(); return decodeOfflineRegionDefinition(query.get<std::string>(0)); +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "load region"); + return unexpected<std::exception_ptr>(std::current_exception()); } -OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) { +expected<OfflineRegionStatus, std::exception_ptr> OfflineDatabase::getRegionCompletedStatus(int64_t regionID) try { OfflineRegionStatus result; std::tie(result.completedResourceCount, result.completedResourceSize) @@ -786,6 +879,9 @@ OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) result.completedResourceSize += result.completedTileSize; return result; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "get region status"); + return unexpected<std::exception_ptr>(std::current_exception()); } std::pair<int64_t, int64_t> OfflineDatabase::getCompletedResourceCountAndSize(int64_t regionID) { @@ -920,7 +1016,7 @@ bool OfflineDatabase::offlineMapboxTileCountLimitExceeded() { return getOfflineMapboxTileCount() >= offlineMapboxTileCountLimit; } -uint64_t OfflineDatabase::getOfflineMapboxTileCount() { +uint64_t OfflineDatabase::getOfflineMapboxTileCount() try { // Calculating this on every call would be much simpler than caching and // manually updating the value, but it would make offline downloads an O(n²) // operation, because the database query below involves an index scan of @@ -942,6 +1038,9 @@ uint64_t OfflineDatabase::getOfflineMapboxTileCount() { offlineMapboxTileCount = query.get<int64_t>(0); return *offlineMapboxTileCount; +} catch (const mapbox::sqlite::Exception& ex) { + handleError(ex, "get offline Mapbox tile count"); + return std::numeric_limits<uint64_t>::max(); } bool OfflineDatabase::exceedsOfflineMapboxTileCountLimit(const Resource& resource) { diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp index 38eb3783ba..fbe6c707e1 100644 --- a/platform/default/mbgl/storage/offline_database.hpp +++ b/platform/default/mbgl/storage/offline_database.hpp @@ -7,6 +7,7 @@ #include <mbgl/util/optional.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/mapbox.hpp> +#include <mbgl/util/expected.hpp> #include <unordered_map> #include <memory> @@ -18,6 +19,7 @@ namespace sqlite { class Database; class Statement; class Query; +class Exception; } // namespace sqlite } // namespace mapbox @@ -26,6 +28,10 @@ namespace mbgl { class Response; class TileID; +namespace util { +struct IOException; +} // namespace util + struct MapboxTileLimitExceededException : util::Exception { MapboxTileLimitExceededException() : util::Exception("Mapbox tile limit exceeded") {} }; @@ -42,14 +48,15 @@ public: // Return value is (inserted, stored size) std::pair<bool, uint64_t> put(const Resource&, const Response&); - std::vector<OfflineRegion> listRegions(); + expected<OfflineRegions, std::exception_ptr> listRegions(); - OfflineRegion createRegion(const OfflineRegionDefinition&, - const OfflineRegionMetadata&); + expected<OfflineRegion, std::exception_ptr> createRegion(const OfflineRegionDefinition&, + const OfflineRegionMetadata&); - OfflineRegionMetadata updateMetadata(const int64_t regionID, const OfflineRegionMetadata&); + expected<OfflineRegionMetadata, std::exception_ptr> + updateMetadata(const int64_t regionID, const OfflineRegionMetadata&); - void deleteRegion(OfflineRegion&&); + std::exception_ptr deleteRegion(OfflineRegion&&); // Return value is (response, stored size) optional<std::pair<Response, uint64_t>> getRegionResource(int64_t regionID, const Resource&); @@ -57,8 +64,8 @@ public: uint64_t putRegionResource(int64_t regionID, const Resource&, const Response&); void putRegionResources(int64_t regionID, const std::list<std::tuple<Resource, Response>>&, OfflineRegionStatus&); - OfflineRegionDefinition getRegionDefinition(int64_t regionID); - OfflineRegionStatus getRegionCompletedStatus(int64_t regionID); + expected<OfflineRegionDefinition, std::exception_ptr> getRegionDefinition(int64_t regionID); + expected<OfflineRegionStatus, std::exception_ptr> getRegionCompletedStatus(int64_t regionID); void setOfflineMapboxTileCountLimit(uint64_t); uint64_t getOfflineMapboxTileCountLimit(); @@ -67,12 +74,15 @@ public: bool exceedsOfflineMapboxTileCountLimit(const Resource&); private: - int userVersion(); - void ensureSchema(); + void initialize(); + void handleError(const mapbox::sqlite::Exception&, const char* action); + void handleError(const util::IOException&, const char* action); + void removeExisting(); void removeOldCacheTable(); - void migrateToVersion3(); + void createSchema(); void migrateToVersion5(); + void migrateToVersion3(); void migrateToVersion6(); mapbox::sqlite::Statement& getStatement(const char *); diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index 4da51db655..118f3aad88 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -24,6 +24,63 @@ namespace mbgl { using namespace style; +// Generic functions + +template <class RegionDefinition> +Range<uint8_t> coveringZoomRange(const RegionDefinition& definition, + style::SourceType type, uint16_t tileSize, const Range<uint8_t>& zoomRange) { + double minZ = std::max<double>(util::coveringZoomLevel(definition.minZoom, type, tileSize), zoomRange.min); + double maxZ = std::min<double>(util::coveringZoomLevel(definition.maxZoom, type, tileSize), zoomRange.max); + + assert(minZ >= 0); + assert(maxZ >= 0); + assert(minZ < std::numeric_limits<uint8_t>::max()); + assert(maxZ < std::numeric_limits<uint8_t>::max()); + return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) }; +} + +template <class Geometry, class Fn> +void tileCover(const Geometry& geometry, uint8_t z, Fn&& fn) { + util::TileCover cover(geometry, z); + while (cover.hasNext()) { + fn(cover.next()->canonical); + } +} + + +template <class Fn> +void tileCover(const OfflineRegionDefinition& definition, style::SourceType type, + uint16_t tileSize, const Range<uint8_t>& zoomRange, Fn&& fn) { + const Range<uint8_t> clampedZoomRange = + definition.match([&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }); + + for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { + definition.match( + [&](const OfflineTilePyramidRegionDefinition& reg){ tileCover(reg.bounds, z, fn); }, + [&](const OfflineGeometryRegionDefinition& reg){ tileCover(reg.geometry, z, fn); } + ); + } +} + +uint64_t tileCount(const OfflineRegionDefinition& definition, style::SourceType type, + uint16_t tileSize, const Range<uint8_t>& zoomRange) { + + const Range<uint8_t> clampedZoomRange = + definition.match([&](auto& reg) { return coveringZoomRange(reg, type, tileSize, zoomRange); }); + + unsigned long result = 0;; + for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { + result += definition.match( + [&](const OfflineTilePyramidRegionDefinition& reg){ return util::tileCount(reg.bounds, z); }, + [&](const OfflineGeometryRegionDefinition& reg){ return util::tileCount(reg.geometry, z); } + ); + } + + return result; +} + +// OfflineDownload + OfflineDownload::OfflineDownload(int64_t id_, OfflineRegionDefinition&& definition_, OfflineDatabase& offlineDatabase_, @@ -62,39 +119,45 @@ OfflineRegionStatus OfflineDownload::getStatus() const { return status; } - OfflineRegionStatus result = offlineDatabase.getRegionCompletedStatus(id); + auto result = offlineDatabase.getRegionCompletedStatus(id); + if (!result) { + // We can't find this offline region because the database is unavailable, or the download + // does not exist. + return {}; + } - result.requiredResourceCount++; - optional<Response> styleResponse = offlineDatabase.get(Resource::style(definition.styleURL)); + result->requiredResourceCount++; + optional<Response> styleResponse = + offlineDatabase.get(Resource::style(definition.match([](auto& reg){ return reg.styleURL; }))); if (!styleResponse) { - return result; + return *result; } style::Parser parser; parser.parse(*styleResponse->data); - result.requiredResourceCountIsPrecise = true; + result->requiredResourceCountIsPrecise = true; for (const auto& source : parser.sources) { SourceType type = source->getType(); auto handleTiledSource = [&] (const variant<std::string, Tileset>& urlOrTileset, const uint16_t tileSize) { if (urlOrTileset.is<Tileset>()) { - result.requiredResourceCount += - definition.tileCount(type, tileSize, urlOrTileset.get<Tileset>().zoomRange); + result->requiredResourceCount += + tileCount(definition, type, tileSize, urlOrTileset.get<Tileset>().zoomRange); } else { - result.requiredResourceCount += 1; + result->requiredResourceCount += 1; const auto& url = urlOrTileset.get<std::string>(); optional<Response> sourceResponse = offlineDatabase.get(Resource::source(url)); if (sourceResponse) { style::conversion::Error error; optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse->data, error); if (tileset) { - result.requiredResourceCount += - definition.tileCount(type, tileSize, (*tileset).zoomRange); + result->requiredResourceCount += + tileCount(definition, type, tileSize, (*tileset).zoomRange); } } else { - result.requiredResourceCountIsPrecise = false; + result->requiredResourceCountIsPrecise = false; } } }; @@ -111,7 +174,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize()); break; } - + case SourceType::RasterDEM: { const auto& rasterDEMSource = *source->as<RasterDEMSource>(); handleTiledSource(rasterDEMSource.getURLOrTileset(), rasterDEMSource.getTileSize()); @@ -121,7 +184,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { case SourceType::GeoJSON: { const auto& geojsonSource = *source->as<GeoJSONSource>(); if (geojsonSource.getURL()) { - result.requiredResourceCount += 1; + result->requiredResourceCount += 1; } break; } @@ -129,7 +192,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { case SourceType::Image: { const auto& imageSource = *source->as<ImageSource>(); if (imageSource.getURL()) { - result.requiredResourceCount += 1; + result->requiredResourceCount += 1; } break; } @@ -142,21 +205,22 @@ OfflineRegionStatus OfflineDownload::getStatus() const { } if (!parser.glyphURL.empty()) { - result.requiredResourceCount += parser.fontStacks().size() * GLYPH_RANGES_PER_FONT_STACK; + result->requiredResourceCount += parser.fontStacks().size() * GLYPH_RANGES_PER_FONT_STACK; } if (!parser.spriteURL.empty()) { - result.requiredResourceCount += 2; + result->requiredResourceCount += 2; } - return result; + return *result; } void OfflineDownload::activateDownload() { status = OfflineRegionStatus(); status.downloadState = OfflineRegionDownloadState::Active; status.requiredResourceCount++; - ensureResource(Resource::style(definition.styleURL), [&](Response styleResponse) { + ensureResource(Resource::style(definition.match([](auto& reg){ return reg.styleURL; })), + [&](Response styleResponse) { status.requiredResourceCountIsPrecise = true; style::Parser parser; @@ -202,7 +266,7 @@ void OfflineDownload::activateDownload() { handleTiledSource(rasterSource.getURLOrTileset(), rasterSource.getTileSize()); break; } - + case SourceType::RasterDEM: { const auto& rasterDEMSource = *source->as<RasterDEMSource>(); handleTiledSource(rasterDEMSource.getURLOrTileset(), rasterDEMSource.getTileSize()); @@ -242,8 +306,9 @@ void OfflineDownload::activateDownload() { } if (!parser.spriteURL.empty()) { - queueResource(Resource::spriteImage(parser.spriteURL, definition.pixelRatio)); - queueResource(Resource::spriteJSON(parser.spriteURL, definition.pixelRatio)); + auto pixelRatio = definition.match([](auto& reg){ return reg.pixelRatio; }); + queueResource(Resource::spriteImage(parser.spriteURL, pixelRatio)); + queueResource(Resource::spriteJSON(parser.spriteURL, pixelRatio)); } continueDownload(); @@ -291,11 +356,12 @@ void OfflineDownload::queueResource(Resource resource) { } void OfflineDownload::queueTiles(SourceType type, uint16_t tileSize, const Tileset& tileset) { - for (const auto& tile : definition.tileCover(type, tileSize, tileset.zoomRange)) { + tileCover(definition, type, tileSize, tileset.zoomRange, [&](const auto& tile) { status.requiredResourceCount++; - resourcesRemaining.push_back( - Resource::tile(tileset.tiles[0], definition.pixelRatio, tile.x, tile.y, tile.z, tileset.scheme)); - } + resourcesRemaining.push_back(Resource::tile( + tileset.tiles[0], definition.match([](auto& def) { return def.pixelRatio; }), tile.x, + tile.y, tile.z, tileset.scheme)); + }); } void OfflineDownload::ensureResource(const Resource& resource, diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 2e76f6eec4..faaa85efd8 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -1,29 +1,61 @@ #include "sqlite3.hpp" #include <sqlite3.h> +#include <algorithm> #include <cassert> #include <cstring> #include <cstdio> #include <chrono> #include <experimental/optional> +#include <mbgl/util/traits.hpp> #include <mbgl/util/logging.hpp> namespace mapbox { namespace sqlite { +static_assert(mbgl::underlying_type(ResultCode::OK) == SQLITE_OK, "error"); +static_assert(mbgl::underlying_type(ResultCode::Error) == SQLITE_ERROR, "error"); +static_assert(mbgl::underlying_type(ResultCode::Internal) == SQLITE_INTERNAL, "error"); +static_assert(mbgl::underlying_type(ResultCode::Perm) == SQLITE_PERM, "error"); +static_assert(mbgl::underlying_type(ResultCode::Abort) == SQLITE_ABORT, "error"); +static_assert(mbgl::underlying_type(ResultCode::Busy) == SQLITE_BUSY, "error"); +static_assert(mbgl::underlying_type(ResultCode::Locked) == SQLITE_LOCKED, "error"); +static_assert(mbgl::underlying_type(ResultCode::NoMem) == SQLITE_NOMEM, "error"); +static_assert(mbgl::underlying_type(ResultCode::ReadOnly) == SQLITE_READONLY, "error"); +static_assert(mbgl::underlying_type(ResultCode::Interrupt) == SQLITE_INTERRUPT, "error"); +static_assert(mbgl::underlying_type(ResultCode::IOErr) == SQLITE_IOERR, "error"); +static_assert(mbgl::underlying_type(ResultCode::Corrupt) == SQLITE_CORRUPT, "error"); +static_assert(mbgl::underlying_type(ResultCode::NotFound) == SQLITE_NOTFOUND, "error"); +static_assert(mbgl::underlying_type(ResultCode::Full) == SQLITE_FULL, "error"); +static_assert(mbgl::underlying_type(ResultCode::CantOpen) == SQLITE_CANTOPEN, "error"); +static_assert(mbgl::underlying_type(ResultCode::Protocol) == SQLITE_PROTOCOL, "error"); +static_assert(mbgl::underlying_type(ResultCode::Schema) == SQLITE_SCHEMA, "error"); +static_assert(mbgl::underlying_type(ResultCode::TooBig) == SQLITE_TOOBIG, "error"); +static_assert(mbgl::underlying_type(ResultCode::Constraint) == SQLITE_CONSTRAINT, "error"); +static_assert(mbgl::underlying_type(ResultCode::Mismatch) == SQLITE_MISMATCH, "error"); +static_assert(mbgl::underlying_type(ResultCode::Misuse) == SQLITE_MISUSE, "error"); +static_assert(mbgl::underlying_type(ResultCode::NoLFS) == SQLITE_NOLFS, "error"); +static_assert(mbgl::underlying_type(ResultCode::Auth) == SQLITE_AUTH, "error"); +static_assert(mbgl::underlying_type(ResultCode::Range) == SQLITE_RANGE, "error"); +static_assert(mbgl::underlying_type(ResultCode::NotADB) == SQLITE_NOTADB, "error"); + class DatabaseImpl { public: DatabaseImpl(sqlite3* db_) : db(db_) { + const int error = sqlite3_extended_result_codes(db, true); + if (error != SQLITE_OK) { + mbgl::Log::Warning(mbgl::Event::Database, error, "Failed to enable extended result codes: %s", sqlite3_errmsg(db)); + } } ~DatabaseImpl() { const int error = sqlite3_close(db); if (error != SQLITE_OK) { - mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", sqlite3_errmsg(db), error); + mbgl::Log::Error(mbgl::Event::Database, error, "Failed to close database: %s", sqlite3_errmsg(db)); } } @@ -65,11 +97,8 @@ public: template <typename T> using optional = std::experimental::optional<T>; -static void errorLogCallback(void *, const int err, const char *msg) { - mbgl::Log::Record(mbgl::EventSeverity::Info, mbgl::Event::Database, err, "%s", msg); -} - -const static bool sqliteVersionCheck __attribute__((unused)) = []() { +__attribute__((constructor)) +static void initalize() { if (sqlite3_libversion_number() / 1000000 != SQLITE_VERSION_NUMBER / 1000000) { char message[96]; snprintf(message, 96, @@ -78,15 +107,17 @@ const static bool sqliteVersionCheck __attribute__((unused)) = []() { throw std::runtime_error(message); } +#ifndef NDEBUG // Enable SQLite logging before initializing the database. - sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, nullptr); - - return true; -}(); + sqlite3_config(SQLITE_CONFIG_LOG, [](void *, const int err, const char *msg) { + mbgl::Log::Record(mbgl::EventSeverity::Debug, mbgl::Event::Database, err, "%s", msg); + }, nullptr); +#endif +} mapbox::util::variant<Database, Exception> Database::tryOpen(const std::string &filename, int flags) { sqlite3* db = nullptr; - const int error = sqlite3_open_v2(filename.c_str(), &db, flags, nullptr); + const int error = sqlite3_open_v2(filename.c_str(), &db, flags | SQLITE_OPEN_URI, nullptr); if (error != SQLITE_OK) { const auto message = sqlite3_errmsg(db); return Exception { error, message }; diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp index 612e92acc6..16f76a0d1a 100644 --- a/platform/default/sqlite3.hpp +++ b/platform/default/sqlite3.hpp @@ -15,7 +15,7 @@ enum OpenFlag : int { ReadWriteCreate = 0b110, }; -enum class ResultCode : int { +enum class ResultCode : uint8_t { OK = 0, Error = 1, Internal = 2, @@ -40,24 +40,25 @@ enum class ResultCode : int { NoLFS = 22, Auth = 23, Range = 25, - NotADB = 26 + NotADB = 26, +}; + +enum class ExtendedResultCode : uint8_t { + Unknown = 0, + ReadOnlyDBMoved = 4, }; class Exception : public std::runtime_error { public: - Exception(int err, const char* msg) - : std::runtime_error(msg), code(static_cast<ResultCode>(err)) { - } - Exception(ResultCode err, const char* msg) - : std::runtime_error(msg), code(err) { - } + Exception(ResultCode err, const char* msg) : Exception(static_cast<int>(err), msg) {} + Exception(int err, const char* msg) : Exception(err, std::string{ msg }) {} Exception(int err, const std::string& msg) - : std::runtime_error(msg), code(static_cast<ResultCode>(err)) { - } - Exception(ResultCode err, const std::string& msg) - : std::runtime_error(msg), code(err) { + : std::runtime_error(msg), + code(static_cast<ResultCode>(err)), + extendedCode(static_cast<ExtendedResultCode>(err >> 8)) { } const ResultCode code = ResultCode::OK; + const ExtendedResultCode extendedCode = ExtendedResultCode::Unknown; }; class DatabaseImpl; diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index a14021cf65..bfc5e77af8 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,21 +2,38 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 4.3.0 +## master + +* The predefined values of `MGLMapView.decelerationRate` are now typed as `MGLMapViewDecelerationRate`s for improved bridging to Swift. ([#12584](https://github.com/mapbox/mapbox-gl-native/pull/12584)) +* When a symbol in an `MGLSymbolStyleLayer` has both an icon and text, both are shown or hidden together based on available space. ([#12521](https://github.com/mapbox/mapbox-gl-native/pull/12521)) +* The `-[MGLMapView visibleFeaturesAtPoint:]` method can now return features near tile boundaries at high zoom levels. ([#12570](https://github.com/mapbox/mapbox-gl-native/pull/12570)) +* Fixed inconsistencies in exception naming. ([#12583](https://github.com/mapbox/mapbox-gl-native/issues/12583)) +* Added `MGLShapeOfflineRegion` for defining arbitrarily shaped offline regions [#11447](https://github.com/mapbox/mapbox-gl-native/pull/11447) +* Added a one-time warning about possible attribute loss when initializing an `MGLShapeSource` with an `MGLShapeCollection` [#12625](https://github.com/mapbox/mapbox-gl-native/pull/12625) +* Added an `-[MGLMapViewDelegate mapView:shapeAnnotationIsEnabled:]` method to specify whether an annotation is selectable. ([#12352](https://github.com/mapbox/mapbox-gl-native/pull/12352)) + +## 4.3.0 - August 15, 2018 ### Styles and rendering * Added an `MGLMapView.preferredFramesPerSecond` property that controls the rate at which the map view is rendered. The default rate now adapts to device capabilities to provide a smoother experience. ([#12501](https://github.com/mapbox/mapbox-gl-native/issues/12501)) +* Token string syntax (`"{token}"`) in `MGLSymbolStyleLayer` `text` and `iconImageName` properties is now correctly converted to the appropriate `NSExpression` equivalent. ([#11659](https://github.com/mapbox/mapbox-gl-native/issues/11659)) +* Fixed a crash when switching between two styles having layers with the same identifier but different layer types. ([#12432](https://github.com/mapbox/mapbox-gl-native/issues/12432)) +* Added a new option to `MGLSymbolPlacement`, `MGLSymbolPlacementLineCenter`, that places the label relative to the center of the geometry. ([#12337](https://github.com/mapbox/mapbox-gl-native/pull/12337)) +* Added a `MGLShapeSourceOptionLineDistanceMetrics` property that enables or disables calculating line distance metrics. + +## User location + +* Added an `MGLMapView.locationManager` property and `MGLLocationManager` protocol for tracking user location using a custom alternative to `CLLocationManager`. ([#12013](https://github.com/mapbox/mapbox-gl-native/pull/12013)) +* Fixed a crash that occurred when `MMELocationManager` was deallocated and the delegate was reporting updates. ([#12542](https://github.com/mapbox/mapbox-gl-native/pull/12542)) ### Other changes * Fixed a crash that occurred when the user started a gesture before the drift animation for a previous gesture was complete. ([#12148](https://github.com/mapbox/mapbox-gl-native/pull/12148)) -* Token string syntax (`"{token}"`) in `MGLSymbolStyleLayer` `text` and `iconImageName` properties is now correctly converted to the appropriate `NSExpression` equivalent. ([#11659](https://github.com/mapbox/mapbox-gl-native/issues/11659)) -* Added an `MGLMapView.locationManager` property and `MGLLocationManager` protocol for tracking user location using a custom alternative to `CLLocationManager`. ([#12013](https://github.com/mapbox/mapbox-gl-native/pull/12013)) -* Fixed a crash when switching between two styles having layers with the same identifier but different layer types. ([#12432](https://github.com/mapbox/mapbox-gl-native/issues/12432)) * Fixed an issue where the symbols for `MGLMapPointForCoordinate` could not be found. ([#12445](https://github.com/mapbox/mapbox-gl-native/issues/12445)) * Fixed an issue causing country and ocean labels to disappear after calling `-[MGLStyle localizeLabelsIntoLocale:]` when the system language is set to Simplified Chinese. ([#12164](https://github.com/mapbox/mapbox-gl-native/issues/12164)) -* Fixed a crash that occurred when `MMELocationManager` was deallocated and the delegate was reporting updates. ([#12542](https://github.com/mapbox/mapbox-gl-native/pull/12542)) +* Closed a security vulnerability introduced in 4.1.0 that would potentially allow the owner of a style to compromise apps loading that style. ([#12571](https://github.com/mapbox/mapbox-gl-native/pull/12571)) +* Reduced binary size and improved performance by enabling LTO. ([#12502](https://github.com/mapbox/mapbox-gl-native/pull/12502)) ## 4.2.0 - July 18, 2018 diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index 834c1b9aa1..427a99d603 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.3.0-alpha.2' + version = '4.3.0' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index 513bdf7b3a..162b4abcb8 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.3.0-alpha.2' + version = '4.3.0' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 796d8425d5..309d84e52e 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.3.0-alpha.2' + version = '4.3.0' m.name = 'Mapbox-iOS-SDK' m.version = version diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 71bad66aee..259c1191bc 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -9,6 +9,7 @@ #import "MBXEmbeddedMapViewController.h" #import <Mapbox/Mapbox.h> +#import "../src/MGLMapView_Experimental.h" #import <objc/runtime.h> @@ -195,7 +196,7 @@ CLLocationCoordinate2D randomWorldCoordinate() { @property (nonatomic) BOOL customUserLocationAnnnotationEnabled; @property (nonatomic, getter=isLocalizingLabels) BOOL localizingLabels; @property (nonatomic) BOOL reuseQueueStatsEnabled; -@property (nonatomic) BOOL showZoomLevelEnabled; +@property (nonatomic) BOOL mapInfoHUDEnabled; @property (nonatomic) BOOL shouldLimitCameraChanges; @property (nonatomic) BOOL randomWalk; @end @@ -280,7 +281,7 @@ CLLocationCoordinate2D randomWorldCoordinate() { [defaults setInteger:self.mapView.userTrackingMode forKey:@"MBXUserTrackingMode"]; [defaults setBool:self.mapView.showsUserLocation forKey:@"MBXShowsUserLocation"]; [defaults setInteger:self.mapView.debugMask forKey:@"MBXDebugMask"]; - [defaults setBool:self.showZoomLevelEnabled forKey:@"MBXShowsZoomLevelHUD"]; + [defaults setBool:self.mapInfoHUDEnabled forKey:@"MBXShowsZoomLevelHUD"]; [defaults synchronize]; } @@ -308,7 +309,7 @@ CLLocationCoordinate2D randomWorldCoordinate() { } if ([defaults boolForKey:@"MBXShowsZoomLevelHUD"]) { - self.showZoomLevelEnabled = YES; + self.mapInfoHUDEnabled = YES; [self updateHUD]; } } @@ -439,7 +440,7 @@ CLLocationCoordinate2D randomWorldCoordinate() { [NSString stringWithFormat:@"%@ Reuse Queue Stats", (_reuseQueueStatsEnabled ? @"Hide" :@"Show")], @"Start World Tour", @"Random Tour", - [NSString stringWithFormat:@"%@ Zoom/Pitch/Direction Label", (_showZoomLevelEnabled ? @"Hide" :@"Show")], + [NSString stringWithFormat:@"%@ Map Info HUD", (_mapInfoHUDEnabled ? @"Hide" :@"Show")], @"Embedded Map View", [NSString stringWithFormat:@"%@ Second Map", ([self.view viewWithTag:2] == nil ? @"Show" : @"Hide")], [NSString stringWithFormat:@"Show Labels in %@", (_localizingLabels ? @"Default Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])], @@ -657,14 +658,14 @@ CLLocationCoordinate2D randomWorldCoordinate() { { self.reuseQueueStatsEnabled = !self.reuseQueueStatsEnabled; self.hudLabel.hidden = !self.reuseQueueStatsEnabled; - self.showZoomLevelEnabled = NO; + self.mapInfoHUDEnabled = NO; [self updateHUD]; break; } case MBXSettingsMiscellaneousShowZoomLevel: { - self.showZoomLevelEnabled = !self.showZoomLevelEnabled; - self.hudLabel.hidden = !self.showZoomLevelEnabled; + self.mapInfoHUDEnabled = !self.mapInfoHUDEnabled; + self.hudLabel.hidden = !self.mapInfoHUDEnabled; self.reuseQueueStatsEnabled = NO; [self updateHUD]; break; @@ -2191,7 +2192,7 @@ CLLocationCoordinate2D randomWorldCoordinate() { } - (void)updateHUD { - if (!self.reuseQueueStatsEnabled && !self.showZoomLevelEnabled) return; + if (!self.reuseQueueStatsEnabled && !self.mapInfoHUDEnabled) return; if (self.hudLabel.hidden) self.hudLabel.hidden = NO; @@ -2203,8 +2204,11 @@ CLLocationCoordinate2D randomWorldCoordinate() { queuedAnnotations += queue.count; } hudString = [NSString stringWithFormat:@"Visible: %ld Queued: %ld", (unsigned long)self.mapView.visibleAnnotations.count, (unsigned long)queuedAnnotations]; - } else if (self.showZoomLevelEnabled) { - hudString = [NSString stringWithFormat:@"%.2f ∕ ↕\U0000FE0E%.f° ∕ %.f°", self.mapView.zoomLevel, self.mapView.camera.pitch, self.mapView.direction]; + } else if (self.mapInfoHUDEnabled) { + if (!self.mapView.experimental_enableFrameRateMeasurement) self.mapView.experimental_enableFrameRateMeasurement = YES; + hudString = [NSString stringWithFormat:@"%.f FPS (%.1fms) ∕ %.2f ∕ ↕\U0000FE0E%.f° ∕ %.f°", + roundf(self.mapView.averageFrameRate), self.mapView.averageFrameTime, + self.mapView.zoomLevel, self.mapView.camera.pitch, self.mapView.direction]; } [self.hudLabel setTitle:hudString forState:UIControlStateNormal]; diff --git a/platform/ios/docs/guides/Adding Markers to a Map.md b/platform/ios/docs/guides/Adding Markers to a Map.md deleted file mode 100644 index b33b536d44..0000000000 --- a/platform/ios/docs/guides/Adding Markers to a Map.md +++ /dev/null @@ -1,62 +0,0 @@ -# Adding Markers to a Map - -Mapbox offers a few different ways to add markers to a map, each with different tradeoffs. Below is an overview of the variety of approaches that can be used. - -## **Annotations API** - -Our annotations API includes the `MGLAnnotationImage`, and `MGLAnnotationView` classes. These are most similar to MapKit’s annotation class and provide a familiar interface for working with markers and callouts. - -| <img src="img/adding-points-to-a-map/annotation-image.png" alt="MGLAnnotationImage" style="height: 500px;"/> | <img src="img/adding-points-to-a-map/annotation-view.png" alt="MGLAnnotationView" style="height: 500px;"/> | -|----------------------|---------------------| -| `MGLAnnotationImage` | `MGLAnnotationView` | - -**MGLAnnotationImage** is an annotation class that is easily customizable with any `UIImage`. -It is highly performant, as the images are rendered directly using OpenGL. However, if you need to animate your annotations or control z-layer ordering, consider working with **MGLAnnotationView** which supports any animation that can be applied to a `UIView`. View hierarchy can be manipulated by using instance methods available on `UIView` such as `-[UIView bringSubviewToFront:]`. - -**MGLAnnotationView** is an annotation class that is an easily customizable `UIView`. Use this class if you need your markers to be dynamic or animated. `MGLAnnotationView` has a significant advantage over `MGLAnnotationImage` when you need every annotation to be unique. For example, annotation views are ideal for showing user locations on a map using high-resolution profile pictures. However, the map can slow down when many annotation views are visible at the same time, so if you need to add a very large number of markers, consider using our runtime styling APIs instead. - -Both `MGLAnnotationImage` and `MGLAnnotationView` can become interactive by adding a [few lines of code](https://www.mapbox.com/ios-sdk/examples/marker/). When the user taps an annotation, the annotation’s name appears in a basic callout. An annotation view can additionally respond to [drag-and-drop gestures](https://www.mapbox.com/ios-sdk/examples/draggable-views/). - -## **Runtime styling API** - -For full control of how markers are displayed on a map, consider using our [runtime styling](runtime-styling.html) APIs. Like `MGLAnnotationImage`, it is a performant approach to adding markers because they rendered directly using OpenGL. However, the runtime styling APIs also provide support for rendering labels together with icons, finer control of z-ordering, and clustering, so consider using this set of APIs if you need to display a large amount of highly customizable markers. - -Our runtime styling API is the most powerful option if you need to create rich data visualizations within in your map, but it is the most complex and has a steeper learning curve than our annotations API. - -The runtime styling API includes our `MGLSymbolStyleLayer` and `MGLCircleStyleLayer` classes that can be used to dynamically display on markers on map when used in conjunction with either an `MGLVectorSource` or an `MGLShapeSource`. - -If you need to implement callouts with the `MGLSymbolStyleLayer` or `MGLCircleStyleLayer`, you will need to implement your own tap gesture recognizer that calls `-[MGLMapView visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:]` to get the tapped point feature, then show a `UIView` you provide. Additionally, if you need to animate markers when using the runtime styling APIs, consider using a timer to update the source data coordinates accordingly. - -| <img src="img/adding-points-to-a-map/circle-layer.png" alt="MGLCircleStyleLayer" style="height: 500px;"/> | <img src="img/adding-points-to-a-map/symbol-layer.png" alt="MGLSymbolStyleLayer" style="height: 500px;"/> | -|----------------------|---------------------| -| `MGLCircleStyleLayer` | `MGLSymbolStyleLayer` | - -The **MGLCircleStyleLayer** class is the style layer class responsible for displaying the source’s point features as circle-shaped markers. You can specify circle fill and outline colors, as well as size. You can also dynamically change the circle’s styling properties based on any attributes your source data contains. - -The **MGLSymbolStyleLayer** class is the style layer class responsible for displaying the source’s point features as icons and labels. You can use custom images as icons and also combine text labels, placing them exactly where you specify. You can also dynamically change the symbol’s styling properties based on any attributes your source data contains. - -Still undecided on which approach will work best for your use case? [Reach out to our support team](https://www.mapbox.com/contact/). - -See the table below for a summary of APIs that can be used to add markers to a map: - -✅ Recommended - -⚠️ Supported with caveats - -➖ Unavailable or not supported - - -| Feature | MGLAnnotationView | MGLAnnotationImage | MGLSymbolStyleLayer | MGLCircleStyleLayer | -|----------------------------------------------------|--------------------------------------------------------------------|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| -| Customizability | ✅ Text labels, interactive subviews | ⚠️ Static images only | ✅ Full support for text labels and label placement | ✅Customize circle color and outline | -| Borrows familiar concepts from | MapKit, Google Maps SDK | ➖ | Mapbox GL JS, Mapbox Studio | Mapbox GL JS, Mapbox Studio | -| Can use images | ✅ | ✅ | ✅ | ➖ | -| Can use text | ✅ | ➖ | ✅ | ➖ | -| Control Z-index | ✅ | ➖ | ⚠️ Add multiple layers at, above, or below a specified layer index to control ordering | ⚠️ Add multiple layers at, above, or below a specified layer index to control ordering | -| Drag and drop | ✅ | ➖ | ➖ | ➖ | -| Core Animation support | ✅ | ➖ | ➖ | ➖ | -| Add/move/replace data | ✅ | ✅ | ⚠️ Partial data updates are less performant than using annotations | ⚠️ Partial data updates are less performant than using annotations | -| SceneKit support | ✅ | ➖ | ➖ | ➖ | -| Can be dynamically styled based on data attributes | ✅ Subclass `MGLPointAnnotation` to add custom attributes | ✅ Subclass `MGLPointAnnotation` to add custom attributes | ✅ | ✅ | -| Supports callouts | ✅ Built-in callouts included | ✅ Built-in callouts included | ⚠️ Implement your own gesture recognizer that uses feature querying, then create custom UIViews to mimic native callouts | ⚠️ Implement your own gesture recognizer that uses feature querying, then create custom UIViews to mimic native callouts | -| Supports clustering | ⚠️ Use a [third-party plugin](https://github.com/hulab/ClusterKit/) | ➖ | ✅ | ✅ | diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 24a2f7d3a4..2552bd283e 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -42,7 +42,7 @@ may be shorter than on a desktop computer. Some of your users may use the Larger Dynamic Type and Accessibility Text features to increase the size of all text on the device. You can use the [runtime styling API](#manipulating-the-style-at-runtime) to adjust your style’s -font and icon sizes accordingly. + font and icon sizes accordingly. Design sprite images and choose font weights that look crisp on both standard-resolution displays and Retina displays. This SDK supports the same @@ -98,7 +98,7 @@ represented at runtime by an `MGLStyle` object, which provides access to various `MGLSource` and `MGLStyleLayer` objects that represent content sources and style layers, respectively. For more information about the capabilities exposed by the runtime styling API, -see “[Runtime Styling](runtime-styling.html)”. +see “[Runtime Styling](https://www.mapbox.com/ios-sdk/maps/overview/runtime-styling/)”. The names of runtime styling classes and properties on iOS are generally consistent with the style specification and Mapbox Studio’s Styles editor. Any @@ -171,6 +171,7 @@ In style JSON | In the SDK `cluster` | `MGLShapeSourceOptionClustered` `clusterRadius` | `MGLShapeSourceOptionClusterRadius` `clusterMaxZoom` | `MGLShapeSourceOptionMaximumZoomLevelForClustering` +`lineMetrics` | `MGLShapeSourceOptionLineDistanceMetrics` To create a shape source from local GeoJSON data, first [convert the GeoJSON data into a shape](working-with-geojson-data.html#converting-geojson-data-into-shape-objects), diff --git a/platform/ios/docs/guides/Migrating to Expressions.md b/platform/ios/docs/guides/Migrating to Expressions.md index 96d4df6853..d92aab0ffc 100644 --- a/platform/ios/docs/guides/Migrating to Expressions.md +++ b/platform/ios/docs/guides/Migrating to Expressions.md @@ -5,7 +5,7 @@ # Migrating from Style Functions to Expressions -[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source. +[Runtime Styling](https://www.mapbox.com/ios-sdk/maps/overview/runtime-styling/) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source. With Mapbox Maps SDK for iOS v4.0.0, style functions have been replaced with expressions. These provide even more tools for developers who want to style their maps dynamically. This guide outlines some tips for migrating from style functions to expressions, and offers an overview of some things that developers can do with expressions. @@ -113,7 +113,7 @@ mapView.style?.insertLayer(layer, below: symbolLayer) If you previously used an interpolation base greater than `0` (other than `1`), you can use `MGLExpressionInterpolationMode.exponential` as the curve type for `+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` or `'exponential'` as the curve type for [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code). The `parameters` argument takes that interpolation base. This interpolates between values exponentially, creating an accelerated ramp effect. -Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key. +Here’s a visualization from Mapbox Studio (see [Mapbox Studio and iOS](https://www.mapbox.com/ios-sdk/maps/overview/mapbox-studio/)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key. <img src="img/data-driven-styling/exponential-function.png" height=344/> <img src="img/data-driven-styling/exponential-function-1.png" height=344/> diff --git a/platform/ios/docs/guides/Runtime Styling.md b/platform/ios/docs/guides/Runtime Styling.md deleted file mode 100644 index 194d8b3bdd..0000000000 --- a/platform/ios/docs/guides/Runtime Styling.md +++ /dev/null @@ -1,57 +0,0 @@ -# Runtime Styling - -Mapbox’s runtime styling features allow you direct control over every layer in your maps with code. It’s now possible to create dynamic maps and visualizations that aren’t possible with other mobile maps SDKs. - -Runtime styling expands upon the design power of [Mapbox Studio](https://www.mapbox.com/mapbox-studio/) and exposes all of the same properties and attributes directly to mobile developers in our SDK. - -Beyond the custom styled maps that you can create with Mapbox Studio, you can now change the look and feel of your map on the fly having maps in your app visually respond to user interaction or or context. Or leverage the power of OpenGL for highly performant and complex data visualizations. Now it’s possible to mix in your own data and bring your map to life. - -## Example use cases - -As an example of what’s possible with runtime styling, consider some of the following use cases: - -### Styling maps on the fly - -At runtime, you can tailor the map specifically to your user interface. Tweak colors, text, and icons to match the style to your brand. - -![dynamic styles](img/runtime-styling/DynamicStyles.gif "an example showing dynamic styles") - -For maps that aren’t going to change in response to custom data or user interaction, consider creating a custom map style with [Mapbox Studio](https://www.mapbox.com/mapbox-studio/). - -### Map interactivity - -You can customize the map to the point of having it respond dynamically based on the actions your users are taking. Increase the text size of streets while a user is driving, emphasize points of interest tailored to a user’s preferences, or change your UI if users are at parks, trails, landmarks, or rivers. - -![emojis](img/runtime-styling/Emoji.gif "an example showing emoji interaction") - -### Powerful data visualization - -Mapbox maps are built on top of OpenGL and can support rendering data without the traditional overhead of `UIView`-based map annotations. - -Mapbox can support data visualizations that were slow or impossible with traditional map SDKs. Render heatmaps, visualize population density, or even go so far as updating the snow levels in the mountains to match recent snowfall. - -![hex bins](img/runtime-styling/HexBins.gif "an example using hex bins") -![population](img/runtime-styling/Population.gif "an example showing population density") -![snow levels](img/runtime-styling/SnowLevels.gif "an example visualizing snow levels in the mountains") - -### Powerful annotations - -The Mapbox SDK gives you access to all of the same tools we use to render our default map styles. Instead of using generic pin markers, enrich your place data or custom polygons with icons and labels that make your maps stand out. - -![custom annotations](img/runtime-styling/CustomAnnotations.gif "an example showing custom annotations") - -### Custom shapes - -Draw custom shapes on the map the same way you would a custom `UIView` or `CALayer`. These shapes keep their geographic scale and are perfect for visualizing everything from indoor floor plans to metro systems to hurricane tracks. - -## Next steps - -Check out the [iOS code examples](https://www.mapbox.com/ios-sdk/examples/runtime-toggle-layer/) to learn more about how to use runtime styling in your own apps. - -## Resources - -* [Information for style authors](for-style-authors.html) -* [Mapbox Streets source reference](https://www.mapbox.com/vector-tiles/mapbox-streets-v7/) -* [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/) -* [Mapbox Studio](https://www.mapbox.com/mapbox-studio/) -* [iOS code examples](https://www.mapbox.com/ios-sdk/examples/) diff --git a/platform/ios/docs/guides/Working with Mapbox Studio.md b/platform/ios/docs/guides/Working with Mapbox Studio.md deleted file mode 100644 index 232b165efd..0000000000 --- a/platform/ios/docs/guides/Working with Mapbox Studio.md +++ /dev/null @@ -1,96 +0,0 @@ -# Working with Mapbox Studio - -[Mapbox Studio’s Styles editor](http://mapbox.com/studio) is Mapbox’s tool for creating custom map styles. It also serves as an excellent tool for rapidly prototyping dynamic maps and [runtime styling](runtime-styling.html) interactions for iOS. - -## Creating a base style - -Start by heading to [mapbox.com/studio](https://www.mapbox.com/studio) and creating a new style. Any style that’s close to what you’ll be using in your app is ideal. - -## Prototyping with data - -The goal in using Mapbox Studio for prototyping runtime styling implementations is to test data presentation assumptions as quickly as possible. With the Mapbox Studio tools, you can import a small subset of your own real data, fake data as a placeholder, or prototype with existing Mapbox data. - -### Prototyping with Mapbox data -The default [Mapbox Streets tileset](https://www.mapbox.com/studio/tilesets/mapbox.mapbox-streets-v7/) might offer data similar to your own that you can use to style before you swap in your own data at runtime. - -For example, if you’re looking to prototype points of interest, consider the `poi_label` layer; if you want to style GPS traces, the `roads` layer might be a good proxy. Take a look at what’s available in [Mapbox Streets](https://www.mapbox.com/studio/tilesets/mapbox.mapbox-streets-v7/): there’s probably a layer that closely matches your data. - -### Importing real data -If you can’t find a good approximation for your data in Mapbox Streets, consider uploading a small subset of your data into Mapbox Studio as a custom tileset. - -From the [Mapbox Studio Dashboard](https://www.mapbox.com/studio/), click `Tilesets` in the sidebar, then click `New Tileset` to get started with most common geo file formats including KML, GPX, GeoJSON, Shapefiles, and CSV. - -### Faking placeholder data -If you don’t have any custom data on hand in a format that works easily with the Tileset importer, you can fake placeholder data with the Dataset Editor. - -From the [Mapbox Studio Dashboard](https://www.mapbox.com/studio/), click `Datasets` in the sidebar, then click on `New Dataset` to get started. - -Zoom into your desired location and use the draw tools on the left to start creating a set of sample data. - -![create shapes](img/studio-workflow/create-polygons.gif) - -Next, add data properties you’d like to use to drive your style. Consider categorical properties or numeric properties that you’d use to filter and group your data. Text properties can be used to display icons or labels. - -![add properties](img/studio-workflow/add-properties.gif) - -**General Guidelines:** - -* Text along a line: add line with a text property -* Text at specific points on a line or polygon: in addition to the line, create points at the specific points you’d like with text properties -* If you want circles where scale doesn’t matter relative to the geography (e.g. always 20 pixels), you can add as a point and style with a circle layer or a symbol -* If you want circles or arcs where the scale matters (e.g. 10 mile radius), you’ll need to approximately freehand a polygon that you can create more precisely later in code. - -When you’re done, save your dataset and export as a tileset. When that’s complete, add your tileset to your style. - -### Import into your style - -1. Click `New Layer` -2. Select your tileset -3. Select your shape type: - * `Symbol`: best for text and icons - * `Line`: best for lines or adding strokes to polygons - * `Fill`: best for filling polygons - * `Circle`: for styling points or nodes along a line or polygon as circles. If you need circles of a fixed radius (e.g. 1 mile radius), you should model your data as a polygon. -4. Add filters if necessary - * You can selectively filter your data by their properties to group and style separately -5. Click on `Create Layer` - -## Styling with Mapbox Studio - -Mapbox Studio shines for styling your data and the process is much faster than attempting to style natively. - -There are some nuances to understand between the different layer types and how they work together. Don’t be afraid to use the layers sidebar to peek into the techniques used to style the stock Mapbox maps. You can duplicate these layers, re-point the source to your own data, and tweak as needed. - -**Best Practices:** - -* Layers are cheap, so duplicate and update filters liberally. -* If you’d like to stroke polygons you’ll need to use two layers: one a fill and one a stroked line. -* If you want to stroke a line, create two layers, one for the default stroke and one with a wider width for its casing -* If you intend to animate properties or transition between values, consider creating separate layers for each state and toggling visibility to visualize the difference. - -## Implement on iOS with runtime styling - -Once you’re happy with the styles you’ve created, it’s time to [get setup with Mapbox in your app](https://www.mapbox.com/ios-sdk/). - -To implement your prototypes with runtime styling: - -1. Implement `-[MGLMapViewDelegate mapView:didFinishLoadingStyle:]`. -2. Add your real data as a source: - * This can be done using vector data from tileset editor ([example](https://www.mapbox.com/ios-sdk/examples/dds-circle-layer)), custom vector tiles, added as GeoJSON ([example](https://www.mapbox.com/ios-sdk/examples/runtime-add-line), or added manually through the app via `MGLShapeSource` ([example](https://www.mapbox.com/ios-sdk/examples/runtime-multiple-annotations)) -3. For each layer you’ve prototyped in Studio, add its corresponding `MGLStyleLayer` subclass. See [“Configuring the map content’s appearance”](for-style-authors.html#configuring-the-map-content-s-appearance) for the available style layer classes. - -**Translating style attributes from Studio** -For each property you’ve edited in Studio, you can hover over the property name to find the corresponding property in the iOS SDK. It’ll generally be the camelCased version of the Property ID, but see [“Configuring the map content’s appearance”](for-style-authors.html#configuring-the-map-content-s-appearance) for a table of properties that differ between Mapbox Studio and the iOS SDK. - -![property values](img/studio-workflow/property-values.png) - -**Translating stop functions** -It’s possible to use stop functions in Mapbox Studio to transition the style of a layer by its zoom level (e.g. a line that gets wider as you zoom in). These can be translated in the mobile SDKs using `+[MGLSyleValue valueWithInterpolationBase:stops:]`. The rate of change between stops in Studio is represented by `interpolationBase`. - -![Stop functions](img/studio-workflow/stop-functions.png) - -## Resources - -* [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/) -* [Mapbox Studio](https://www.mapbox.com/mapbox-studio/) -* [iOS code examples](https://www.mapbox.com/ios-sdk/examples/) diff --git a/platform/ios/docs/img/adding-points-to-a-map/annotation-image.png b/platform/ios/docs/img/adding-points-to-a-map/annotation-image.png Binary files differdeleted file mode 100644 index b9aa946363..0000000000 --- a/platform/ios/docs/img/adding-points-to-a-map/annotation-image.png +++ /dev/null diff --git a/platform/ios/docs/img/adding-points-to-a-map/annotation-view.png b/platform/ios/docs/img/adding-points-to-a-map/annotation-view.png Binary files differdeleted file mode 100644 index 90c181f664..0000000000 --- a/platform/ios/docs/img/adding-points-to-a-map/annotation-view.png +++ /dev/null diff --git a/platform/ios/docs/img/adding-points-to-a-map/circle-layer.png b/platform/ios/docs/img/adding-points-to-a-map/circle-layer.png Binary files differdeleted file mode 100644 index 5edf88e0da..0000000000 --- a/platform/ios/docs/img/adding-points-to-a-map/circle-layer.png +++ /dev/null diff --git a/platform/ios/docs/img/adding-points-to-a-map/symbol-layer.png b/platform/ios/docs/img/adding-points-to-a-map/symbol-layer.png Binary files differdeleted file mode 100644 index cff39b0bce..0000000000 --- a/platform/ios/docs/img/adding-points-to-a-map/symbol-layer.png +++ /dev/null diff --git a/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif b/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif Binary files differdeleted file mode 100644 index 71d4c4a8a0..0000000000 --- a/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif +++ /dev/null diff --git a/platform/ios/docs/img/runtime-styling/DynamicStyles.gif b/platform/ios/docs/img/runtime-styling/DynamicStyles.gif Binary files differdeleted file mode 100644 index 8854474ede..0000000000 --- a/platform/ios/docs/img/runtime-styling/DynamicStyles.gif +++ /dev/null diff --git a/platform/ios/docs/img/runtime-styling/Emoji.gif b/platform/ios/docs/img/runtime-styling/Emoji.gif Binary files differdeleted file mode 100644 index afb7a42693..0000000000 --- a/platform/ios/docs/img/runtime-styling/Emoji.gif +++ /dev/null diff --git a/platform/ios/docs/img/runtime-styling/HexBins.gif b/platform/ios/docs/img/runtime-styling/HexBins.gif Binary files differdeleted file mode 100644 index 6078ac9e8d..0000000000 --- a/platform/ios/docs/img/runtime-styling/HexBins.gif +++ /dev/null diff --git a/platform/ios/docs/img/runtime-styling/Population.gif b/platform/ios/docs/img/runtime-styling/Population.gif Binary files differdeleted file mode 100644 index 8de14e6422..0000000000 --- a/platform/ios/docs/img/runtime-styling/Population.gif +++ /dev/null diff --git a/platform/ios/docs/img/runtime-styling/SnowLevels.gif b/platform/ios/docs/img/runtime-styling/SnowLevels.gif Binary files differdeleted file mode 100644 index 463e0398d2..0000000000 --- a/platform/ios/docs/img/runtime-styling/SnowLevels.gif +++ /dev/null diff --git a/platform/ios/docs/img/studio-workflow/add-properties.gif b/platform/ios/docs/img/studio-workflow/add-properties.gif Binary files differdeleted file mode 100644 index 6cab64c050..0000000000 --- a/platform/ios/docs/img/studio-workflow/add-properties.gif +++ /dev/null diff --git a/platform/ios/docs/img/studio-workflow/create-polygons.gif b/platform/ios/docs/img/studio-workflow/create-polygons.gif Binary files differdeleted file mode 100644 index 6383dd31df..0000000000 --- a/platform/ios/docs/img/studio-workflow/create-polygons.gif +++ /dev/null diff --git a/platform/ios/docs/img/studio-workflow/property-values.png b/platform/ios/docs/img/studio-workflow/property-values.png Binary files differdeleted file mode 100644 index 2b85db80d6..0000000000 --- a/platform/ios/docs/img/studio-workflow/property-values.png +++ /dev/null diff --git a/platform/ios/docs/img/studio-workflow/stop-functions.png b/platform/ios/docs/img/studio-workflow/stop-functions.png Binary files differdeleted file mode 100644 index 8480cb2d53..0000000000 --- a/platform/ios/docs/img/studio-workflow/stop-functions.png +++ /dev/null diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index b77c67fbb3..3875d46865 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -294,6 +294,8 @@ 8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */; }; 8989B17F201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */; }; 920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */; }; + 9221BAAD2069843A0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9221BAAC2069843A0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h */; }; + 9221BAB020699F8A0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9221BAAC2069843A0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h */; }; 927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */; }; 927FBCFF1F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 927FBD001F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -301,6 +303,12 @@ 927FBD021F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */; }; 929EFFAB1F56DCD4003A77D5 /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; }; 92F2C3ED1F0E3C3A00268EC0 /* MGLRendererFrontend.h in Headers */ = {isa = PBXBuildFile; fileRef = 92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */; }; + 92FC0AEA207CEE16007B6B54 /* MGLShapeOfflineRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = 92FC0AE7207CEE16007B6B54 /* MGLShapeOfflineRegion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 92FC0AEB207CEE16007B6B54 /* MGLShapeOfflineRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = 92FC0AE7207CEE16007B6B54 /* MGLShapeOfflineRegion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 92FC0AEC207CEE16007B6B54 /* MGLShapeOfflineRegion_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 92FC0AE8207CEE16007B6B54 /* MGLShapeOfflineRegion_Private.h */; }; + 92FC0AED207CEE16007B6B54 /* MGLShapeOfflineRegion_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 92FC0AE8207CEE16007B6B54 /* MGLShapeOfflineRegion_Private.h */; }; + 92FC0AEE207CEE16007B6B54 /* MGLShapeOfflineRegion.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92FC0AE9207CEE16007B6B54 /* MGLShapeOfflineRegion.mm */; }; + 92FC0AEF207CEE16007B6B54 /* MGLShapeOfflineRegion.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92FC0AE9207CEE16007B6B54 /* MGLShapeOfflineRegion.mm */; }; 96036A01200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */; }; 96036A02200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */; }; 96036A03200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */; }; @@ -973,11 +981,15 @@ 8989B17A201A48EA0081CF59 /* MGLHeatmapStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLHeatmapStyleLayer.h; sourceTree = "<group>"; }; 8989B17B201A48EA0081CF59 /* MGLHeatmapStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHeatmapStyleLayer.mm; sourceTree = "<group>"; }; 920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLSourceQueryTests.m; path = ../../darwin/test/MGLSourceQueryTests.m; sourceTree = "<group>"; }; + 9221BAAC2069843A0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTilePyramidOfflineRegion_Private.h; sourceTree = "<group>"; }; 927FBCFA1F4DAA8300F8BF1F /* MBXSnapshotsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBXSnapshotsViewController.h; sourceTree = "<group>"; }; 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXSnapshotsViewController.m; sourceTree = "<group>"; }; 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter.h; sourceTree = "<group>"; }; 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapSnapshotter.mm; sourceTree = "<group>"; }; 92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererFrontend.h; sourceTree = "<group>"; }; + 92FC0AE7207CEE16007B6B54 /* MGLShapeOfflineRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeOfflineRegion.h; sourceTree = "<group>"; }; + 92FC0AE8207CEE16007B6B54 /* MGLShapeOfflineRegion_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeOfflineRegion_Private.h; sourceTree = "<group>"; }; + 92FC0AE9207CEE16007B6B54 /* MGLShapeOfflineRegion.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeOfflineRegion.mm; sourceTree = "<group>"; }; 960369FF200565C700510F3D /* NSOrthography+MGLAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSOrthography+MGLAdditions.h"; sourceTree = "<group>"; }; 96036A00200565C700510F3D /* NSOrthography+MGLAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSOrthography+MGLAdditions.m"; sourceTree = "<group>"; }; 96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLNSOrthographyAdditionsTests.m; sourceTree = "<group>"; }; @@ -1008,6 +1020,7 @@ 96E0272C1E57C7E5004B8E66 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; }; 96E0272D1E57C7E6004B8E66 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; }; 96E0272E1E57C7E7004B8E66 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; }; + 96F017292118FBAE00892778 /* MGLMapView_Experimental.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapView_Experimental.h; sourceTree = "<group>"; }; 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = "<group>"; }; AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTelemetryConfig.h; sourceTree = "<group>"; }; AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTelemetryConfig.m; sourceTree = "<group>"; }; @@ -1955,6 +1968,7 @@ CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */, DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */, DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */, + 96F017292118FBAE00892778 /* MGLMapView_Experimental.h */, DA17BE2F1CC4BAC300402C41 /* MGLMapView_Private.h */, DA8848361CBAFB8500AB86E3 /* MGLMapView.h */, DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */, @@ -2116,7 +2130,11 @@ DA8847E61CBAFA5100AB86E3 /* MGLOfflineStorage.h */, DA8848091CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h */, DA88480A1CBAFA6200AB86E3 /* MGLOfflineStorage.mm */, + 92FC0AE8207CEE16007B6B54 /* MGLShapeOfflineRegion_Private.h */, + 92FC0AE7207CEE16007B6B54 /* MGLShapeOfflineRegion.h */, + 92FC0AE9207CEE16007B6B54 /* MGLShapeOfflineRegion.mm */, DA8847ED1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h */, + 9221BAAC2069843A0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h */, DA8848101CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm */, ); name = "Offline Maps"; @@ -2209,6 +2227,7 @@ buildActionMask = 2147483647; files = ( 556660DB1E1D8E8D00E2C41B /* MGLFoundation.h in Headers */, + 92FC0AEA207CEE16007B6B54 /* MGLShapeOfflineRegion.h in Headers */, 35D13AC31D3D19DD00AFB4E0 /* MGLFillStyleLayer.h in Headers */, DA88483A1CBAFB8500AB86E3 /* MGLAnnotationImage.h in Headers */, DAF2571B201901E200367EF5 /* MGLHillshadeStyleLayer.h in Headers */, @@ -2275,6 +2294,7 @@ 071BBB031EE76146001FB02A /* MGLImageSource.h in Headers */, DA8847F41CBAFA5100AB86E3 /* MGLOfflinePack.h in Headers */, DA88482E1CBAFA6200AB86E3 /* NSException+MGLAdditions.h in Headers */, + 9221BAAD2069843A0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h in Headers */, 96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */, 408AA8571DAEDA1700022900 /* NSDictionary+MGLAdditions.h in Headers */, DA88483F1CBAFB8500AB86E3 /* MGLUserLocation.h in Headers */, @@ -2319,6 +2339,7 @@ DA8847F51CBAFA5100AB86E3 /* MGLOfflineRegion.h in Headers */, DA737EE11D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */, ACF969F420CB04E600B23FB7 /* MMEEventsService.h in Headers */, + 92FC0AEC207CEE16007B6B54 /* MGLShapeOfflineRegion_Private.h in Headers */, AC518DFF201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */, DA88481B1CBAFA6200AB86E3 /* MGLGeometry_Private.h in Headers */, 3510FFF91D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */, @@ -2349,6 +2370,7 @@ 556660CA1E1BF3A900E2C41B /* MGLFoundation.h in Headers */, 96E516ED200058A200A02306 /* MGLComputedShapeSource.h in Headers */, 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */, + 92FC0AEB207CEE16007B6B54 /* MGLShapeOfflineRegion.h in Headers */, DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */, 350098BC1D480108004B2AF0 /* MGLVectorTileSource.h in Headers */, FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */, @@ -2377,6 +2399,7 @@ CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */, DABFB86D1CBE9A0F00D62B32 /* MGLAnnotationImage.h in Headers */, DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */, + 92FC0AED207CEE16007B6B54 /* MGLShapeOfflineRegion_Private.h in Headers */, 927FBD001F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */, 3566C7721D4A9198008152BC /* MGLSource_Private.h in Headers */, 353933FF1D3FB7DD003F57D7 /* MGLSymbolStyleLayer.h in Headers */, @@ -2453,6 +2476,7 @@ 96E516E02000550C00A02306 /* MGLFeature_Private.h in Headers */, 353933F61D3FB785003F57D7 /* MGLBackgroundStyleLayer.h in Headers */, DABFB85D1CBE99E500D62B32 /* MGLAccountManager.h in Headers */, + 9221BAB020699F8A0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h in Headers */, 96E516F5200059B100A02306 /* MGLNetworkConfiguration.h in Headers */, 96E516F42000597D00A02306 /* NSData+MGLAdditions.h in Headers */, 96E516DD200054F200A02306 /* MGLPolygon_Private.h in Headers */, @@ -3008,6 +3032,7 @@ 40834C441FE05F7500C1BD0D /* reporting_utils.m in Sources */, 408AA8581DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */, DA35A2A11CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */, + 92FC0AEE207CEE16007B6B54 /* MGLShapeOfflineRegion.mm in Sources */, 35305D481D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */, 40834BF61FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */, DA8848291CBAFA6200AB86E3 /* MGLStyle.mm in Sources */, @@ -3135,6 +3160,7 @@ 40834C511FE05F7600C1BD0D /* reporting_utils.m in Sources */, 35305D491D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */, 357FE2E01E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */, + 92FC0AEF207CEE16007B6B54 /* MGLShapeOfflineRegion.mm in Sources */, DAA4E42D1CBB730400178DFB /* MGLAnnotationImage.m in Sources */, 40834C0A1FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */, 558DE7A31E5615E400C7916D /* MGLFoundation.mm in Sources */, @@ -3690,7 +3716,10 @@ baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(mbgl_filesource_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = test/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; @@ -3714,7 +3743,10 @@ baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; - HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; + HEADER_SEARCH_PATHS = ( + "$(mbgl_core_INCLUDE_DIRECTORIES)", + "$(mbgl_filesource_INCLUDE_DIRECTORIES)", + ); INFOPLIST_FILE = test/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; @@ -3846,7 +3878,7 @@ BITCODE_GENERATION_MODE = bitcode; HEADER_SEARCH_PATHS = ( "$(mbgl_core_INCLUDE_DIRECTORIES)", - "$(mbgl_filesource_LINK_LIBRARIES)", + "$(mbgl_filesource_INCLUDE_DIRECTORIES)", ); OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( @@ -3877,7 +3909,7 @@ BITCODE_GENERATION_MODE = bitcode; HEADER_SEARCH_PATHS = ( "$(mbgl_core_INCLUDE_DIRECTORIES)", - "$(mbgl_filesource_LINK_LIBRARIES)", + "$(mbgl_filesource_INCLUDE_DIRECTORIES)", ); LLVM_LTO = YES; OTHER_CFLAGS = "-fvisibility=hidden"; diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index f995cc06cd..84ca50ed48 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -15,11 +15,8 @@ umbrella_header: src/Mapbox.h framework_root: ../darwin/src custom_categories: - - name: Guides + - name: Appendices children: - - Adding Markers to a Map - - Runtime Styling - - Working with Mapbox Studio - Working with GeoJSON Data - Predicates and Expressions - Migrating to Expressions diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index ddc8be23f0..20bfeeef39 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -23,14 +23,17 @@ NS_ASSUME_NONNULL_BEGIN @protocol MGLFeature; @protocol MGLLocationManager; +/** Options for `MGLMapView.decelerationRate`. */ +typedef CGFloat MGLMapViewDecelerationRate NS_TYPED_EXTENSIBLE_ENUM; + /** The default deceleration rate for a map view. */ -FOUNDATION_EXTERN MGL_EXPORT const CGFloat MGLMapViewDecelerationRateNormal; +FOUNDATION_EXTERN MGL_EXPORT const MGLMapViewDecelerationRate MGLMapViewDecelerationRateNormal; /** A fast deceleration rate for a map view. */ -FOUNDATION_EXTERN MGL_EXPORT const CGFloat MGLMapViewDecelerationRateFast; +FOUNDATION_EXTERN MGL_EXPORT const MGLMapViewDecelerationRate MGLMapViewDecelerationRateFast; /** Disables deceleration in a map view. */ -FOUNDATION_EXTERN MGL_EXPORT const CGFloat MGLMapViewDecelerationRateImmediate; +FOUNDATION_EXTERN MGL_EXPORT const MGLMapViewDecelerationRate MGLMapViewDecelerationRateImmediate; /** The vertical alignment of an annotation within a map view. Used with @@ -94,6 +97,10 @@ FOUNDATION_EXTERN MGL_EXPORT const MGLMapViewPreferredFramesPerSecond MGLMapView /** The maximum supported frame rate; typically 60 FPS. */ FOUNDATION_EXTERN MGL_EXPORT const MGLMapViewPreferredFramesPerSecond MGLMapViewPreferredFramesPerSecondMaximum; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLMissingLocationServicesUsageDescriptionException; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLUserLocationAnnotationTypeException; +FOUNDATION_EXTERN MGL_EXPORT MGLExceptionName const MGLResourceNotFoundException; + /** An interactive, customizable map view with an interface similar to the one provided by Apple’s MapKit. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index f772432eb7..8ca41b328c 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -84,14 +84,18 @@ class MBGLView; class MGLAnnotationContext; -const CGFloat MGLMapViewDecelerationRateNormal = UIScrollViewDecelerationRateNormal; -const CGFloat MGLMapViewDecelerationRateFast = UIScrollViewDecelerationRateFast; -const CGFloat MGLMapViewDecelerationRateImmediate = 0.0; +const MGLMapViewDecelerationRate MGLMapViewDecelerationRateNormal = UIScrollViewDecelerationRateNormal; +const MGLMapViewDecelerationRate MGLMapViewDecelerationRateFast = UIScrollViewDecelerationRateFast; +const MGLMapViewDecelerationRate MGLMapViewDecelerationRateImmediate = 0.0; const MGLMapViewPreferredFramesPerSecond MGLMapViewPreferredFramesPerSecondDefault = -1; const MGLMapViewPreferredFramesPerSecond MGLMapViewPreferredFramesPerSecondLowPower = 30; const MGLMapViewPreferredFramesPerSecond MGLMapViewPreferredFramesPerSecondMaximum = 60; +const MGLExceptionName MGLMissingLocationServicesUsageDescriptionException = @"MGLMissingLocationServicesUsageDescriptionException"; +const MGLExceptionName MGLUserLocationAnnotationTypeException = @"MGLUserLocationAnnotationTypeException"; +const MGLExceptionName MGLResourceNotFoundException = @"MGLResourceNotFoundException"; + /// Indicates the manner in which the map view is tracking the user location. typedef NS_ENUM(NSUInteger, MGLUserTrackingState) { /// The map view is not yet tracking the user location. @@ -138,7 +142,7 @@ static NSString * const MGLInvisibleStyleMarkerSymbolName = @"invisible_marker"; /// Prefix that denotes a sprite installed by MGLMapView, to avoid collisions /// with style-defined sprites. -NSString *const MGLAnnotationSpritePrefix = @"com.mapbox.sprites."; +NSString * const MGLAnnotationSpritePrefix = @"com.mapbox.sprites."; /// Slop area around the hit testing point, allowing for imprecise annotation selection. const CGFloat MGLAnnotationImagePaddingForHitTest = 5; @@ -240,6 +244,12 @@ public: @property (nonatomic) MGLUserLocation *userLocation; @property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<MGLAnnotationView *> *> *annotationViewReuseQueueByIdentifier; +/// Experimental rendering performance measurement. +@property (nonatomic) BOOL experimental_enableFrameRateMeasurement; +@property (nonatomic) CGFloat averageFrameRate; +@property (nonatomic) CFTimeInterval frameTime; +@property (nonatomic) CFTimeInterval averageFrameTime; + @end @implementation MGLMapView @@ -296,6 +306,11 @@ public: BOOL _accessibilityValueAnnouncementIsPending; MGLReachability *_reachability; + + /// Experimental rendering performance measurement. + CFTimeInterval _frameCounterStartTime; + NSInteger _frameCount; + CFTimeInterval _frameDurations; } #pragma mark - Setup & Teardown - @@ -1104,6 +1119,27 @@ public: [self.glView display]; } + + if (self.experimental_enableFrameRateMeasurement) + { + CFTimeInterval now = CACurrentMediaTime(); + + self.frameTime = now - _displayLink.timestamp; + _frameDurations += self.frameTime; + + _frameCount++; + + CFTimeInterval elapsed = now - _frameCounterStartTime; + + if (elapsed >= 1.0) { + self.averageFrameRate = _frameCount / elapsed; + self.averageFrameTime = (_frameDurations / _frameCount) * 1000; + + _frameCount = 0; + _frameDurations = 0; + _frameCounterStartTime = now; + } + } } - (void)setNeedsGLDisplay @@ -4147,7 +4183,11 @@ public: { if ([annotation isKindOfClass:[MGLMultiPoint class]]) { - return false; + if ([self.delegate respondsToSelector:@selector(mapView:shapeAnnotationIsEnabled:)]) { + return !!(![self.delegate mapView:self shapeAnnotationIsEnabled:(MGLMultiPoint *)annotation]); + } else { + return false; + } } MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; @@ -4790,7 +4830,8 @@ public: NSString *suggestedUsageKeys = requiresWhenInUseUsageDescription ? @"NSLocationWhenInUseUsageDescription and (optionally) NSLocationAlwaysAndWhenInUseUsageDescription" : @"NSLocationWhenInUseUsageDescription and/or NSLocationAlwaysUsageDescription"; - [NSException raise:@"Missing Location Services usage description" format:@"This app must have a value for %@ in its Info.plist.", suggestedUsageKeys]; + [NSException raise:MGLMissingLocationServicesUsageDescriptionException + format:@"This app must have a value for %@ in its Info.plist.", suggestedUsageKeys]; } } @@ -4827,7 +4868,7 @@ public: userLocationAnnotationView = (MGLUserLocationAnnotationView *)[self.delegate mapView:self viewForAnnotation:self.userLocation]; if (userLocationAnnotationView && ! [userLocationAnnotationView isKindOfClass:MGLUserLocationAnnotationView.class]) { - [NSException raise:@"MGLUserLocationAnnotationTypeException" + [NSException raise:MGLUserLocationAnnotationTypeException format:@"User location annotation view must be a kind of MGLUserLocationAnnotationView. %@", userLocationAnnotationView.debugDescription]; } } @@ -6007,7 +6048,7 @@ public: if ( ! image) { - [NSException raise:@"MGLResourceNotFoundException" format: + [NSException raise:MGLResourceNotFoundException format: @"The resource named “%@” could not be found in the Mapbox framework bundle.", imageName]; } @@ -6296,8 +6337,8 @@ private: NSURL *url = URLString.length ? [NSURL URLWithString:URLString] : nil; if (URLString.length && !url) { - [NSException raise:@"Invalid style URL" format: - @"“%@” is not a valid style URL.", URLString]; + [NSException raise:MGLInvalidStyleURLException + format:@"“%@” is not a valid style URL.", URLString]; } self.styleURL = url; } diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h index 201e3db84b..4bd1a95c9b 100644 --- a/platform/ios/src/MGLMapViewDelegate.h +++ b/platform/ios/src/MGLMapViewDelegate.h @@ -433,6 +433,18 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark Selecting Annotations /** + Returns a Boolean value indicating whether the shape annotation can be selected. + + If the return value is `YES`, the user can select the annotation by tapping + on it. If the delegate does not implement this method, the default value is `YES`. + + @param mapView The map view that has selected the annotation. + @param annotation The object representing the shape annotation. + @return A Boolean value indicating whether the annotation can be selected. + */ +- (BOOL)mapView:(MGLMapView *)mapView shapeAnnotationIsEnabled:(MGLShape *)annotation; + +/** Tells the delegate that one of its annotations was selected. You can use this method to track changes in the selection state of annotations. diff --git a/platform/ios/src/MGLMapView_Experimental.h b/platform/ios/src/MGLMapView_Experimental.h new file mode 100644 index 0000000000..94f8d67fb0 --- /dev/null +++ b/platform/ios/src/MGLMapView_Experimental.h @@ -0,0 +1,32 @@ +#import <Mapbox/Mapbox.h> + +@interface MGLMapView (Experimental) + +#pragma mark Rendering Performance Measurement + +/** Enable rendering performance measurement. */ +@property (nonatomic) BOOL experimental_enableFrameRateMeasurement; + +/** + Average frames per second over the previous second, updated once per second. + + Requires `experimental_enableFrameRateMeasurement`. + */ +@property (nonatomic, readonly) CGFloat averageFrameRate; + +/** + Frame render duration for the previous frame, updated instantaneously. + + Requires `experimental_enableFrameRateMeasurement`. + */ +@property (nonatomic, readonly) CFTimeInterval frameTime; + +/** + Average frame render duration over the previous second, updated once per + second. + + Requires `experimental_enableFrameRateMeasurement`. + */ +@property (nonatomic, readonly) CFTimeInterval averageFrameTime; + +@end diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index a0afe2d9cc..2af80b455d 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -57,6 +57,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLRasterTileSource.h" #import "MGLRasterDEMSource.h" #import "MGLImageSource.h" +#import "MGLShapeOfflineRegion.h" #import "MGLTilePyramidOfflineRegion.h" #import "MGLTypes.h" #import "MGLUserLocation.h" diff --git a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift index 4d11b000b9..48673b1d14 100644 --- a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift +++ b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift @@ -58,6 +58,8 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate { func mapView(_ mapView: MGLMapView, tapOnCalloutFor annotation: MGLAnnotation) {} func mapViewDidFinishRenderingFrame(_ mapView: MGLMapView, fullyRendered: Bool) {} + + func mapView(_ mapView: MGLMapView, shapeAnnotationIsEnabled annotation: MGLShape) -> Bool { return false } func mapView(_ mapView: MGLMapView, didAdd annotationViews: [MGLAnnotationView]) {} diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index c1eb4bfe12..b55cedcacb 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -81,6 +81,7 @@ macro(mbgl_platform_core) target_add_mason_package(mbgl-core PUBLIC libjpeg-turbo) target_add_mason_package(mbgl-core PUBLIC webp) target_add_mason_package(mbgl-core PRIVATE icu) + target_add_mason_package(mbgl-core PUBLIC geojson) target_link_libraries(mbgl-core PRIVATE nunicode diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 001b3792d5..d8a0067ad6 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -2,10 +2,26 @@ # master +* When a symbol in an `MGLSymbolStyleLayer` has both an icon and text, both are shown or hidden together based on available space. ([#12521](https://github.com/mapbox/mapbox-gl-native/pull/12521)) +* The `-[MGLMapView annotationAtPoint:]` method can now return annotations near tile boundaries at high zoom levels. ([#12570](https://github.com/mapbox/mapbox-gl-native/pull/12570)) +* Fixed inconsistencies in exception naming. ([#12583](https://github.com/mapbox/mapbox-gl-native/issues/12583)) +* Added `MGLShapeOfflineRegion` for defining arbitrarily shaped offline regions [#11447](https://github.com/mapbox/mapbox-gl-native/pull/11447) +* Added an `-[MGLMapViewDelegate mapView:shapeAnnotationIsEnabled:]` method to specify whether an annotation is selectable. ([#12352](https://github.com/mapbox/mapbox-gl-native/pull/12352)) + +# 0.10.0 - August 15, 2018 + ## Styles and rendering * Token string syntax (`"{token}"`) in `MGLSymbolStyleLayer` `text` and `iconImageName` properties is now correctly converted to the appropriate `NSExpression` equivalent. ([#11659](https://github.com/mapbox/mapbox-gl-native/issues/11659)) * Fixed a crash when switching between two styles having layers with the same identifier but different layer types. ([#12432](https://github.com/mapbox/mapbox-gl-native/issues/12432)) +* Added a new option to `MGLSymbolPlacement`, `MGLSymbolPlacementLineCenter`, that places the label relative to the center of the geometry. ([#12337](https://github.com/mapbox/mapbox-gl-native/pull/12337)) +* Added a `MGLShapeSourceOptionLineDistanceMetrics` property that enables or disables calculating line distance metrics. + +## Other changes + +* Fixed an issue where the symbols for `MGLMapPointForCoordinate` could not be found. ([#12445](https://github.com/mapbox/mapbox-gl-native/issues/12445)) +* Fixed an issue causing country and ocean labels to disappear after calling `-[MGLStyle localizeLabelsIntoLocale:]` when the system language is set to Simplified Chinese. ([#12164](https://github.com/mapbox/mapbox-gl-native/issues/12164)) +* Closed a security vulnerability introduced in 0.8.0 that would potentially allow the owner of a style to compromise apps loading that style. ([#12571](https://github.com/mapbox/mapbox-gl-native/pull/12571)) # 0.9.0 - July 18, 2018 diff --git a/platform/macos/Mapbox-macOS-SDK-symbols.podspec b/platform/macos/Mapbox-macOS-SDK-symbols.podspec index 58f4bad09b..7ab9aa4de2 100644 --- a/platform/macos/Mapbox-macOS-SDK-symbols.podspec +++ b/platform/macos/Mapbox-macOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '0.8.0' + version = '0.10.0' m.name = 'Mapbox-macOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/macos/Mapbox-macOS-SDK.podspec b/platform/macos/Mapbox-macOS-SDK.podspec index 6af7cb001f..756f3828b6 100644 --- a/platform/macos/Mapbox-macOS-SDK.podspec +++ b/platform/macos/Mapbox-macOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '0.8.0' + version = '0.10.0' m.name = 'Mapbox-macOS-SDK' m.version = version diff --git a/platform/macos/app/AppDelegate.h b/platform/macos/app/AppDelegate.h index a1d9297b2f..87b7514292 100644 --- a/platform/macos/app/AppDelegate.h +++ b/platform/macos/app/AppDelegate.h @@ -21,4 +21,6 @@ extern NSString * const MGLMapboxAccessTokenDefaultsKey; @property (copy) NSURL *pendingStyleURL; @property (assign) MGLMapDebugMaskOptions pendingDebugMask; +- (void)watchOfflinePack:(MGLOfflinePack *)pack; + @end diff --git a/platform/macos/app/AppDelegate.m b/platform/macos/app/AppDelegate.m index f7b35d0c83..eda989d7f9 100644 --- a/platform/macos/app/AppDelegate.m +++ b/platform/macos/app/AppDelegate.m @@ -135,6 +135,8 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; } - (void)applicationWillTerminate:(NSNotification *)notification { + [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:nil]; + if (![[NSUserDefaults standardUserDefaults] boolForKey:@"NSQuitAlwaysKeepsWindows"]) { NSDocument *currentDocument = [NSDocumentController sharedDocumentController].currentDocument; if ([currentDocument isKindOfClass:[MapDocument class]]) { @@ -204,6 +206,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; - (IBAction)delete:(id)sender { for (MGLOfflinePack *pack in self.offlinePacksArrayController.selectedObjects) { + [self unwatchOfflinePack:pack]; [[MGLOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) { if (error) { [[NSAlert alertWithError:error] runModal]; @@ -228,11 +231,13 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; } case MGLOfflinePackStateInactive: + [self watchOfflinePack:pack]; [pack resume]; break; case MGLOfflinePackStateActive: [pack suspend]; + [self unwatchOfflinePack:pack]; break; default: @@ -241,6 +246,27 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; } } +- (void)watchOfflinePack:(MGLOfflinePack *)pack { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(offlinePackDidChangeProgress:) name:MGLOfflinePackProgressChangedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(offlinePackDidReceiveError:) name:MGLOfflinePackErrorNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(offlinePackDidReceiveError:) name:MGLOfflinePackMaximumMapboxTilesReachedNotification object:nil]; +} + +- (void)unwatchOfflinePack:(MGLOfflinePack *)pack { + [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:pack]; +} + +- (void)offlinePackDidChangeProgress:(NSNotification *)notification { + MGLOfflinePack *pack = notification.object; + if (pack.state == MGLOfflinePackStateComplete) { + [[NSSound soundNamed:@"Glass"] play]; + } +} + +- (void)offlinePackDidReceiveError:(NSNotification *)notification { + [[NSSound soundNamed:@"Basso"] play]; +} + #pragma mark Help methods - (IBAction)showShortcuts:(id)sender { diff --git a/platform/macos/app/Base.lproj/MapDocument.xib b/platform/macos/app/Base.lproj/MapDocument.xib index 5d0525a29d..72fa024fcc 100644 --- a/platform/macos/app/Base.lproj/MapDocument.xib +++ b/platform/macos/app/Base.lproj/MapDocument.xib @@ -1,15 +1,21 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14306.4" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <dependencies> <deployment identifier="macosx"/> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14306.4"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="MapDocument"> <connections> + <outlet property="addOfflinePackWindow" destination="NmZ-Tf-v2O" id="ZPE-9L-vPJ"/> <outlet property="mapView" destination="q4d-kF-8Hi" id="7hI-dS-A5R"/> <outlet property="mapViewContextMenu" destination="XbX-6a-Mgy" id="YD0-1r-5N2"/> + <outlet property="maximumOfflinePackZoomLevelField" destination="5sj-XD-neD" id="Edu-lU-3j9"/> + <outlet property="maximumOfflinePackZoomLevelFormatter" destination="4cD-xh-teT" id="IOh-5e-4FO"/> + <outlet property="minimumOfflinePackZoomLevelField" destination="Xo1-tZ-WQ6" id="ETh-er-ReB"/> + <outlet property="minimumOfflinePackZoomLevelFormatter" destination="LGN-Et-RKY" id="Klk-iF-fM0"/> + <outlet property="offlinePackNameField" destination="tUU-DX-RxU" id="r7P-uL-4qo"/> <outlet property="splitView" destination="IPR-fm-vk8" id="9xt-ar-uad"/> <outlet property="styleLayersArrayController" destination="GXW-3J-Gff" id="Ygs-7o-juz"/> <outlet property="styleLayersTableView" destination="Mm4-6F-qEb" id="TB5-ha-JJE"/> @@ -57,7 +63,7 @@ <splitView autosaveName="MBXLayersSplitView" dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="IPR-fm-vk8"> <rect key="frame" x="0.0" y="0.0" width="642" height="480"/> <subviews> - <scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="sMc-vT-RwH"> + <scrollView misplaced="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="sMc-vT-RwH"> <rect key="frame" x="0.0" y="0.0" width="163" height="480"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <clipView key="contentView" id="bSc-hK-bzQ"> @@ -71,7 +77,7 @@ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/> <tableColumns> - <tableColumn identifier="" editable="NO" width="16" minWidth="16" maxWidth="1000" id="P3U-a3-c8q"> + <tableColumn editable="NO" width="16" minWidth="16" maxWidth="1000" id="P3U-a3-c8q"> <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border"> <font key="font" metaFont="smallSystem"/> <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/> @@ -88,7 +94,7 @@ </binding> </connections> </tableColumn> - <tableColumn identifier="" editable="NO" width="141" minWidth="40" maxWidth="1000" id="BwD-ww-7uw"> + <tableColumn editable="NO" width="141" minWidth="40" maxWidth="1000" id="BwD-ww-7uw"> <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border"> <font key="font" metaFont="smallSystem"/> <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/> @@ -133,7 +139,7 @@ <autoresizingMask key="autoresizingMask"/> </scroller> </scrollView> - <customView id="q4d-kF-8Hi" customClass="MGLMapView"> + <customView misplaced="YES" id="q4d-kF-8Hi" customClass="MGLMapView"> <rect key="frame" x="164" y="0.0" width="478" height="480"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <constraints> @@ -301,8 +307,175 @@ <outlet property="delegate" destination="-2" id="yvb-NB-VGl"/> </connections> </menu> + <window title="Add Offline Pack" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="NmZ-Tf-v2O"> + <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> + <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> + <rect key="contentRect" x="109" y="131" width="392" height="192"/> + <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/> + <view key="contentView" id="Aqq-bl-feU"> + <rect key="frame" x="0.0" y="0.0" width="392" height="192"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Xjw-df-oAz"> + <rect key="frame" x="98" y="155" width="276" height="17"/> + <textFieldCell key="cell" lineBreakMode="clipping" title="Add offline pack" id="Iec-RB-iqn"> + <font key="font" metaFont="systemBold"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sui-dp-hbb"> + <rect key="frame" x="98" y="78" width="44" height="17"/> + <textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Name:" id="2El-Zw-T6E"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="D3l-hX-2Dh"> + <rect key="frame" x="98" y="105" width="276" height="42"/> + <textFieldCell key="cell" selectable="YES" title="Mapbox GL will download all the resources needed for viewing the currently visible coordinate bounds in the current style." id="3Gw-Zy-sBT"> + <font key="font" metaFont="smallSystem"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="tUU-DX-RxU"> + <rect key="frame" x="148" y="75" width="224" height="22"/> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="lwQ-N1-PTI"> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <connections> + <outlet property="nextKeyView" destination="Xo1-tZ-WQ6" id="VIu-TG-LQu"/> + </connections> + </textField> + <imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="p5T-3H-wqL"> + <rect key="frame" x="20" y="108" width="64" height="64"/> + <constraints> + <constraint firstAttribute="height" constant="64" id="2Xy-pC-rE3"/> + <constraint firstAttribute="width" constant="64" id="SWY-Rg-9R8"/> + </constraints> + <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSApplicationIcon" id="cww-fO-sNX"/> + </imageView> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hhq-J9-mzB"> + <rect key="frame" x="98" y="52" width="44" height="17"/> + <textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="From:" id="fkO-YX-Yzt"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Xo1-tZ-WQ6"> + <rect key="frame" x="148" y="49" width="96" height="22"/> + <constraints> + <constraint firstAttribute="width" constant="96" id="8Rw-FU-HGV"/> + </constraints> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="g7z-bq-I00"> + <numberFormatter key="formatter" formatterBehavior="custom10_4" positiveFormat="z#,##0" numberStyle="decimal" allowsFloats="NO" minimumIntegerDigits="1" maximumIntegerDigits="2000000000" id="LGN-Et-RKY"> + <real key="minimum" value="0.0"/> + </numberFormatter> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <connections> + <outlet property="nextKeyView" destination="5sj-XD-neD" id="90r-pR-v8j"/> + </connections> + </textField> + <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8r0-F0-e5i"> + <rect key="frame" x="250" y="52" width="20" height="17"/> + <textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="to:" id="RBS-Uj-AVT"> + <font key="font" metaFont="system"/> + <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + </textField> + <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5sj-XD-neD"> + <rect key="frame" x="276" y="49" width="96" height="22"/> + <constraints> + <constraint firstAttribute="width" constant="96" id="faj-tE-bfc"/> + </constraints> + <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="dUq-Vg-vgQ"> + <numberFormatter key="formatter" formatterBehavior="custom10_4" positiveFormat="z#,##0" numberStyle="decimal" allowsFloats="NO" minimumIntegerDigits="1" maximumIntegerDigits="2000000000" id="4cD-xh-teT"> + <real key="minimum" value="0.0"/> + </numberFormatter> + <font key="font" metaFont="system"/> + <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> + <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/> + </textFieldCell> + <connections> + <outlet property="nextKeyView" destination="tUU-DX-RxU" id="o5A-2J-9Sl"/> + </connections> + </textField> + <button verticalHuggingPriority="750" tag="1" translatesAutoresizingMaskIntoConstraints="NO" id="EyW-0r-iV7"> + <rect key="frame" x="313" y="13" width="65" height="32"/> + <buttonCell key="cell" type="push" title="Add" bezelStyle="rounded" alignment="center" borderStyle="border" tag="1" imageScaling="proportionallyDown" inset="2" id="5xR-IX-Qnp"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="system"/> + <string key="keyEquivalent" base64-UTF8="YES"> +DQ +</string> + </buttonCell> + <connections> + <action selector="confirmAddingOfflinePack:" target="-2" id="7ic-hp-A2O"/> + </connections> + </button> + <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ggP-hg-92n"> + <rect key="frame" x="231" y="13" width="82" height="32"/> + <buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="L4F-7l-Wwq"> + <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> + <font key="font" metaFont="system"/> + <string key="keyEquivalent" base64-UTF8="YES"> +Gw +</string> + </buttonCell> + <connections> + <action selector="confirmAddingOfflinePack:" target="-2" id="Ljo-bK-4BU"/> + </connections> + </button> + </subviews> + <constraints> + <constraint firstItem="hhq-J9-mzB" firstAttribute="firstBaseline" secondItem="Xo1-tZ-WQ6" secondAttribute="firstBaseline" id="0Rv-Ze-u9B"/> + <constraint firstAttribute="trailing" secondItem="Xjw-df-oAz" secondAttribute="trailing" constant="20" id="0gu-PF-adU"/> + <constraint firstItem="p5T-3H-wqL" firstAttribute="top" secondItem="Aqq-bl-feU" secondAttribute="top" constant="20" id="2bA-hD-M6H"/> + <constraint firstItem="ggP-hg-92n" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Aqq-bl-feU" secondAttribute="leading" constant="20" symbolic="YES" id="493-sg-uf0"/> + <constraint firstItem="Xo1-tZ-WQ6" firstAttribute="top" secondItem="tUU-DX-RxU" secondAttribute="bottom" constant="4" id="4Ql-yp-7Kv"/> + <constraint firstAttribute="bottom" secondItem="EyW-0r-iV7" secondAttribute="bottom" constant="20" id="7cm-ds-QeW"/> + <constraint firstItem="D3l-hX-2Dh" firstAttribute="leading" secondItem="p5T-3H-wqL" secondAttribute="trailing" constant="16" id="7xT-tt-0Wx"/> + <constraint firstItem="EyW-0r-iV7" firstAttribute="leading" secondItem="ggP-hg-92n" secondAttribute="trailing" constant="12" id="9AJ-g9-XXg"/> + <constraint firstItem="D3l-hX-2Dh" firstAttribute="top" secondItem="Xjw-df-oAz" secondAttribute="bottom" constant="8" id="BwZ-hX-4t5"/> + <constraint firstItem="sui-dp-hbb" firstAttribute="firstBaseline" secondItem="tUU-DX-RxU" secondAttribute="firstBaseline" id="MUq-FE-BiZ"/> + <constraint firstItem="sui-dp-hbb" firstAttribute="leading" secondItem="D3l-hX-2Dh" secondAttribute="leading" id="NU2-ve-0Mo"/> + <constraint firstItem="tUU-DX-RxU" firstAttribute="top" secondItem="D3l-hX-2Dh" secondAttribute="bottom" constant="8" id="Rn0-66-xYg"/> + <constraint firstItem="8r0-F0-e5i" firstAttribute="firstBaseline" secondItem="5sj-XD-neD" secondAttribute="firstBaseline" id="Shc-KJ-NR4"/> + <constraint firstItem="Xo1-tZ-WQ6" firstAttribute="leading" secondItem="tUU-DX-RxU" secondAttribute="leading" id="Usc-k5-8R9"/> + <constraint firstItem="8r0-F0-e5i" firstAttribute="leading" secondItem="Xo1-tZ-WQ6" secondAttribute="trailing" constant="8" id="W7y-oQ-Tsb"/> + <constraint firstItem="Xo1-tZ-WQ6" firstAttribute="leading" secondItem="hhq-J9-mzB" secondAttribute="trailing" constant="8" id="g3w-AE-VVQ"/> + <constraint firstItem="5sj-XD-neD" firstAttribute="trailing" secondItem="tUU-DX-RxU" secondAttribute="trailing" id="gWo-ar-g0U"/> + <constraint firstItem="Xjw-df-oAz" firstAttribute="leading" secondItem="p5T-3H-wqL" secondAttribute="trailing" constant="16" id="jRa-3D-tbZ"/> + <constraint firstItem="Xjw-df-oAz" firstAttribute="top" secondItem="Aqq-bl-feU" secondAttribute="top" constant="20" id="nrG-fO-QE5"/> + <constraint firstItem="D3l-hX-2Dh" firstAttribute="trailing" secondItem="Xjw-df-oAz" secondAttribute="trailing" id="o5U-kq-Wbz"/> + <constraint firstItem="tUU-DX-RxU" firstAttribute="leading" secondItem="sui-dp-hbb" secondAttribute="trailing" constant="8" id="oVs-7V-Dxq"/> + <constraint firstItem="tUU-DX-RxU" firstAttribute="trailing" secondItem="D3l-hX-2Dh" secondAttribute="trailing" id="p3o-y4-fNP"/> + <constraint firstItem="hhq-J9-mzB" firstAttribute="leading" secondItem="sui-dp-hbb" secondAttribute="leading" id="sff-ty-Y6C"/> + <constraint firstItem="5sj-XD-neD" firstAttribute="leading" secondItem="8r0-F0-e5i" secondAttribute="trailing" constant="8" id="tf8-pK-DZR"/> + <constraint firstItem="Xo1-tZ-WQ6" firstAttribute="firstBaseline" secondItem="8r0-F0-e5i" secondAttribute="firstBaseline" id="vdt-kP-ISY"/> + <constraint firstItem="EyW-0r-iV7" firstAttribute="trailing" secondItem="5sj-XD-neD" secondAttribute="trailing" id="wWy-ra-Uqt"/> + <constraint firstItem="p5T-3H-wqL" firstAttribute="leading" secondItem="Aqq-bl-feU" secondAttribute="leading" constant="20" id="whj-f7-Iu6"/> + <constraint firstItem="ggP-hg-92n" firstAttribute="firstBaseline" secondItem="EyW-0r-iV7" secondAttribute="firstBaseline" id="yl0-vC-sO8"/> + <constraint firstItem="EyW-0r-iV7" firstAttribute="top" secondItem="5sj-XD-neD" secondAttribute="bottom" constant="8" id="zxS-ci-Can"/> + </constraints> + </view> + <connections> + <outlet property="initialFirstResponder" destination="tUU-DX-RxU" id="W2D-pZ-p0s"/> + </connections> + <point key="canvasLocation" x="79" y="867"/> + </window> </objects> <resources> + <image name="NSApplicationIcon" width="128" height="128"/> <image name="NSListViewTemplate" width="14" height="10"/> <image name="NSShareTemplate" width="11" height="16"/> <image name="symbol" width="13" height="13"/> diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 9ba28f3b37..447929201c 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -75,6 +75,12 @@ NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *sha @property (weak) IBOutlet NSTableView *styleLayersTableView; @property (weak) IBOutlet NSMenu *mapViewContextMenu; @property (weak) IBOutlet NSSplitView *splitView; +@property (weak) IBOutlet NSWindow *addOfflinePackWindow; +@property (weak) IBOutlet NSTextField *offlinePackNameField; +@property (weak) IBOutlet NSTextField *minimumOfflinePackZoomLevelField; +@property (weak) IBOutlet NSNumberFormatter *minimumOfflinePackZoomLevelFormatter; +@property (weak) IBOutlet NSTextField *maximumOfflinePackZoomLevelField; +@property (weak) IBOutlet NSNumberFormatter *maximumOfflinePackZoomLevelFormatter; @end @@ -915,33 +921,49 @@ NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *sha #pragma mark Offline packs - (IBAction)addOfflinePack:(id)sender { - NSAlert *namePrompt = [[NSAlert alloc] init]; - namePrompt.messageText = @"Add offline pack"; - namePrompt.informativeText = @"Choose a name for the pack:"; - NSTextField *nameTextField = [[NSTextField alloc] initWithFrame:NSZeroRect]; - nameTextField.placeholderString = MGLStringFromCoordinateBounds(self.mapView.visibleCoordinateBounds); - [nameTextField sizeToFit]; - NSRect textFieldFrame = nameTextField.frame; - textFieldFrame.size.width = 300; - nameTextField.frame = textFieldFrame; - namePrompt.accessoryView = nameTextField; - [namePrompt addButtonWithTitle:@"Add"]; - [namePrompt addButtonWithTitle:@"Cancel"]; - if ([namePrompt runModal] != NSAlertFirstButtonReturn) { - return; - } - - id <MGLOfflineRegion> region = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:self.mapView.styleURL bounds:self.mapView.visibleCoordinateBounds fromZoomLevel:self.mapView.zoomLevel toZoomLevel:self.mapView.maximumZoomLevel]; - NSData *context = [[NSValueTransformer valueTransformerForName:@"OfflinePackNameValueTransformer"] reverseTransformedValue:nameTextField.stringValue]; - [[MGLOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack * _Nullable pack, NSError * _Nullable error) { - if (error) { - [[NSAlert alertWithError:error] runModal]; - } else { - [pack resume]; + self.offlinePackNameField.stringValue = @""; + self.offlinePackNameField.placeholderString = MGLStringFromCoordinateBounds(self.mapView.visibleCoordinateBounds); + self.minimumOfflinePackZoomLevelField.doubleValue = floor(self.mapView.zoomLevel); + self.maximumOfflinePackZoomLevelField.doubleValue = ceil(self.mapView.maximumZoomLevel); + self.minimumOfflinePackZoomLevelFormatter.minimum = @(floor(self.mapView.minimumZoomLevel)); + self.maximumOfflinePackZoomLevelFormatter.minimum = @(floor(self.mapView.minimumZoomLevel)); + self.minimumOfflinePackZoomLevelFormatter.maximum = @(ceil(self.mapView.maximumZoomLevel)); + self.maximumOfflinePackZoomLevelFormatter.maximum = @(ceil(self.mapView.maximumZoomLevel)); + + [self.addOfflinePackWindow makeFirstResponder:self.offlinePackNameField]; + + __weak __typeof__(self) weakSelf = self; + [self.window beginSheet:self.addOfflinePackWindow completionHandler:^(NSModalResponse returnCode) { + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf || returnCode != NSModalResponseOK) { + return; + } + + id <MGLOfflineRegion> region = + [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:strongSelf.mapView.styleURL + bounds:strongSelf.mapView.visibleCoordinateBounds + fromZoomLevel:strongSelf.minimumOfflinePackZoomLevelField.integerValue + toZoomLevel:strongSelf.maximumOfflinePackZoomLevelField.integerValue]; + NSString *name = strongSelf.offlinePackNameField.stringValue; + if (!name.length) { + name = strongSelf.offlinePackNameField.placeholderString; } + NSData *context = [[NSValueTransformer valueTransformerForName:@"OfflinePackNameValueTransformer"] reverseTransformedValue:name]; + [[MGLOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack * _Nullable pack, NSError * _Nullable error) { + if (error) { + [[NSAlert alertWithError:error] runModal]; + } else { + [(AppDelegate *)NSApp.delegate watchOfflinePack:pack]; + [pack resume]; + } + }]; }]; } +- (IBAction)confirmAddingOfflinePack:(id)sender { + [self.window endSheet:self.addOfflinePackWindow returnCode:[sender tag] ? NSModalResponseOK : NSModalResponseCancel]; +} + #pragma mark Mouse events - (void)handlePressGesture:(NSPressGestureRecognizer *)gestureRecognizer { diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 40d1edef22..fd110b5bf7 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -158,6 +158,7 @@ In style JSON | In the SDK `cluster` | `MGLShapeSourceOptionClustered` `clusterRadius` | `MGLShapeSourceOptionClusterRadius` `clusterMaxZoom` | `MGLShapeSourceOptionMaximumZoomLevelForClustering` +`lineMetrics` | `MGLShapeSourceOptionLineDistanceMetrics` To create a shape source from local GeoJSON data, first [convert the GeoJSON data into a shape](working-with-geojson-data.html#converting-geojson-data-into-shape-objects), diff --git a/platform/macos/docs/guides/Migrating to Expressions.md b/platform/macos/docs/guides/Migrating to Expressions.md index e8d038dbb0..44e14a6eec 100644 --- a/platform/macos/docs/guides/Migrating to Expressions.md +++ b/platform/macos/docs/guides/Migrating to Expressions.md @@ -5,7 +5,7 @@ # Migrating from Style Functions to Expressions -[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source. +[Runtime Styling](https://www.mapbox.com/ios-sdk/maps/overview/runtime-styling/) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source. With Mapbox Maps SDK for macOS v0.7.0, style functions have been replaced with expressions. These provide even more tools for developers who want to style their maps dynamically. This guide outlines some tips for migrating from style functions to expressions, and offers an overview of some things that developers can do with expressions. @@ -113,7 +113,7 @@ mapView.style?.insertLayer(layer, below: symbolLayer) If you previously used an interpolation base greater than `0` (other than `1`), you can use `MGLExpressionInterpolationMode.exponential` as the curve type for `+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` or `'exponential'` as the curve type for [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code). The `parameters` argument takes that interpolation base. This interpolates between values exponentially, creating an accelerated ramp effect. -Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key. +Here’s a visualization from Mapbox Studio (see [Mapbox Studio and iOS](https://www.mapbox.com/ios-sdk/maps/overview/mapbox-studio/)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key. <img src="img/data-driven-styling/exponential-function.png" height=344/> <img src="img/data-driven-styling/exponential-function-1.png" height=344/> diff --git a/platform/macos/docs/pod-README.md b/platform/macos/docs/pod-README.md index 4827124be0..5dbef70efb 100644 --- a/platform/macos/docs/pod-README.md +++ b/platform/macos/docs/pod-README.md @@ -40,7 +40,7 @@ Create a [Podfile](https://guides.cocoapods.org/syntax/podfile.html) with the fo platform :osx, '10.11' target 'TargetNameForYourApp' do - pod 'Mapbox-iOS-SDK', '~> x.y' + pod 'Mapbox-macOS-SDK', '~> x.y' end ``` diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index 915c1f46a9..5f1a214541 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -15,7 +15,7 @@ umbrella_header: src/Mapbox.h framework_root: ../darwin/src custom_categories: - - name: Guides + - name: Appendices children: - Working with GeoJSON Data - Predicates and Expressions diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index ffb7b524c6..1785caddb9 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -93,7 +93,11 @@ 92092EF01F5EB10E00AF5130 /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 92092EF11F5EB10E00AF5130 /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */; }; 920A3E591E6F859D00C16EFC /* MGLSourceQueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */; }; + 9221BAAF20699CBB0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9221BAAE20699CBA0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h */; }; + 9250B8C32073C69100EF338C /* MGLShapeOfflineRegion.h in Headers */ = {isa = PBXBuildFile; fileRef = 9250B8C22073C69000EF338C /* MGLShapeOfflineRegion.h */; settings = {ATTRIBUTES = (Public, ); }; }; 92F2C3EB1F0E3A1900268EC0 /* MGLRendererFrontend.h in Headers */ = {isa = PBXBuildFile; fileRef = 92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */; }; + 92FC0AE4207CC8DA007B6B54 /* MGLShapeOfflineRegion_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 92FC0AE3207CC8DA007B6B54 /* MGLShapeOfflineRegion_Private.h */; }; + 92FC0AE6207CDD8D007B6B54 /* MGLShapeOfflineRegion.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92FC0AE5207CDD8D007B6B54 /* MGLShapeOfflineRegion.mm */; }; 9654C12B1FFC38E000DB6A19 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C12A1FFC38E000DB6A19 /* MGLPolyline_Private.h */; }; 9654C12D1FFC394700DB6A19 /* MGLPolygon_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C12C1FFC394700DB6A19 /* MGLPolygon_Private.h */; }; 96E027311E57C9A7004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027331E57C9A7004B8E66 /* Localizable.strings */; }; @@ -383,7 +387,11 @@ 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter.h; sourceTree = "<group>"; }; 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapSnapshotter.mm; sourceTree = "<group>"; }; 920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLSourceQueryTests.m; sourceTree = "<group>"; }; + 9221BAAE20699CBA0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTilePyramidOfflineRegion_Private.h; sourceTree = "<group>"; }; + 9250B8C22073C69000EF338C /* MGLShapeOfflineRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeOfflineRegion.h; sourceTree = "<group>"; }; 92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererFrontend.h; sourceTree = "<group>"; }; + 92FC0AE3207CC8DA007B6B54 /* MGLShapeOfflineRegion_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeOfflineRegion_Private.h; sourceTree = "<group>"; }; + 92FC0AE5207CDD8D007B6B54 /* MGLShapeOfflineRegion.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeOfflineRegion.mm; sourceTree = "<group>"; }; 9654C12A1FFC38E000DB6A19 /* MGLPolyline_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolyline_Private.h; sourceTree = "<group>"; }; 9654C12C1FFC394700DB6A19 /* MGLPolygon_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPolygon_Private.h; sourceTree = "<group>"; }; 966091701E5BBFF700A9A03B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; }; @@ -1007,7 +1015,11 @@ DAE6C3511CC31E0400DB3429 /* MGLOfflineStorage.h */, DAE6C3741CC31E2A00DB3429 /* MGLOfflineStorage_Private.h */, DAE6C3751CC31E2A00DB3429 /* MGLOfflineStorage.mm */, + 9250B8C22073C69000EF338C /* MGLShapeOfflineRegion.h */, + 92FC0AE3207CC8DA007B6B54 /* MGLShapeOfflineRegion_Private.h */, + 92FC0AE5207CDD8D007B6B54 /* MGLShapeOfflineRegion.mm */, DAE6C3581CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h */, + 9221BAAE20699CBA0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h */, DAE6C37B1CC31E2A00DB3429 /* MGLTilePyramidOfflineRegion.mm */, ); name = "Offline Maps"; @@ -1214,6 +1226,7 @@ 352742781D4C220900A1ECE6 /* MGLStyleValue.h in Headers */, DAE6C35E1CC31E0400DB3429 /* MGLMultiPoint.h in Headers */, 35602BFF1D3EA9B40050646F /* MGLStyleLayer_Private.h in Headers */, + 92FC0AE4207CC8DA007B6B54 /* MGLShapeOfflineRegion_Private.h in Headers */, DAF0D8161DFE6B1800B28378 /* MGLAttributionInfo_Private.h in Headers */, DAE6C3971CC31E2A00DB3429 /* NSBundle+MGLAdditions.h in Headers */, DAED385F1D62CED700D7640F /* NSURL+MGLAdditions.h in Headers */, @@ -1246,6 +1259,7 @@ 35602BFA1D3EA99F0050646F /* MGLFillStyleLayer.h in Headers */, DA35A2A41CC9EB1A00E826B2 /* MGLCoordinateFormatter.h in Headers */, 35C5D8491D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h in Headers */, + 9250B8C32073C69100EF338C /* MGLShapeOfflineRegion.h in Headers */, DD0902B31DB1AC6400C5BDCE /* MGLNetworkConfiguration.h in Headers */, DAE6C3621CC31E0400DB3429 /* MGLOverlay.h in Headers */, DAE6C3651CC31E0400DB3429 /* MGLPolyline.h in Headers */, @@ -1287,6 +1301,7 @@ 352742851D4C244700A1ECE6 /* MGLRasterTileSource.h in Headers */, 9654C12D1FFC394700DB6A19 /* MGLPolygon_Private.h in Headers */, 408AA85B1DAEECFE00022900 /* MGLShape_Private.h in Headers */, + 9221BAAF20699CBB0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h in Headers */, DACC22181CF3D4F700D220D9 /* MGLFeature_Private.h in Headers */, 9654C12B1FFC38E000DB6A19 /* MGLPolyline_Private.h in Headers */, DA6408D71DA4E5DA00908C90 /* MGLVectorStyleLayer.h in Headers */, @@ -1568,6 +1583,7 @@ DAE6C3B51CC31EF300DB3429 /* MGLCompassCell.m in Sources */, DA8F25901D51CA600010E6B5 /* MGLRasterStyleLayer.mm in Sources */, DAD165751CF4CD7A001FF4B9 /* MGLShapeCollection.mm in Sources */, + 92FC0AE6207CDD8D007B6B54 /* MGLShapeOfflineRegion.mm in Sources */, 35C5D8481D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm in Sources */, DA35A2AE1CCA091800E826B2 /* MGLCompassDirectionFormatter.m in Sources */, DACA8623201920BE00E9693A /* MGLRasterDEMSource.mm in Sources */, @@ -1967,10 +1983,6 @@ INFOPLIST_FILE = "$(SRCROOT)/sdk/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/cmake/Debug", - ); OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_LDFLAGS = ( "$(mbgl_core_LINK_LIBRARIES)", diff --git a/platform/macos/src/MGLMapView+IBAdditions.mm b/platform/macos/src/MGLMapView+IBAdditions.mm index cef2863ee6..0c65abc031 100644 --- a/platform/macos/src/MGLMapView+IBAdditions.mm +++ b/platform/macos/src/MGLMapView+IBAdditions.mm @@ -1,5 +1,7 @@ #import "MGLMapView+IBAdditions.h" +#import "MGLStyle.h" + #import "MGLMapView_Private.h" @implementation MGLMapView (IBAdditions) @@ -17,7 +19,7 @@ [NSCharacterSet whitespaceAndNewlineCharacterSet]]; NSURL *url = URLString.length ? [NSURL URLWithString:URLString] : nil; if (URLString.length && !url) { - [NSException raise:@"Invalid style URL" + [NSException raise:MGLInvalidStyleURLException format:@"“%@” is not a valid style URL.", URLString]; } self.styleURL = url; diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 154b716377..3c1fe18499 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -2108,7 +2108,11 @@ public: if ([annotation isKindOfClass:[MGLMultiPoint class]]) { - return false; + if ([self.delegate respondsToSelector:@selector(mapView:shapeAnnotationIsEnabled:)]) { + return !!(![self.delegate mapView:self shapeAnnotationIsEnabled:(MGLMultiPoint *)annotation]); + } else { + return false; + } } MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; diff --git a/platform/macos/src/MGLMapViewDelegate.h b/platform/macos/src/MGLMapViewDelegate.h index dae5b40286..2a8b28c1b4 100644 --- a/platform/macos/src/MGLMapViewDelegate.h +++ b/platform/macos/src/MGLMapViewDelegate.h @@ -244,6 +244,18 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark Selecting Annotations /** + Returns a Boolean value indicating whether the shape annotation can be selected. + + If the return value is `YES`, the user can select the annotation by clicking + on it. If the delegate does not implement this method, the default value is `YES`. + + @param mapView The map view that has selected the annotation. + @param annotation The object representing the shape annotation. + @return A Boolean value indicating whether the annotation can be selected. + */ +- (BOOL)mapView:(MGLMapView *)mapView shapeAnnotationIsEnabled:(MGLShape *)annotation; + +/** Tells the delegate that one of its annotations has been selected. You can use this method to track changes to the selection state of annotations. diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index 198998a874..dcffd73dfc 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -56,6 +56,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLRasterDEMSource.h" #import "MGLImageSource.h" #import "MGLTilePyramidOfflineRegion.h" +#import "MGLShapeOfflineRegion.h" #import "MGLTypes.h" #import "NSValue+MGLAdditions.h" #import "MGLStyleValue.h" diff --git a/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift b/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift index 3f82e7c61a..00635d97eb 100644 --- a/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift +++ b/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift @@ -26,6 +26,8 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate { func mapViewDidFinishRenderingMap(_ mapView: MGLMapView, fullyRendered: Bool) {} func mapViewDidFailLoadingMap(_ mapView: MGLMapView, withError error: Error) {} + + func mapView(_ mapView: MGLMapView, shapeAnnotationIsEnabled annotation: MGLShape) -> Bool { return false } func mapView(_ mapView: MGLMapView, didDeselect annotation: MGLAnnotation) {} diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md index d2b6c76ab1..cfb86f4dbf 100644 --- a/platform/node/CHANGELOG.md +++ b/platform/node/CHANGELOG.md @@ -1,6 +1,9 @@ # master +- Don't default-show text/icons that depend on the placement of a paired icon/text [#12483](https://github.com/mapbox/mapbox-gl-native/issues/12483) +- Fix symbol querying for annotations near tile boundaries at high zoom. ([#12472](https://github.com/mapbox/mapbox-gl-native/issues/12472)) - The `Map` constructor now accepts a `mode` option which can be either `"static"` (default) or `"tile"`. It must be set to `"tile"` when rendering individual tiles in order for the symbols to match across tiles. - Remove unnecessary memory use when collision debug mode is not enabled ([#12294](https://github.com/mapbox/mapbox-gl-native/issues/12294)) +- Added support for rendering `symbol-placement: line-center` ([#12337](https://github.com/mapbox/mapbox-gl-native/pull/12337)) # 3.5.8 - October 19, 2017 - Fixes an issue that causes memory leaks when not deleting the frontend object diff --git a/platform/node/src/node_conversion.hpp b/platform/node/src/node_conversion.hpp index 7c5bbf4386..ea6652c5e2 100644 --- a/platform/node/src/node_conversion.hpp +++ b/platform/node/src/node_conversion.hpp @@ -8,8 +8,8 @@ #include <mbgl/util/optional.hpp> #include <mbgl/util/feature.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/platform/node/src/node_expression.hpp b/platform/node/src/node_expression.hpp index 05af217bde..a53f8c18db 100644 --- a/platform/node/src/node_expression.hpp +++ b/platform/node/src/node_expression.hpp @@ -1,6 +1,6 @@ #pragma once -#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/style/expression/expression.hpp> #include <exception> #include <memory> diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index 4bcd1d97bc..0cc93d7e95 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -822,7 +822,7 @@ void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info return Nan::ThrowTypeError("Second argument must be a string"); } - mbgl::optional<Error> error = setLayoutProperty(*layer, *Nan::Utf8String(info[1]), Convertible(info[2])); + mbgl::optional<Error> error = layer->setLayoutProperty(*Nan::Utf8String(info[1]), Convertible(info[2])); if (error) { return Nan::ThrowTypeError(error->message.c_str()); } @@ -854,7 +854,7 @@ void NodeMap::SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) return Nan::ThrowTypeError("Second argument must be a string"); } - mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), Convertible(info[2])); + mbgl::optional<Error> error = layer->setPaintProperty(*Nan::Utf8String(info[1]), Convertible(info[2])); if (error) { return Nan::ThrowTypeError(error->message.c_str()); } diff --git a/platform/node/test/expression.test.js b/platform/node/test/expression.test.js index ffd1c68ff2..4635ef78f7 100644 --- a/platform/node/test/expression.test.js +++ b/platform/node/test/expression.test.js @@ -1,10 +1,8 @@ -'use strict'; +import {run} from '../../../mapbox-gl-js/test/integration/lib/expression'; +import mbgl from '../index'; +import ignores from './ignores.json'; -var suite = require('../../../mapbox-gl-js/test/integration').expression; -var mbgl = require('../index'); -var ignores = require('./ignores.json'); - -var tests; +let tests; if (process.argv[1] === __filename && process.argv.length > 2) { tests = process.argv.slice(2); @@ -30,7 +28,7 @@ function getExpectedType(spec) { return typeof spec.type === 'string' ? {kind: spec.type} : null; } -suite.run('native', {ignores: ignores, tests: tests}, (fixture) => { +run('native', {ignores, tests}, (fixture) => { const compiled = {}; const recompiled = {}; const result = { @@ -55,7 +53,7 @@ suite.run('native', {ignores: ignores, tests: tests}, (fixture) => { type: 'Feature', properties: {}, geometry: { type: 'Point', coordinates: [0, 0] } - }, input[1]) + }, input[1]); const output = expression.evaluate(input[0], feature); evaluateResults.push(output); @@ -68,7 +66,7 @@ suite.run('native', {ignores: ignores, tests: tests}, (fixture) => { compilationResult.result = 'error'; compilationResult.errors = expression; } - } + }; result.outputs = evaluateExpression(expression, compiled); if (expression instanceof mbgl.Expression) { diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 18e9e11c49..2d46317a95 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -1,5 +1,9 @@ { "expression-tests/collator/accent-equals-de": "Locale-specific behavior changes based on platform.", + "expression-tests/formatted/basic": "skip - https://github.com/mapbox/mapbox-gl-native/pull/12624", + "expression-tests/formatted/to-string": "skip - https://github.com/mapbox/mapbox-gl-native/pull/12624", + "expression-tests/interpolate-hcl/linear": "https://github.com/mapbox/mapbox-gl-native/issues/8720", + "expression-tests/interpolate-lab/linear": "https://github.com/mapbox/mapbox-gl-native/issues/8720", "expression-tests/is-supported-script/default": "This tests RTL text plugin behavior specific to GL JS", "expression-tests/resolved-locale/basic": "Even the 'en' locale may not be present on some test systems.", "query-tests/geometry/multilinestring": "needs investigation", @@ -9,12 +13,16 @@ "query-tests/world-wrapping/point": "skip - needs issue", "query-tests/circle-radius/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "query-tests/circle-stroke-width/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", + "query-tests/fill-extrusion-translate/multiple-layers": "https://github.com/mapbox/mapbox-gl-native/issues/12701", + "query-tests/fill-translate/multiple-layers": "https://github.com/mapbox/mapbox-gl-native/issues/12701", "query-tests/line-gap-width/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "query-tests/line-offset/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "query-tests/line-width/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "query-tests/feature-state/default": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "query-tests/regressions/mapbox-gl-js#6555": "skip - no querySourceFeatures in mbgl-node; needs issue", "render-tests/background-color/transition": "https://github.com/mapbox/mapbox-gl-native/issues/10619", + "render-tests/basic-v9/z0-wide": "https://github.com/mapbox/mapbox-gl-native/pull/12611", + "render-tests/bright-v9/z0-wide": "https://github.com/mapbox/mapbox-gl-native/pull/12611", "render-tests/collator/resolved-locale": "Some test platforms don't resolve 'en' locale", "render-tests/collator/default": "Some test platforms don't resolve 'en' locale", "render-tests/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841", @@ -56,9 +64,13 @@ "render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847", "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745", "render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745", + "render-tests/satellite-v9/z0-wide": "https://github.com/mapbox/mapbox-gl-native/pull/12611", "render-tests/symbol-cross-fade/chinese": "https://github.com/mapbox/mapbox-gl-native/issues/10619", "render-tests/symbol-placement/line-overscaled": "https://github.com/mapbox/mapbox-gl-js/issues/5654", "render-tests/symbol-visibility/visible": "https://github.com/mapbox/mapbox-gl-native/issues/10409", + "render-tests/text-field/formatted-arabic": "skip - https://github.com/mapbox/mapbox-gl-native/pull/12624", + "render-tests/text-field/formatted-line": "skip - https://github.com/mapbox/mapbox-gl-native/pull/12624", + "render-tests/text-field/formatted": "skip - https://github.com/mapbox/mapbox-gl-native/pull/12624", "render-tests/text-no-cross-source-collision/default": "skip - gl-js only", "render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", "render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", diff --git a/platform/node/test/query.test.js b/platform/node/test/query.test.js index 02602d3f5a..3dfce4474c 100644 --- a/platform/node/test/query.test.js +++ b/platform/node/test/query.test.js @@ -1,8 +1,6 @@ -'use strict'; - -const suite = require('../../../mapbox-gl-js/test/integration').query; -const suiteImplementation = require('./suite_implementation'); -const ignores = require('./ignores.json'); +import {run} from '../../../mapbox-gl-js/test/integration/lib/query'; +import implementation from './suite_implementation'; +import ignores from './ignores.json'; let tests; @@ -10,4 +8,4 @@ if (process.argv[1] === __filename && process.argv.length > 2) { tests = process.argv.slice(2); } -suite.run('native', {tests: tests, ignores: ignores}, suiteImplementation); +run('native', {tests, ignores}, implementation); diff --git a/platform/node/test/render.test.js b/platform/node/test/render.test.js index 812a531f20..950f8eb7ab 100644 --- a/platform/node/test/render.test.js +++ b/platform/node/test/render.test.js @@ -1,7 +1,5 @@ -'use strict'; +import {run} from '../../../mapbox-gl-js/test/integration/lib/render'; +import implementation from './suite_implementation'; +import ignores from './ignores.json'; -const suite = require('../../../mapbox-gl-js/test/integration').render; -const suiteImplementation = require('./suite_implementation'); -const ignores = require('./ignores.json'); - -suite.run('native', ignores, suiteImplementation); +run('native', ignores, implementation); diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js index c09e8f50bf..704cab8940 100644 --- a/platform/node/test/suite_implementation.js +++ b/platform/node/test/suite_implementation.js @@ -1,24 +1,24 @@ -'use strict'; - -var mbgl = require('../index'); -var request = require('request'); -var PNG = require('pngjs').PNG; -var fs = require('fs'); -var path = require('path'); +import mbgl from '../index'; +import request from 'request'; +import {PNG} from 'pngjs'; +import * as fs from 'fs'; +import * as path from 'path'; mbgl.on('message', function(msg) { console.log('%s (%s): %s', msg.severity, msg.class, msg.text); }); // Map of map objects by pixel ratio -var maps = new Map(); +const maps = new Map(); + +export default function (style, options, callback) { + const tileMode = options.mapMode === 'tile'; + let map; -module.exports = function (style, options, callback) { - var tileMode = options.mapMode === 'tile'; if (options.recycleMap) { - var key = options.pixelRatio + '/' + tileMode; + const key = options.pixelRatio + '/' + tileMode; if (maps.has(key)) { - var map = maps.get(key); + map = maps.get(key); map.request = mapRequest; } else { maps.set(key, new mbgl.Map({ @@ -26,18 +26,18 @@ module.exports = function (style, options, callback) { request: mapRequest, mode: options.mapMode })); - var map = maps.get(key); + map = maps.get(key); } } else { - var map = new mbgl.Map({ + map = new mbgl.Map({ ratio: options.pixelRatio, request: mapRequest, mode: options.mapMode }); } - var timedOut = false; - var watchdog = setTimeout(function () { + let timedOut = false; + const watchdog = setTimeout(function () { timedOut = true; map.dumpDebugLogs(); callback(new Error('timed out after 20 seconds')); @@ -61,19 +61,19 @@ module.exports = function (style, options, callback) { request(req.url, {encoding: null}, function (err, response, body) { if (err) { callback(err); - } else if (response.statusCode == 404) { + } else if (response.statusCode === 404) { callback(); - } else if (response.statusCode != 200) { + } else if (response.statusCode !== 200) { callback(new Error(response.statusMessage)); } else { callback(null, {data: body}); } }); - }; + } applyOperations(options.operations, function() { map.render(options, function (err, pixels) { - var results = options.queryGeometry ? + const results = options.queryGeometry ? map.queryRenderedFeatures(options.queryGeometry, options.queryOptions || {}) : []; if (!options.recycleMap) { @@ -86,7 +86,7 @@ module.exports = function (style, options, callback) { }); function applyOperations(operations, callback) { - var operation = operations && operations[0]; + const operation = operations && operations[0]; if (!operations || operations.length === 0) { callback(); @@ -102,7 +102,7 @@ module.exports = function (style, options, callback) { applyOperations(operations.slice(1), callback); }, operation[1]); } else if (operation[0] === 'addImage' || operation[0] === 'updateImage') { - var img = PNG.sync.read(fs.readFileSync(path.join(__dirname, '../../../mapbox-gl-js/test/integration', operation[2]))); + const img = PNG.sync.read(fs.readFileSync(path.join(__dirname, '../../../mapbox-gl-js/test/integration', operation[2]))); const testOpts = (operation.length > 3) ? operation[3] : {}; const options = { @@ -110,7 +110,7 @@ module.exports = function (style, options, callback) { width: img.width, pixelRatio: testOpts.pixelRatio || 1, sdf: testOpts.sdf || false - } + }; map.addImage(operation[1], img.data, options); diff --git a/platform/qt/src/http_file_source.cpp b/platform/qt/src/http_file_source.cpp index 6e70693241..b95cfed0e9 100644 --- a/platform/qt/src/http_file_source.cpp +++ b/platform/qt/src/http_file_source.cpp @@ -29,6 +29,9 @@ void HTTPFileSource::Impl::request(HTTPRequest* req) } QNetworkRequest networkRequest = req->networkRequest(); +#if QT_VERSION >= 0x050600 + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); +#endif data.first = m_manager->get(networkRequest); connect(data.first, SIGNAL(finished()), this, SLOT(onReplyFinished())); @@ -72,7 +75,7 @@ void HTTPFileSource::Impl::cancel(HTTPRequest* req) void HTTPFileSource::Impl::onReplyFinished() { QNetworkReply* reply = qobject_cast<QNetworkReply *>(sender()); - const QUrl& url = reply->url(); + const QUrl& url = reply->request().url(); auto it = m_pending.find(url); if (it == m_pending.end()) { diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 459eacba4b..d584012830 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -13,11 +13,11 @@ #include <mbgl/math/log2.hpp> #include <mbgl/math/minmax.hpp> #include <mbgl/style/style.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/source.hpp> #include <mbgl/style/conversion/filter.hpp> #include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/style/filter.hpp> #include <mbgl/style/layers/custom_layer.hpp> #include <mbgl/style/layers/background_layer.hpp> @@ -1017,7 +1017,7 @@ void QMapboxGL::removeAnnotation(QMapbox::AnnotationID id) */ bool QMapboxGL::setLayoutProperty(const QString& layer, const QString& propertyName, const QVariant& value) { - return d_ptr->setProperty(&mbgl::style::conversion::setLayoutProperty, layer, propertyName, value); + return d_ptr->setProperty(&mbgl::style::Layer::setLayoutProperty, layer, propertyName, value); } /*! @@ -1077,7 +1077,7 @@ bool QMapboxGL::setLayoutProperty(const QString& layer, const QString& propertyN bool QMapboxGL::setPaintProperty(const QString& layer, const QString& propertyName, const QVariant& value) { - return d_ptr->setProperty(&mbgl::style::conversion::setPaintProperty, layer, propertyName, value); + return d_ptr->setProperty(&mbgl::style::Layer::setPaintProperty, layer, propertyName, value); } /*! @@ -1379,7 +1379,7 @@ void QMapboxGL::addCustomLayer(const QString &id, } void initialize() { - ptr->initialize(); + ptr->initialize(); } void render(const mbgl::style::CustomLayerRenderParameters& params) { @@ -1922,16 +1922,16 @@ bool QMapboxGLPrivate::setProperty(const PropertySetter& setter, const QString& if (!document.HasParseError()) { // Treat value as a valid JSON. const mbgl::JSValue* jsonValue = &document; - result = setter(*layerObject, propertyString, jsonValue); + result = (layerObject->*setter)(propertyString, jsonValue); } else { - result = setter(*layerObject, propertyString, value); + result = (layerObject->*setter)(propertyString, value); } } else { - result = setter(*layerObject, propertyString, value); + result = (layerObject->*setter)(propertyString, value); } if (result) { - qWarning() << "Error setting paint property" << name << "on layer" << layer << ":" << QString::fromStdString(result->message); + qWarning() << "Error setting property" << name << "on layer" << layer << ":" << QString::fromStdString(result->message); return false; } diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index 80789ac8f1..b94f4de194 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -37,7 +37,7 @@ public: void render(); void setFramebufferObject(quint32 fbo, const QSize& size); - using PropertySetter = std::function<mbgl::optional<mbgl::style::conversion::Error>(mbgl::style::Layer&, const std::string&, const mbgl::style::conversion::Convertible&)>; + using PropertySetter = mbgl::optional<mbgl::style::conversion::Error> (mbgl::style::Layer::*)(const std::string&, const mbgl::style::conversion::Convertible&); bool setProperty(const PropertySetter& setter, const QString& layer, const QString& name, const QVariant& value); mbgl::EdgeInsets margins; diff --git a/platform/qt/src/qt_conversion.hpp b/platform/qt/src/qt_conversion.hpp index 19b0cb54fc..99a262be54 100644 --- a/platform/qt/src/qt_conversion.hpp +++ b/platform/qt/src/qt_conversion.hpp @@ -1,7 +1,7 @@ #pragma once -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/optional.hpp> #include <QVariant> diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp index 2ca09fd3ad..96cee5fff8 100644 --- a/platform/qt/src/sqlite3.cpp +++ b/platform/qt/src/sqlite3.cpp @@ -23,13 +23,6 @@ namespace mapbox { namespace sqlite { -// https://www.sqlite.org/rescode.html#ok -static_assert(mbgl::underlying_type(ResultCode::OK) == 0, "error"); -// https://www.sqlite.org/rescode.html#cantopen -static_assert(mbgl::underlying_type(ResultCode::CantOpen) == 14, "error"); -// https://www.sqlite.org/rescode.html#notadb -static_assert(mbgl::underlying_type(ResultCode::NotADB) == 26, "error"); - void checkQueryError(const QSqlQuery& query) { QSqlError lastError = query.lastError(); if (lastError.type() != QSqlError::NoError) { @@ -114,6 +107,11 @@ mapbox::util::variant<Database, Exception> Database::tryOpen(const std::string & connectOptions.append("QSQLITE_OPEN_READONLY"); } + if (filename.compare(0, 5, "file:") == 0) { + if (!connectOptions.isEmpty()) connectOptions.append(';'); + connectOptions.append("QSQLITE_OPEN_URI"); + } + db.setConnectOptions(connectOptions); db.setDatabaseName(QString(filename.c_str())); diff --git a/scripts/check_binary_size.js b/scripts/check_binary_size.js index 9caa1b9998..5a8aa70b1b 100755 --- a/scripts/check_binary_size.js +++ b/scripts/check_binary_size.js @@ -51,9 +51,7 @@ function getPriorSize() { console.log('No matching check found.'); return Promise.resolve(null); } - const prior = +run.output.summary.match(/`.*` is (\d+) bytes/)[1]; - console.log(`Prior size was ${prettyBytes(prior)}.`); - return prior; + return +run.output.summary.match(/`.*` is (\d+) bytes/)[1]; }); } @@ -71,6 +69,8 @@ github.apps.createInstallationToken({installation_id: SIZE_CHECK_APP_INSTALLATIO } })(); + console.log(`${label}: ${title} (${size} bytes)`); + return github.checks.create({ owner: 'mapbox', repo: 'mapbox-gl-native', diff --git a/scripts/config.xcconfig.in b/scripts/config.xcconfig.in index 357732c9ae..69ca2424a1 100644 --- a/scripts/config.xcconfig.in +++ b/scripts/config.xcconfig.in @@ -1,9 +1,9 @@ // Do not edit -- generated by CMake // mbgl-core -mbgl_core_INCLUDE_DIRECTORIES = "@mbgl_core_INCLUDE_DIRECTORIES@" -mbgl_core_LINK_LIBRARIES = "@mbgl_core_LINK_LIBRARIES@" +mbgl_core_INCLUDE_DIRECTORIES = "$<JOIN:$<TARGET_PROPERTY:mbgl-core,INTERFACE_INCLUDE_DIRECTORIES>," ">" +mbgl_core_LINK_LIBRARIES = "$<TARGET_PROPERTY:mbgl-core,XCODE_ATTRIBUTE_XCCONFIG_LINK_LIBRARIES>" // mbgl-filesource -mbgl_filesource_INCLUDE_DIRECTORIES = "@mbgl_filesource_INCLUDE_DIRECTORIES@" -mbgl_filesource_LINK_LIBRARIES = "@mbgl_filesource_LINK_LIBRARIES@" +mbgl_filesource_INCLUDE_DIRECTORIES = "$<JOIN:$<TARGET_PROPERTY:mbgl-filesource,INTERFACE_INCLUDE_DIRECTORIES>," ">" +mbgl_filesource_LINK_LIBRARIES = "$<TARGET_PROPERTY:mbgl-filesource,XCODE_ATTRIBUTE_XCCONFIG_LINK_LIBRARIES>" diff --git a/scripts/environment.js b/scripts/environment.js index 9777377f19..0468be3720 100755 --- a/scripts/environment.js +++ b/scripts/environment.js @@ -24,7 +24,7 @@ if (pr) { } else { const head = process.env['CIRCLE_SHA1']; for (const sha of execSync(`git rev-list --max-count=10 ${head}`).toString().trim().split('\n')) { - const base = execSync(`git branch -r --contains ${sha} origin/master origin/release-*`).toString().trim().replace(/^origin\//, ''); + const base = execSync(`git branch -r --contains ${sha} origin/master origin/release-*`).toString().split('\n')[0].trim().replace(/^origin\//, ''); if (base) { const mergeBase = execSync(`git merge-base origin/${base} ${head}`).toString().trim(); console.log(`export CIRCLE_TARGET_BRANCH=${base}`); diff --git a/scripts/generate-cmake-files.js b/scripts/generate-cmake-files.js index 4b6a8b8672..bbbb9accec 100755 --- a/scripts/generate-cmake-files.js +++ b/scripts/generate-cmake-files.js @@ -6,7 +6,7 @@ const ejs = require('ejs'); require('./style-code'); -function generateCMakeListFile(name, regex, patterns) { +function generateFileList(name, regex, patterns) { const files = child_process.execSync(`git ls-files ${patterns.map((p) => '"' + p + '"').join(' ')}`).toString().trim().split('\n'); var groups = {}; for (const file of files) { @@ -18,15 +18,15 @@ function generateCMakeListFile(name, regex, patterns) { groups[group].push(file); } - const fileListCmake = ejs.compile(fs.readFileSync('cmake/files.cmake.ejs', 'utf8'), {strict: true}); - writeIfModified(`cmake/${name}-files.cmake`, fileListCmake({ name: name, groups: groups })); + const fileListCmake = ejs.compile(fs.readFileSync('cmake/files.txt.ejs', 'utf8'), {strict: true}); + writeIfModified(`cmake/${name}-files.txt`, fileListCmake({ groups: groups })); } -generateCMakeListFile('core', /^(?:src|include)\/(?:mbgl\/)?(.+)\/[^\/]+$/, +generateFileList('core', /^(?:src|include)\/(?:mbgl\/)?(.+)\/[^\/]+$/, [ 'include/*.hpp', 'include/*.h', 'src/*.hpp', 'src/*.cpp', 'src/*.h', 'src/*.c' ]); -generateCMakeListFile('benchmark', /^benchmark\/(?:(?:src|include)\/)?(?:mbgl\/)?(?:(.+)\/)?[^\/]+$/, +generateFileList('benchmark', /^benchmark\/(?:(?:src|include)\/)?(?:mbgl\/)?(?:(.+)\/)?[^\/]+$/, [ 'benchmark/*.hpp', 'benchmark/*.cpp', 'benchmark/*.h', 'benchmark/*.c' ]); -generateCMakeListFile('test', /^test\/(?:(?:src|include)\/)?(?:mbgl\/)?(?:(.+)\/)?[^\/]+$/, +generateFileList('test', /^test\/(?:(?:src|include)\/)?(?:mbgl\/)?(?:(.+)\/)?[^\/]+$/, [ 'test/*.hpp', 'test/*.cpp', 'test/*.h', 'test/*.c' ]); diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index db37a9b96f..236df0a921 100755 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -37,6 +37,7 @@ global.evaluatedType = function (property) { return 'bool'; case 'number': return 'float'; + case 'formatted': case 'string': return 'std::string'; case 'enum': @@ -120,6 +121,7 @@ global.defaultValue = function (property) { switch (property.type) { case 'number': return property.default; + case 'formatted': case 'string': return JSON.stringify(property.default || ""); case 'enum': @@ -192,9 +194,6 @@ for (const layer of layers) { writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer_properties.cpp`, propertiesCpp(layer)); } -const propertySettersHpp = ejs.compile(fs.readFileSync('src/mbgl/style/conversion/make_property_setters.hpp.ejs', 'utf8'), {strict: true}); -writeIfModified('src/mbgl/style/conversion/make_property_setters.hpp', propertySettersHpp({layers: layers})); - // Light const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => { var property = spec[`light`][name]; diff --git a/scripts/vendor/expected.sh b/scripts/vendor/expected.sh new file mode 100755 index 0000000000..58c98902f6 --- /dev/null +++ b/scripts/vendor/expected.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +source "$(dirname "${BASH_SOURCE[0]}")/common.sh" + +NAME=expected +VERSION=a2359cf61478d735a308e952b1c9840714f61386 +ROOT=expected-lite-$VERSION + +download "https://github.com/martinmoene/expected-lite/archive/$VERSION.tar.gz" +init +extract "$ROOT/include" "$ROOT/LICENSE.txt" +mv include/nonstd/expected.hpp include +rm -rf include/nonstd +file_list include -name "*.hpp" diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index 41eedf17dc..1baf83179e 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -140,7 +140,13 @@ std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const Canonic auto pointLayer = tileData->addLayer(PointLayerID); LatLngBounds tileBounds(tileID); - + // Hack for https://github.com/mapbox/mapbox-gl-native/issues/12472 + // To handle precision issues, query a slightly larger area than the tile bounds + // Symbols at a border can be included in vector data for both tiles + // The rendering/querying logic will make sure the symbols show up in only one of the tiles + tileBounds.extend(LatLng(tileBounds.south() - 0.000000001, tileBounds.west() - 0.000000001)); + tileBounds.extend(LatLng(tileBounds.north() + 0.000000001, tileBounds.east() + 0.000000001)); + symbolTree.query(boost::geometry::index::intersects(tileBounds), boost::make_function_output_iterator([&](const auto& val){ val->updateLayer(tileID, *pointLayer); diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 41469f293d..ab718351ab 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -258,11 +258,6 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, const float textMaxBoxScale = tilePixelRatio * textMaxSize / glyphSize; const float iconBoxScale = tilePixelRatio * layoutIconSize; const float symbolSpacing = tilePixelRatio * layout.get<SymbolSpacing>(); - // CJL: I'm not sure why SymbolPlacementType::Line -> avoidEdges = false. It seems redundant since - // getAnchors will already avoid generating anchors outside the tile bounds. - // However, SymbolPlacementType::LineCenter allows anchors outside tile boundaries, so its behavior - // here should match SymbolPlacement::Point - const bool avoidEdges = layout.get<SymbolAvoidEdges>() && layout.get<SymbolPlacement>() != SymbolPlacementType::Line; const float textPadding = layout.get<TextPadding>() * tilePixelRatio; const float iconPadding = layout.get<IconPadding>() * tilePixelRatio; const float textMaxAngle = layout.get<TextMaxAngle>() * util::DEG2RAD; @@ -274,25 +269,14 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size()); auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { - // https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers - // +-------------------+ Symbols with anchors located on tile edges - // |(0,0) || are duplicated on neighbor tiles. - // | || - // | || In continuous mode, to avoid overdraw we - // | || skip symbols located on the extent edges. - // | Tile || In still mode, we include the features in - // | || the buffers for both tiles and clip them - // | || at draw time. - // | || - // +-------------------| In this scenario, the inner bounding box - // +-------------------+ is called 'withinPlus0', and the outer - // (extent,extent) is called 'inside'. - const bool withinPlus0 = anchor.point.x >= 0 && anchor.point.x < util::EXTENT && anchor.point.y >= 0 && anchor.point.y < util::EXTENT; - const bool inside = withinPlus0 || anchor.point.x == util::EXTENT || anchor.point.y == util::EXTENT; - - if (avoidEdges && !inside) return; - - if (mode == MapMode::Tile || withinPlus0) { + const bool anchorInsideTile = anchor.point.x >= 0 && anchor.point.x < util::EXTENT && anchor.point.y >= 0 && anchor.point.y < util::EXTENT; + + if (mode == MapMode::Tile || anchorInsideTile) { + // For static/continuous rendering, only add symbols anchored within this tile: + // neighboring symbols will be added as part of the neighboring tiles. + // In tiled rendering mode, add all symbols in the buffers so that we can: + // (1) render symbols that overlap into this tile + // (2) approximate collision detection effects from neighboring symbols symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout.evaluate(zoom, feature), layoutTextSize, textBoxScale, textPadding, textPlacement, textOffset, diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 0378cc0970..651f12828f 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -66,8 +66,8 @@ struct SymbolLayoutAttributes : gl::Attributes< {{ tx, ty, - static_cast<uint16_t>(sizeData.min * 10), - static_cast<uint16_t>(sizeData.max * 10) + static_cast<uint16_t>(sizeData.min * 256), + static_cast<uint16_t>(sizeData.max * 256) }} }; } diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 027e864be5..a3f652fc6e 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -193,8 +193,8 @@ void SymbolBucket::sortFeatures(const float angle) { std::sort(symbolInstanceIndexes.begin(), symbolInstanceIndexes.end(), [sin, cos, this](size_t &aIndex, size_t &bIndex) { const SymbolInstance& a = symbolInstances[aIndex]; const SymbolInstance& b = symbolInstances[bIndex]; - const int32_t aRotated = static_cast<int32_t>(std::lround(sin * a.anchor.point.x + cos * a.anchor.point.y)); - const int32_t bRotated = static_cast<int32_t>(std::lround(sin * b.anchor.point.x + cos * b.anchor.point.y)); + const int32_t aRotated = static_cast<int32_t>(::lround(sin * a.anchor.point.x + cos * a.anchor.point.y)); + const int32_t bRotated = static_cast<int32_t>(::lround(sin * b.anchor.point.x + cos * b.anchor.point.y)); return aRotated != bRotated ? aRotated < bRotated : a.dataFeatureIndex > b.dataFeatureIndex; diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp index 25eef98fcf..8bcd3f1837 100644 --- a/src/mbgl/renderer/layers/render_hillshade_layer.cpp +++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp @@ -59,8 +59,8 @@ bool RenderHillshadeLayer::hasTransition() const { void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src) { if (parameters.pass != RenderPass::Translucent && parameters.pass != RenderPass::Pass3D) return; - - RenderRasterDEMSource* demsrc = dynamic_cast<RenderRasterDEMSource*>(src); + + RenderRasterDEMSource* demsrc = static_cast<RenderRasterDEMSource*>(src); const uint8_t TERRAIN_RGB_MAXZOOM = 15; const uint8_t maxzoom = demsrc != nullptr ? demsrc->getMaxZoom() : TERRAIN_RGB_MAXZOOM; diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 63fcb6cfd5..f9e4e7c043 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -126,7 +126,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { ); }; - assert(dynamic_cast<GeometryTile*>(&tile.tile)); + assert(tile.tile.kind == Tile::Kind::Geometry); GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile); if (bucket.hasIconData()) { diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp index a667d5837e..4cd033da75 100644 --- a/src/mbgl/renderer/render_layer.cpp +++ b/src/mbgl/renderer/render_layer.cpp @@ -91,7 +91,7 @@ void RenderLayer::checkRenderability(const PaintParameters& parameters, activeBindingCount - parameters.context.minimumRequiredVertexBindingCount); hasRenderFailures = true; } else if (activeBindingCount > parameters.context.minimumRequiredVertexBindingCount) { - Log::Error(Event::OpenGL, + Log::Warning(Event::OpenGL, "The layer '%s' uses more data-driven properties than some devices may support. " "Though it will render correctly on this device, it may have rendering errors " "on other devices. To ensure compatibility with all devices, use %d fewer " diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index fea27403c9..d3f72b89b9 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -173,6 +173,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { renderLayers.at(entry.first)->setImpl(entry.second.after); } + if (!layerDiff.removed.empty() || !layerDiff.added.empty() || !layerDiff.changed.empty()) { + glyphManager->evict(fontStacks(*updateParameters.layers)); + } + // Update layers for class and zoom changes. for (const auto& entry : renderLayers) { RenderLayer& layer = *entry.second; diff --git a/src/mbgl/shaders/source.cpp b/src/mbgl/shaders/source.cpp index d5a4593ad9..242b726004 100644 --- a/src/mbgl/shaders/source.cpp +++ b/src/mbgl/shaders/source.cpp @@ -1272,193 +1272,193 @@ const char* source() { 0xaf, 0xea, 0x39, 0x92, 0x2d, 0xac, 0x48, 0x1c, 0xf1, 0x0c, 0x60, 0x84, 0xdf, 0x0e, 0xde, 0x75, 0x24, 0xee, 0x6f, 0x87, 0xef, 0x3a, 0x9a, 0x51, - 0xe1, 0xa1, 0x3b, 0x1c, 0xa8, 0x53, 0xf7, 0x0f, - 0x81, 0xb3, 0x4f, 0x48, 0xdc, 0x1a, 0x0e, 0xba, - 0xff, 0xba, 0x9e, 0x9a, 0x86, 0x7b, 0xad, 0x9e, - 0x24, 0x4b, 0xb4, 0xe1, 0x37, 0xd7, 0xa9, 0xba, - 0xdc, 0x79, 0x4d, 0xee, 0xfb, 0xdb, 0x68, 0xa0, - 0xf6, 0x82, 0x1b, 0xde, 0x09, 0x0c, 0x73, 0x91, - 0x18, 0xde, 0x09, 0x90, 0x5c, 0xd0, 0xbd, 0x8d, - 0xd6, 0x5f, 0xcf, 0x02, 0x72, 0x8b, 0x27, 0x17, - 0x55, 0x54, 0x16, 0x99, 0x65, 0xa2, 0xf1, 0xa1, - 0xc7, 0x97, 0x90, 0xa5, 0x6e, 0x14, 0x54, 0x66, - 0x9c, 0x84, 0xa5, 0xbb, 0xe9, 0xc5, 0x37, 0x46, - 0x5e, 0xad, 0x45, 0xa9, 0xdf, 0xc4, 0xec, 0xc5, - 0xb1, 0x86, 0xd0, 0x54, 0xab, 0x5f, 0x0f, 0xbf, - 0x3c, 0x3b, 0xd6, 0x9d, 0x8a, 0xc6, 0x9c, 0x0d, - 0x52, 0xba, 0x2f, 0xfc, 0x1f, 0x5b, 0x5f, 0xf8, - 0xf6, 0xcf, 0x1d, 0x69, 0xa7, 0x50, 0x0f, 0x56, - 0x01, 0xe6, 0xef, 0x75, 0x1a, 0x9c, 0xd3, 0xdc, - 0xa1, 0x13, 0x2e, 0x2e, 0xeb, 0x54, 0xc4, 0x81, - 0x97, 0x76, 0x67, 0x61, 0x10, 0xa9, 0xd7, 0x0a, - 0x19, 0xdb, 0x0d, 0xf1, 0x8a, 0x6b, 0xda, 0xc7, - 0x7a, 0x48, 0x62, 0x78, 0x2c, 0x6b, 0xfd, 0x44, - 0xfd, 0xef, 0xc0, 0x58, 0xb6, 0x88, 0x56, 0x40, - 0x12, 0x2f, 0xe1, 0xed, 0x6c, 0xda, 0x19, 0x48, - 0x92, 0x67, 0xf2, 0xba, 0x49, 0x33, 0x65, 0xf1, - 0x0d, 0xb7, 0xee, 0x8b, 0xd1, 0x01, 0x5d, 0x5f, - 0x4b, 0x8e, 0x5b, 0xe2, 0x10, 0xbc, 0xd6, 0x74, - 0x58, 0xb2, 0xc9, 0x43, 0x6f, 0x0d, 0xde, 0x7d, - 0xce, 0x31, 0x6a, 0x13, 0xfb, 0xd6, 0x84, 0xa3, - 0x69, 0x9b, 0x79, 0xdc, 0xeb, 0xad, 0x28, 0x5c, - 0xa5, 0xc8, 0x1b, 0xca, 0xd4, 0x7e, 0x56, 0x1e, - 0x58, 0xc3, 0x02, 0xcb, 0x44, 0x4d, 0xd7, 0x75, - 0xd5, 0x84, 0xae, 0x9b, 0xf9, 0xfa, 0x22, 0xf6, - 0x75, 0x45, 0xe3, 0xca, 0x92, 0x8d, 0xd5, 0x75, - 0x79, 0xc6, 0x28, 0x67, 0x4e, 0x0b, 0x2f, 0xb6, - 0xba, 0x02, 0x5f, 0x65, 0x10, 0x61, 0xdb, 0x92, - 0x45, 0x47, 0xe0, 0x0d, 0x19, 0x16, 0x5e, 0xb8, - 0x19, 0xc5, 0xed, 0x95, 0x20, 0x1e, 0x3d, 0xc6, - 0xfc, 0x47, 0xa7, 0x9c, 0xe1, 0xc0, 0xe1, 0xdd, - 0x4f, 0x8a, 0xfd, 0x56, 0x6c, 0x7e, 0x86, 0x30, - 0xa1, 0xa3, 0x87, 0xf2, 0x5e, 0x6c, 0x05, 0x01, - 0xef, 0x9a, 0x85, 0xfa, 0xa6, 0x96, 0xe2, 0x94, - 0xaf, 0xa0, 0x35, 0xc4, 0x8e, 0xf2, 0xda, 0xd0, - 0x58, 0x76, 0xca, 0xbf, 0x4e, 0x9c, 0x54, 0x51, - 0xf6, 0x45, 0x35, 0x1f, 0x87, 0x55, 0x52, 0x87, - 0xb5, 0xe8, 0x76, 0x6d, 0x58, 0x8b, 0xaa, 0xec, - 0x0d, 0x8e, 0x65, 0xae, 0x20, 0x9f, 0xe8, 0xdb, - 0xeb, 0x02, 0x1c, 0x9b, 0x20, 0xe8, 0xd3, 0x06, - 0xa6, 0xa2, 0x38, 0x5e, 0xb4, 0x96, 0xaa, 0x23, - 0x19, 0xb4, 0x3f, 0x4e, 0x73, 0xa4, 0x37, 0x56, - 0xbb, 0x6c, 0x74, 0x3e, 0xd7, 0x47, 0x33, 0xfe, - 0xdb, 0xb7, 0xa5, 0x1a, 0x4d, 0x68, 0xb6, 0xe4, - 0x62, 0x44, 0x0f, 0xf9, 0xa1, 0xe5, 0xaa, 0x05, - 0x6e, 0xd6, 0x4b, 0x23, 0x77, 0x42, 0x3b, 0xbb, - 0x1a, 0x9c, 0x99, 0x64, 0x11, 0xc0, 0x6c, 0x30, - 0x8e, 0x7c, 0x8a, 0xe9, 0x97, 0x0a, 0x02, 0x2b, - 0xa3, 0x59, 0x40, 0x62, 0xe9, 0x5d, 0xb4, 0x68, - 0x2c, 0x68, 0x82, 0xc7, 0xec, 0x6f, 0x2e, 0x78, - 0x38, 0x1d, 0x9f, 0xd8, 0xdd, 0x17, 0xf2, 0xd0, - 0xdb, 0xa6, 0x27, 0x69, 0x12, 0x39, 0xf9, 0x64, - 0x31, 0x0a, 0x6f, 0x2e, 0x2d, 0xde, 0xe2, 0x0b, - 0x4e, 0x5b, 0xda, 0x97, 0x57, 0x1c, 0xaa, 0xd1, - 0x5e, 0x09, 0x9d, 0x2b, 0xf2, 0x46, 0xcb, 0xdf, - 0xe5, 0xbd, 0x43, 0x5b, 0x3d, 0x1e, 0xfa, 0xc2, - 0xb5, 0x61, 0x69, 0x2e, 0xe1, 0x03, 0x74, 0xa6, - 0xe4, 0x5a, 0x5c, 0x57, 0x37, 0x37, 0x5c, 0x7e, - 0xb9, 0xa2, 0xbb, 0x2d, 0x9d, 0xbd, 0x8b, 0xd3, - 0xef, 0x28, 0xbd, 0x13, 0x0d, 0xc2, 0xff, 0x8b, - 0x33, 0xd8, 0x09, 0x01, 0xdc, 0x1f, 0x5d, 0x84, - 0x42, 0x4f, 0x5e, 0xbe, 0x47, 0xcf, 0x36, 0x29, - 0xac, 0x65, 0x4a, 0xf6, 0x44, 0xe5, 0x14, 0x8e, - 0x76, 0xcc, 0xdb, 0x39, 0xd5, 0xb5, 0xb2, 0x64, - 0x9d, 0x4e, 0x8c, 0xba, 0xcb, 0x89, 0x3b, 0xfd, - 0x30, 0x46, 0x14, 0xa2, 0x4b, 0xf8, 0x8f, 0xb2, - 0x65, 0xe3, 0x33, 0x49, 0x3e, 0xe0, 0x8e, 0x39, - 0x1b, 0x18, 0x7d, 0x05, 0xdc, 0x8a, 0x08, 0x88, - 0x00, 0x8b, 0xe8, 0xd1, 0x22, 0xdd, 0x08, 0xe0, - 0xfb, 0xc3, 0x75, 0x4a, 0x86, 0x49, 0x29, 0x47, - 0x5a, 0x28, 0x16, 0xd5, 0xed, 0xec, 0x18, 0xcb, - 0xde, 0x12, 0x89, 0x10, 0xb4, 0x16, 0xbd, 0xda, - 0xfc, 0x1f, 0x10, 0x1c, 0xce, 0x40, 0x61, 0xef, - 0x28, 0x10, 0xed, 0x0e, 0xd6, 0x13, 0x56, 0x3d, - 0x7a, 0xc8, 0x59, 0xae, 0x27, 0xde, 0xed, 0x4c, - 0x0f, 0xca, 0xf4, 0x50, 0x7b, 0xf9, 0x1f, 0x46, - 0x51, 0xd3, 0x43, 0x19, 0x53, 0xde, 0xf0, 0x5a, - 0xc6, 0x54, 0xaa, 0x7c, 0x32, 0x63, 0x17, 0x37, - 0x78, 0x76, 0xb8, 0xd5, 0x1a, 0x5d, 0x16, 0x16, - 0x5e, 0x94, 0x34, 0x61, 0x6d, 0xca, 0x1b, 0xb0, - 0x36, 0x95, 0x2a, 0xb1, 0xb6, 0x8b, 0x1b, 0xb0, - 0x76, 0xab, 0x7d, 0x01, 0x6f, 0x6b, 0x68, 0x40, - 0x0d, 0x8f, 0x33, 0x4c, 0x79, 0x3d, 0xae, 0xa6, - 0x4e, 0x25, 0xba, 0x76, 0x71, 0x03, 0xc6, 0x6e, - 0xb5, 0xab, 0x91, 0xae, 0x7f, 0x52, 0xa5, 0x8b, - 0xaf, 0x40, 0xb9, 0xf6, 0x71, 0x95, 0x55, 0x7a, - 0x15, 0xc2, 0x05, 0x0f, 0xa2, 0x87, 0x67, 0x9e, - 0xba, 0x27, 0xa3, 0xe2, 0xb5, 0x8d, 0x9c, 0xd5, - 0x56, 0xc8, 0x6d, 0x6d, 0x69, 0x78, 0x3a, 0xbb, - 0x77, 0x80, 0xfb, 0xf2, 0xeb, 0xf0, 0x3a, 0x56, - 0x34, 0x8b, 0x1f, 0x92, 0x7c, 0xaa, 0xff, 0xac, - 0x7c, 0xdc, 0x63, 0x8a, 0x3b, 0x05, 0x5e, 0x59, - 0xf7, 0xcc, 0xc7, 0x05, 0x58, 0xc9, 0xf3, 0x1a, - 0xb0, 0xb3, 0xf8, 0x1e, 0xc1, 0xd5, 0x7f, 0x56, - 0x62, 0x67, 0x8a, 0x3b, 0x05, 0x9e, 0x58, 0x87, - 0x9d, 0x0b, 0xb0, 0x92, 0xb7, 0x7d, 0x26, 0x8f, - 0x82, 0x2c, 0x2e, 0xa6, 0x67, 0xaa, 0xfe, 0x75, - 0x89, 0x29, 0xee, 0x14, 0x38, 0x5c, 0x1d, 0x5e, - 0x2e, 0xc0, 0x4a, 0x56, 0x75, 0x15, 0x76, 0xda, - 0x5b, 0x4d, 0xff, 0x55, 0x8f, 0x9b, 0x7a, 0xc7, - 0x65, 0x31, 0xb2, 0x46, 0xcc, 0xb4, 0xd7, 0x5b, - 0x15, 0x4b, 0xfa, 0x4c, 0xed, 0xb6, 0x45, 0xb3, - 0xed, 0xce, 0x6e, 0xfb, 0x27, 0xb3, 0xdb, 0xbe, - 0x92, 0xb1, 0x5d, 0xf0, 0xac, 0x44, 0x91, 0x57, - 0x9a, 0x17, 0x4d, 0xc6, 0xd5, 0x25, 0xbe, 0x6d, - 0x8a, 0xbc, 0xcb, 0x64, 0x4d, 0xaf, 0x3b, 0xa6, - 0x89, 0x72, 0x5f, 0xe4, 0x7a, 0x19, 0x6c, 0xad, - 0xa0, 0x63, 0xa5, 0x1c, 0x9d, 0x2c, 0x28, 0xb7, - 0x68, 0xc6, 0x20, 0xb5, 0xe3, 0x90, 0xc6, 0x45, - 0xc5, 0x09, 0x48, 0x83, 0x88, 0xdf, 0x18, 0x81, - 0x64, 0xaf, 0x33, 0x21, 0x13, 0x34, 0xfb, 0x89, - 0x08, 0x25, 0x56, 0x0e, 0xd0, 0x81, 0x09, 0x9d, - 0x31, 0x64, 0x28, 0x1a, 0xf4, 0xc6, 0xe7, 0xd4, - 0xa7, 0xfe, 0xa5, 0x4e, 0xc0, 0x8a, 0x07, 0x94, - 0x7c, 0x0f, 0x63, 0x3b, 0xc6, 0xd3, 0x01, 0xda, - 0xab, 0x19, 0x2d, 0x86, 0x89, 0xa9, 0x1e, 0x30, - 0x45, 0x91, 0xd1, 0xc3, 0x8e, 0x0b, 0x18, 0x5e, - 0x67, 0xbc, 0x91, 0x97, 0xce, 0x0b, 0xc3, 0xc5, - 0x72, 0x49, 0x4a, 0xa0, 0xa9, 0xd0, 0xfb, 0x14, - 0xcb, 0x11, 0x73, 0xd9, 0xdb, 0x6e, 0xe0, 0x94, - 0x17, 0xfd, 0xea, 0x51, 0xff, 0x39, 0x4c, 0xee, - 0xdb, 0x98, 0xdb, 0x6f, 0xdf, 0xd4, 0xfe, 0x19, - 0x99, 0xd9, 0x99, 0xa5, 0x48, 0x1a, 0x25, 0x5a, - 0x7f, 0xac, 0x00, 0x75, 0xbd, 0x28, 0x9c, 0xc7, - 0x78, 0x56, 0x1c, 0x23, 0xf1, 0x3f, 0x26, 0xaf, - 0x3a, 0x58, 0x91, 0xf0, 0x03, 0x60, 0xeb, 0x45, - 0x5c, 0x1b, 0xf3, 0x99, 0x53, 0xb2, 0xe1, 0x84, - 0x5d, 0xec, 0xe8, 0xe5, 0x8a, 0xdd, 0xc1, 0x9b, - 0x04, 0xe4, 0xb0, 0x39, 0xea, 0xc7, 0xb8, 0x65, - 0x88, 0x56, 0xf9, 0xe4, 0x09, 0x63, 0xc3, 0x7d, - 0xe4, 0xf6, 0xa1, 0xed, 0x95, 0x7a, 0x1b, 0x0c, - 0x1b, 0xb2, 0xc0, 0x5d, 0x65, 0xf5, 0xa6, 0xdc, - 0x90, 0xb1, 0x17, 0x1b, 0xbe, 0x6c, 0x29, 0x41, - 0x89, 0x30, 0xe7, 0xe7, 0x93, 0xcb, 0xc0, 0xa3, - 0xb0, 0x4f, 0x80, 0x4d, 0x6d, 0x7f, 0xbd, 0xdd, - 0x15, 0xc2, 0xee, 0x0a, 0xe1, 0x8b, 0xbb, 0x42, - 0x30, 0x5c, 0xc5, 0x7d, 0x2d, 0xe6, 0x3c, 0x34, - 0xb3, 0x25, 0xca, 0xcf, 0xe2, 0xb2, 0x41, 0x3d, - 0xe2, 0xd5, 0xa6, 0xbe, 0xe9, 0x2d, 0xdc, 0x3e, - 0xc8, 0x6b, 0x0c, 0x52, 0x67, 0xd5, 0xe3, 0x30, - 0x7c, 0x17, 0x7b, 0x41, 0xa1, 0xb9, 0x4c, 0x3c, - 0x32, 0x56, 0x6d, 0x95, 0x1f, 0x9b, 0x35, 0xad, - 0x1d, 0xc1, 0xa1, 0xc0, 0x6a, 0xf1, 0xe2, 0x2b, - 0x0e, 0x15, 0xdf, 0xe8, 0xec, 0xbb, 0xef, 0xc7, - 0xaf, 0xff, 0x29, 0x8e, 0xac, 0xd0, 0x51, 0x2f, - 0xbf, 0xfb, 0xe1, 0xe5, 0xf8, 0x87, 0x17, 0x7f, - 0xff, 0xfb, 0x0b, 0x98, 0x8c, 0xe1, 0xe0, 0xb0, - 0x5f, 0x7e, 0xfb, 0x50, 0x65, 0x7c, 0x40, 0x55, - 0xe3, 0x64, 0x0b, 0x3b, 0xe3, 0x7d, 0x5b, 0x08, - 0xef, 0xd5, 0xb6, 0x77, 0x7f, 0xc6, 0xb9, 0x7b, - 0xb6, 0xab, 0xdd, 0x89, 0x51, 0xac, 0xf2, 0x7e, - 0xad, 0xda, 0x80, 0xe4, 0xbc, 0xb3, 0xa9, 0xb3, - 0x7c, 0xdd, 0x86, 0x51, 0xe8, 0x4a, 0x9b, 0xd0, - 0xcd, 0xac, 0x39, 0x57, 0x1b, 0x73, 0x6e, 0x64, - 0x86, 0xb9, 0xeb, 0xf7, 0x6b, 0x05, 0x3b, 0xca, - 0xcd, 0x2c, 0x20, 0x57, 0x19, 0x40, 0x6e, 0x66, - 0xbd, 0xe0, 0x33, 0x42, 0x2e, 0xb2, 0x36, 0x3a, - 0x54, 0x1d, 0x33, 0x72, 0xc9, 0x7b, 0x17, 0x45, - 0x23, 0x82, 0x55, 0x76, 0x59, 0x3a, 0x23, 0xcc, - 0x8c, 0xc9, 0x3a, 0x45, 0xef, 0xb1, 0x6b, 0x4b, - 0xc8, 0xca, 0xde, 0x6c, 0x3c, 0xd4, 0x6d, 0x8a, - 0x29, 0xca, 0x26, 0xfc, 0x12, 0xea, 0xd4, 0x66, - 0xc7, 0x7d, 0xd1, 0x32, 0x9d, 0xee, 0xb9, 0xbb, - 0x42, 0x1e, 0x0c, 0xf6, 0xd3, 0xc7, 0xf5, 0x6c, - 0x86, 0x9e, 0xd8, 0xa3, 0xc3, 0xa7, 0xf4, 0x60, - 0x9f, 0xe3, 0x8a, 0x00, 0x62, 0xf8, 0xb7, 0x2d, - 0x8c, 0x4b, 0xe6, 0x5d, 0xf2, 0xba, 0x87, 0xc6, - 0x36, 0xed, 0xa9, 0x32, 0x85, 0x58, 0xcb, 0x2c, - 0xd6, 0x1e, 0x1c, 0xf4, 0xc3, 0xe7, 0x00, 0x5b, - 0x1e, 0x27, 0x4f, 0x2c, 0xac, 0xdb, 0xdb, 0xa0, - 0x4d, 0x51, 0x8c, 0x24, 0xbe, 0x8c, 0xad, 0x45, - 0x5f, 0x7d, 0x4b, 0xae, 0xd0, 0x7d, 0x38, 0x52, - 0xa1, 0x35, 0x6a, 0xf9, 0x56, 0xad, 0xf2, 0xa2, - 0x19, 0xaf, 0x99, 0xd5, 0x7b, 0x81, 0xd2, 0x54, - 0x33, 0x3e, 0x53, 0x0a, 0xa5, 0x81, 0x03, 0xdc, - 0x13, 0xa5, 0xb7, 0x7d, 0x8e, 0xe8, 0x28, 0xaf, - 0xbf, 0xad, 0x57, 0x6e, 0x34, 0x80, 0xae, 0x03, - 0xad, 0xc3, 0xa3, 0x7a, 0x52, 0xf8, 0x78, 0x83, - 0xe7, 0x6c, 0x28, 0x5d, 0xb9, 0xa7, 0xf8, 0x2d, - 0x5c, 0x93, 0xff, 0x7f, 0xc9, 0x73, 0xe5, 0xd5 + 0x91, 0xcf, 0x31, 0xa6, 0xa5, 0x93, 0xc2, 0x8b, + 0xc0, 0xe9, 0x27, 0x2c, 0x6e, 0x0d, 0x09, 0x8d, + 0x40, 0x6d, 0x57, 0x4d, 0x03, 0xbe, 0x56, 0x57, + 0x92, 0x29, 0xda, 0xf0, 0x9b, 0xeb, 0x54, 0x5d, + 0xef, 0xbc, 0x26, 0x07, 0xfe, 0x6d, 0x74, 0x50, + 0x7b, 0xc9, 0x0d, 0xf7, 0x04, 0x96, 0xb9, 0x48, + 0x0c, 0xf7, 0x04, 0x48, 0x2e, 0xe8, 0xde, 0x46, + 0x6b, 0xb0, 0x67, 0x01, 0x39, 0xc6, 0x93, 0x93, + 0x2a, 0xaa, 0x8b, 0xcc, 0x34, 0xd1, 0xfc, 0xd0, + 0xe3, 0x6b, 0xc8, 0x52, 0x37, 0x0a, 0x2a, 0xb3, + 0x4e, 0xc2, 0xd2, 0xdd, 0xf6, 0xe2, 0x1b, 0x23, + 0xb1, 0xd6, 0xa2, 0xd4, 0x6f, 0x62, 0xf7, 0xe2, + 0x58, 0x43, 0x68, 0xaa, 0xd5, 0xaf, 0x87, 0x5f, + 0x9e, 0x1d, 0xeb, 0x56, 0x45, 0x63, 0xce, 0x26, + 0x29, 0xdd, 0x17, 0xfe, 0x8f, 0xed, 0x2f, 0x7c, + 0xff, 0xe7, 0x8e, 0xb4, 0x53, 0xa8, 0x07, 0xab, + 0x00, 0xf3, 0xf7, 0x3a, 0x0d, 0xce, 0x69, 0xee, + 0xd0, 0x0d, 0x17, 0x97, 0x75, 0x2a, 0xe2, 0xc0, + 0x4b, 0xbb, 0xb3, 0x30, 0x88, 0xd4, 0x7b, 0x85, + 0x8c, 0x2d, 0x87, 0x78, 0xc9, 0x35, 0xed, 0x63, + 0x3d, 0x24, 0x31, 0x3c, 0x98, 0xb5, 0x86, 0xa2, + 0xfe, 0x77, 0x60, 0x6c, 0x5b, 0x44, 0x2b, 0x20, + 0x8b, 0x97, 0xf0, 0x76, 0xb6, 0xed, 0x0c, 0x64, + 0xc9, 0x33, 0x79, 0xe1, 0xa4, 0xd9, 0xb2, 0xf8, + 0x86, 0x5b, 0x03, 0xb1, 0x1f, 0xd0, 0x05, 0xb6, + 0xe4, 0xb9, 0x25, 0x1e, 0xc1, 0x6b, 0x4d, 0xc7, + 0x25, 0x1b, 0x3d, 0x06, 0x6a, 0x6b, 0xf0, 0xf6, + 0x73, 0x0e, 0x52, 0x9b, 0xd8, 0xb7, 0x26, 0x1c, + 0x4d, 0xdb, 0xcc, 0xe5, 0x5e, 0x6f, 0x45, 0xe1, + 0x2a, 0x49, 0xde, 0x50, 0x26, 0xf7, 0xb3, 0x32, + 0xc1, 0x1a, 0x26, 0x58, 0x26, 0x6a, 0xba, 0xb0, + 0xab, 0x26, 0x74, 0xdd, 0xcc, 0xd7, 0x57, 0xb1, + 0xaf, 0x2b, 0x1a, 0x57, 0x96, 0x6c, 0xac, 0xae, + 0xcb, 0x33, 0x46, 0x59, 0x73, 0x5a, 0x78, 0xb5, + 0xd5, 0x15, 0xf8, 0x2e, 0x83, 0x08, 0xdb, 0x96, + 0x2d, 0x3a, 0x02, 0xef, 0xc8, 0xb0, 0xf0, 0xc2, + 0xcd, 0x29, 0x6e, 0xaf, 0x04, 0x71, 0xe9, 0x31, + 0x66, 0x40, 0x3a, 0xe5, 0x1c, 0x07, 0x0e, 0xf7, + 0x7e, 0x52, 0xec, 0xb7, 0x62, 0xf3, 0x33, 0x84, + 0x09, 0x1d, 0x3e, 0x94, 0xf9, 0x62, 0x2b, 0x08, + 0x78, 0xdb, 0x2c, 0xd4, 0x37, 0xb5, 0x14, 0xa7, + 0x7c, 0x09, 0xad, 0x21, 0x76, 0x94, 0xdf, 0x86, + 0xc6, 0xb2, 0x53, 0xfe, 0x75, 0xe2, 0x24, 0x8b, + 0xb2, 0xaf, 0xaa, 0xf9, 0x40, 0xac, 0x92, 0x3b, + 0xac, 0x45, 0xb7, 0x6b, 0xc3, 0x5a, 0x54, 0xe5, + 0x6f, 0x70, 0x6c, 0x73, 0x05, 0x09, 0x45, 0xdf, + 0x5f, 0x17, 0xe0, 0xd8, 0x04, 0x41, 0x9f, 0x36, + 0x30, 0x15, 0xc5, 0xf1, 0xa2, 0xbd, 0x54, 0x1d, + 0xca, 0xa0, 0xff, 0x71, 0xa2, 0x23, 0xbd, 0xb1, + 0xda, 0x65, 0xb3, 0xf3, 0xb9, 0x3e, 0x9c, 0xf1, + 0xdf, 0xbe, 0x2d, 0xd7, 0x68, 0x42, 0xb3, 0x65, + 0x17, 0x23, 0x7c, 0xc8, 0x0f, 0x2d, 0x57, 0x31, + 0x70, 0xf3, 0x5e, 0x1a, 0xc9, 0x13, 0xda, 0xd9, + 0xd5, 0xe0, 0xd4, 0x24, 0x9b, 0x00, 0xe6, 0x83, + 0x71, 0x24, 0x54, 0x4c, 0xc0, 0x54, 0x10, 0x59, + 0x19, 0xcd, 0x02, 0x12, 0x4b, 0xef, 0xa2, 0x45, + 0x63, 0x41, 0x23, 0x3c, 0xe6, 0x7f, 0x73, 0xc1, + 0xc3, 0xf1, 0xf8, 0xc4, 0xee, 0xbe, 0x90, 0x89, + 0xde, 0x36, 0x3e, 0x49, 0xa3, 0xc8, 0xc9, 0x27, + 0x8b, 0x52, 0x78, 0x73, 0x79, 0xf1, 0x16, 0xdf, + 0x70, 0xda, 0xf2, 0xbe, 0xbc, 0xe4, 0x50, 0x8d, + 0xf6, 0x4a, 0xe8, 0x5c, 0x91, 0x39, 0x5a, 0xfe, + 0x2e, 0x6f, 0x1e, 0xda, 0xea, 0xf9, 0xd0, 0x17, + 0xae, 0x0f, 0x4b, 0x83, 0x09, 0x1f, 0xa0, 0x33, + 0x25, 0xd9, 0xe2, 0xba, 0xba, 0xd9, 0xe1, 0xf2, + 0xcb, 0x15, 0xdd, 0x6e, 0xe9, 0xfc, 0x5d, 0x9c, + 0x80, 0x47, 0x69, 0x9e, 0x68, 0x12, 0xfe, 0x5f, + 0x9c, 0xc1, 0x4e, 0x08, 0xe0, 0xfe, 0xe8, 0x22, + 0x14, 0x7a, 0xf4, 0xf2, 0x3d, 0xfa, 0xb6, 0x49, + 0x61, 0x2d, 0x53, 0xd2, 0x27, 0xaa, 0xa7, 0x70, + 0xb4, 0x63, 0xe6, 0xce, 0xa9, 0xae, 0x95, 0x25, + 0xeb, 0x74, 0x62, 0x14, 0x5e, 0x4e, 0xdd, 0xe9, + 0x87, 0x31, 0xa2, 0x10, 0x5d, 0xc2, 0x7f, 0x94, + 0x35, 0x1b, 0x1f, 0x4a, 0xf2, 0x01, 0x77, 0xcc, + 0xf9, 0xc0, 0xe8, 0x2b, 0xe0, 0x56, 0x44, 0x40, + 0x04, 0x58, 0x44, 0xcf, 0x16, 0xe9, 0x4e, 0x00, + 0x5f, 0x20, 0xae, 0x53, 0x32, 0x4d, 0x4a, 0x39, + 0xd2, 0x42, 0xb1, 0xa8, 0x70, 0x67, 0xc7, 0x58, + 0xf6, 0x96, 0x48, 0x84, 0xa0, 0xb5, 0xe8, 0xdd, + 0xe6, 0xff, 0x80, 0xe0, 0x70, 0x06, 0x2a, 0x7b, + 0x47, 0x81, 0x68, 0x77, 0xb0, 0x9e, 0xb0, 0xea, + 0xd1, 0x53, 0xce, 0x72, 0x3d, 0xf1, 0x6e, 0x67, + 0x7c, 0x50, 0xc6, 0x87, 0xda, 0xeb, 0xff, 0x30, + 0x8a, 0x9a, 0x9e, 0xca, 0x98, 0xf2, 0x86, 0xf7, + 0x32, 0xa6, 0x52, 0xe5, 0xa3, 0x19, 0xbb, 0xb8, + 0xc1, 0xb7, 0xc3, 0xad, 0xd6, 0xe8, 0xb4, 0xb0, + 0xf0, 0xa2, 0xa4, 0x09, 0x6b, 0x53, 0xde, 0x80, + 0xb5, 0xa9, 0x54, 0x89, 0xb5, 0x5d, 0xdc, 0x80, + 0xb5, 0x5b, 0xed, 0x0b, 0x78, 0x5d, 0x43, 0x03, + 0x6a, 0x78, 0x9e, 0x61, 0xca, 0xeb, 0x71, 0x35, + 0x75, 0x2a, 0xd1, 0xb5, 0x8b, 0x1b, 0x30, 0x76, + 0xab, 0x5d, 0x8d, 0x74, 0xfd, 0xa3, 0x2a, 0x5d, + 0x7c, 0x05, 0xca, 0xb5, 0xcf, 0xab, 0xac, 0xd2, + 0xab, 0x10, 0x2e, 0xf8, 0x10, 0x3d, 0x3c, 0x03, + 0xd5, 0x3d, 0x99, 0x15, 0xaf, 0x6d, 0xe6, 0xac, + 0xb6, 0x43, 0x6e, 0x6b, 0x4d, 0xc3, 0xd3, 0xd9, + 0xbd, 0x05, 0xdc, 0x97, 0x5f, 0x87, 0xd7, 0xb1, + 0xa3, 0x59, 0xfc, 0x90, 0xe4, 0x53, 0xfd, 0x67, + 0xe5, 0xf3, 0x1e, 0x53, 0xdc, 0x29, 0xf0, 0xca, + 0xba, 0x87, 0x3e, 0x2e, 0xc0, 0x4a, 0x9e, 0xd7, + 0x80, 0x9d, 0xc5, 0xf7, 0x08, 0xae, 0xfe, 0xb3, + 0x12, 0x3b, 0x53, 0xdc, 0x29, 0xf0, 0xc4, 0x3a, + 0xec, 0x5c, 0x80, 0x95, 0xbc, 0xed, 0x33, 0x79, + 0x16, 0x64, 0x71, 0x31, 0x3d, 0x53, 0xf5, 0xef, + 0x4b, 0x4c, 0x71, 0xa7, 0xc0, 0xe1, 0xea, 0xf0, + 0x72, 0x01, 0x56, 0xb2, 0xaa, 0xab, 0xb0, 0xd3, + 0xfe, 0x6a, 0xfa, 0xaf, 0x7a, 0xdc, 0xd4, 0x4b, + 0x2e, 0x8b, 0x91, 0x35, 0x62, 0xa6, 0xfd, 0xde, + 0xaa, 0x58, 0xd2, 0x67, 0x6a, 0xb9, 0x2d, 0x1a, + 0x6e, 0x77, 0x96, 0xdb, 0x3f, 0x9d, 0xe5, 0xf6, + 0x95, 0x8c, 0xef, 0x82, 0xa7, 0x25, 0x0a, 0xbd, + 0xd2, 0xc0, 0x68, 0xb2, 0xae, 0x2e, 0xf1, 0x7d, + 0x53, 0xe4, 0x5d, 0x26, 0x6b, 0x7a, 0xe1, 0x31, + 0x4d, 0x94, 0x0b, 0x23, 0xd7, 0xcb, 0x60, 0x73, + 0x05, 0x1d, 0x2b, 0xed, 0xe8, 0x64, 0x41, 0xf9, + 0x45, 0x33, 0x06, 0xa9, 0x9d, 0x87, 0x34, 0x2e, + 0x2a, 0x56, 0x40, 0x1a, 0x44, 0xfc, 0xce, 0x08, + 0x64, 0x7b, 0x9d, 0x0d, 0x99, 0xa0, 0xd9, 0xcf, + 0x44, 0x28, 0xb9, 0x72, 0x80, 0x4e, 0x4c, 0xe8, + 0x90, 0x21, 0xc3, 0xd1, 0xa0, 0x47, 0x3e, 0xa7, + 0x3f, 0xf5, 0x2f, 0x75, 0x12, 0x56, 0x3c, 0xa2, + 0xe4, 0x9b, 0x18, 0xdb, 0x39, 0x9e, 0x8e, 0xd0, + 0x5e, 0xcd, 0x68, 0x31, 0x54, 0x4c, 0xf5, 0x80, + 0x29, 0x92, 0x8c, 0x1e, 0x76, 0x5c, 0xc0, 0xf0, + 0x3a, 0xe3, 0x8d, 0xbc, 0x74, 0x5e, 0x18, 0x2e, + 0x96, 0x4b, 0x52, 0x02, 0x5d, 0x85, 0xde, 0xa8, + 0x58, 0xce, 0x98, 0xcb, 0xde, 0x76, 0x03, 0xa7, + 0xdc, 0xe8, 0x57, 0x8f, 0xfa, 0xcf, 0x61, 0x74, + 0xdf, 0xc6, 0xe0, 0x7e, 0xfb, 0xc6, 0xf6, 0xcf, + 0xc8, 0xd0, 0xce, 0x2c, 0x45, 0xd2, 0x28, 0xd1, + 0xfa, 0x63, 0x05, 0xa8, 0xeb, 0x45, 0xe1, 0x3c, + 0xc6, 0xd3, 0xe2, 0x18, 0x89, 0xff, 0x31, 0x79, + 0xd6, 0xc1, 0x8a, 0x84, 0x1f, 0x00, 0x5b, 0x2f, + 0xe2, 0xda, 0x98, 0xd3, 0x9c, 0x12, 0x0e, 0x27, + 0xec, 0x66, 0x47, 0xaf, 0x57, 0xec, 0x0e, 0xde, + 0x24, 0x20, 0x89, 0xcd, 0x51, 0x43, 0xc6, 0x2d, + 0x43, 0xb4, 0xca, 0x67, 0x4f, 0x18, 0x1b, 0xee, + 0x23, 0xb7, 0x0f, 0x6d, 0xaf, 0xd4, 0xdb, 0x60, + 0xe8, 0x90, 0x05, 0xee, 0x2a, 0xab, 0x37, 0xe5, + 0x8a, 0x8c, 0xbd, 0xd8, 0xf0, 0x65, 0x4b, 0x09, + 0x4a, 0x84, 0x39, 0x3f, 0xa1, 0x5c, 0x06, 0x1e, + 0x85, 0x7e, 0x02, 0x6c, 0x6a, 0xfb, 0xeb, 0xed, + 0x2e, 0x11, 0x76, 0x97, 0x08, 0x5f, 0xdc, 0x25, + 0x82, 0xe1, 0x2a, 0xee, 0x8b, 0x31, 0xe7, 0xb1, + 0x99, 0x2d, 0x53, 0x7e, 0x16, 0xd7, 0x0d, 0xea, + 0x21, 0xaf, 0x36, 0xf6, 0x4d, 0x6f, 0xe1, 0xfe, + 0x41, 0x5e, 0x64, 0x90, 0x42, 0xab, 0x1e, 0x88, + 0xe1, 0xdb, 0xd8, 0x0b, 0x0a, 0xcf, 0x65, 0x62, + 0x92, 0xb1, 0x72, 0xab, 0x7c, 0xd9, 0xac, 0x69, + 0xed, 0x08, 0x0e, 0x07, 0x56, 0x8b, 0x17, 0x5f, + 0x72, 0xa8, 0x18, 0x47, 0x67, 0xdf, 0x7d, 0x3f, + 0x7e, 0xfd, 0x4f, 0x71, 0x64, 0x85, 0x8f, 0x7a, + 0xf9, 0xdd, 0x0f, 0x2f, 0xc7, 0x3f, 0xbc, 0xf8, + 0xfb, 0xdf, 0x5f, 0xc0, 0x64, 0x0c, 0x07, 0x87, + 0xfd, 0xf2, 0xfb, 0x87, 0x2a, 0xf3, 0x03, 0x2a, + 0x1b, 0x27, 0x5b, 0x58, 0x1a, 0xef, 0xdb, 0x46, + 0x78, 0xaf, 0xd6, 0xbd, 0xfb, 0x33, 0xcf, 0xdd, + 0xb3, 0x65, 0xed, 0x4e, 0xcc, 0x62, 0x95, 0x37, + 0x6c, 0xd5, 0x26, 0x24, 0xe7, 0xad, 0x4d, 0x9d, + 0xed, 0xeb, 0x36, 0xcc, 0x42, 0x57, 0x5a, 0x85, + 0x6e, 0x66, 0xcf, 0xb9, 0xda, 0x9c, 0x73, 0x23, + 0x43, 0xcc, 0x5d, 0xbf, 0x61, 0x2b, 0x58, 0x52, + 0x6e, 0x66, 0x03, 0xb9, 0xca, 0x04, 0x72, 0x33, + 0xfb, 0x05, 0x9f, 0x11, 0x72, 0x91, 0xb5, 0xd9, + 0xa1, 0xea, 0x98, 0x91, 0x4b, 0xde, 0xbb, 0x28, + 0x9a, 0x11, 0xac, 0xb2, 0xcb, 0xd2, 0x19, 0x61, + 0x66, 0x4c, 0xd6, 0x29, 0x7a, 0x90, 0x5d, 0x5b, + 0x42, 0x56, 0x16, 0x67, 0xe3, 0xa5, 0x6e, 0x53, + 0x4c, 0x51, 0x36, 0xe1, 0xd7, 0x50, 0xa7, 0x36, + 0x3b, 0xee, 0x8b, 0x96, 0xe9, 0x74, 0xcf, 0xdd, + 0x15, 0xf2, 0x60, 0xb0, 0x9f, 0x3f, 0xae, 0x67, + 0x33, 0xf4, 0xc6, 0x26, 0x33, 0x02, 0xc8, 0x52, + 0x1c, 0x5b, 0xc4, 0x31, 0x2b, 0x68, 0xd3, 0x05, + 0xce, 0x74, 0xc9, 0xf3, 0x1e, 0x1a, 0xdb, 0xb4, + 0xa7, 0xca, 0x14, 0x62, 0x2d, 0xb3, 0x58, 0x7b, + 0x70, 0xd0, 0x0f, 0x9f, 0x03, 0x6c, 0x79, 0x9c, + 0x3c, 0xb1, 0xb0, 0x6e, 0x6f, 0x83, 0x36, 0x45, + 0x32, 0x92, 0xf8, 0x32, 0xb6, 0x16, 0x7d, 0xf5, + 0x2d, 0xb9, 0x42, 0xf7, 0xe1, 0x48, 0x85, 0xd6, + 0xa8, 0xe5, 0x7b, 0xb5, 0xca, 0xab, 0x66, 0xbc, + 0x68, 0x56, 0x6f, 0x06, 0x4a, 0x53, 0xcd, 0xf8, + 0x4c, 0x29, 0x9c, 0x06, 0x0e, 0x70, 0x4f, 0x94, + 0xde, 0xf7, 0x39, 0xa2, 0xa3, 0xbc, 0x00, 0xb7, + 0x5e, 0xba, 0xd1, 0x00, 0xba, 0x0e, 0xb4, 0x0e, + 0x8f, 0xea, 0x49, 0xe1, 0xe3, 0x0d, 0x9e, 0xb4, + 0xa1, 0x74, 0xe5, 0x9e, 0xe2, 0xb7, 0x70, 0x51, + 0xfe, 0xff, 0x01, 0x25, 0x68, 0xe6, 0xc5 }; static std::string decompressed = util::decompress(std::string(reinterpret_cast<const char*>(compressed), sizeof(compressed))); return decompressed.c_str(); diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index 9e33b99def..db758b656b 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -8,7 +8,7 @@ namespace shaders { const char* symbol_icon::name = "symbol_icon"; const char* symbol_icon::vertexSource = source() + 59607; -const char* symbol_icon::fragmentSource = source() + 63011; +const char* symbol_icon::fragmentSource = source() + 63013; } // namespace shaders } // namespace mbgl diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index 3443b4f21c..de9d3729a3 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -7,8 +7,8 @@ namespace mbgl { namespace shaders { const char* symbol_sdf::name = "symbol_sdf"; -const char* symbol_sdf::vertexSource = source() + 63461; -const char* symbol_sdf::fragmentSource = source() + 69382; +const char* symbol_sdf::vertexSource = source() + 63463; +const char* symbol_sdf::fragmentSource = source() + 69386; } // namespace shaders } // namespace mbgl diff --git a/src/mbgl/style/conversion/color_ramp_property_value.cpp b/src/mbgl/style/conversion/color_ramp_property_value.cpp index f29438b6a2..0da16c67ee 100644 --- a/src/mbgl/style/conversion/color_ramp_property_value.cpp +++ b/src/mbgl/style/conversion/color_ramp_property_value.cpp @@ -1,6 +1,6 @@ #include <mbgl/style/conversion/color_ramp_property_value.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/style/expression/value.hpp> #include <mbgl/style/expression/is_constant.hpp> #include <mbgl/style/expression/is_expression.hpp> diff --git a/src/mbgl/style/conversion/constant.cpp b/src/mbgl/style/conversion/constant.cpp index d432b5f051..de4ab22269 100644 --- a/src/mbgl/style/conversion/constant.cpp +++ b/src/mbgl/style/conversion/constant.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { @@ -31,6 +32,38 @@ optional<std::string> Converter<std::string>::operator()(const Convertible& valu return *converted; } +template <class T> +optional<T> Converter<T, typename std::enable_if_t<std::is_enum<T>::value>>::operator()(const Convertible& value, Error& error) const { + optional<std::string> string = toString(value); + if (!string) { + error.message = "value must be a string"; + return nullopt; + } + + const auto result = Enum<T>::toEnum(*string); + if (!result) { + error.message = "value must be a valid enumeration value"; + return nullopt; + } + + return *result; +} + +template optional<AlignmentType> Converter<AlignmentType>::operator()(const Convertible&, Error&) const; +template optional<CirclePitchScaleType> Converter<CirclePitchScaleType>::operator()(const Convertible&, Error&) const; +template optional<HillshadeIlluminationAnchorType> Converter<HillshadeIlluminationAnchorType>::operator()(const Convertible&, Error&) const; +template optional<IconTextFitType> Converter<IconTextFitType>::operator()(const Convertible&, Error&) const; +template optional<LightAnchorType> Converter<LightAnchorType>::operator()(const Convertible&, Error&) const; +template optional<LineCapType> Converter<LineCapType>::operator()(const Convertible&, Error&) const; +template optional<LineJoinType> Converter<LineJoinType>::operator()(const Convertible&, Error&) const; +template optional<RasterResamplingType> Converter<RasterResamplingType>::operator()(const Convertible&, Error&) const; +template optional<SymbolAnchorType> Converter<SymbolAnchorType>::operator()(const Convertible&, Error&) const; +template optional<SymbolPlacementType> Converter<SymbolPlacementType>::operator()(const Convertible&, Error&) const; +template optional<TextJustifyType> Converter<TextJustifyType>::operator()(const Convertible&, Error&) const; +template optional<TextTransformType> Converter<TextTransformType>::operator()(const Convertible&, Error&) const; +template optional<TranslateAnchorType> Converter<TranslateAnchorType>::operator()(const Convertible&, Error&) const; +template optional<VisibilityType> Converter<VisibilityType>::operator()(const Convertible&, Error&) const; + optional<Color> Converter<Color>::operator()(const Convertible& value, Error& error) const { optional<std::string> string = toString(value); if (!string) { @@ -47,6 +80,29 @@ optional<Color> Converter<Color>::operator()(const Convertible& value, Error& er return *color; } +template <size_t N> +optional<std::array<float, N>> Converter<std::array<float, N>>::operator()(const Convertible& value, Error& error) const { + if (!isArray(value) || arrayLength(value) != N) { + error.message = "value must be an array of " + util::toString(N) + " numbers"; + return nullopt; + } + + std::array<float, N> result; + for (size_t i = 0; i < N; i++) { + optional<float> n = toNumber(arrayMember(value, i)); + if (!n) { + error.message = "value must be an array of " + util::toString(N) + " numbers"; + return nullopt; + } + result[i] = *n; + } + return result; +} + +template optional<std::array<float, 2>> Converter<std::array<float, 2>>::operator()(const Convertible&, Error&) const; +template optional<std::array<float, 3>> Converter<std::array<float, 3>>::operator()(const Convertible&, Error&) const; +template optional<std::array<float, 4>> Converter<std::array<float, 4>>::operator()(const Convertible&, Error&) const; + optional<std::vector<float>> Converter<std::vector<float>>::operator()(const Convertible& value, Error& error) const { if (!isArray(value)) { error.message = "value must be an array"; diff --git a/src/mbgl/style/conversion/coordinate.cpp b/src/mbgl/style/conversion/coordinate.cpp index 20abd45e26..ee03bffb30 100644 --- a/src/mbgl/style/conversion/coordinate.cpp +++ b/src/mbgl/style/conversion/coordinate.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/conversion/coordinate.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/conversion/custom_geometry_source_options.cpp b/src/mbgl/style/conversion/custom_geometry_source_options.cpp index 8983f9f479..491509c28f 100644 --- a/src/mbgl/style/conversion/custom_geometry_source_options.cpp +++ b/src/mbgl/style/conversion/custom_geometry_source_options.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/conversion/custom_geometry_source_options.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp index 5114c61778..fc25ab0b0d 100644 --- a/src/mbgl/style/conversion/filter.cpp +++ b/src/mbgl/style/conversion/filter.cpp @@ -1,10 +1,11 @@ #include <mbgl/style/conversion/filter.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/style/expression/literal.hpp> -#include <mbgl/util/geometry.hpp> #include <mbgl/style/expression/expression.hpp> #include <mbgl/style/expression/type.hpp> #include <mbgl/style/expression/compound_expression.hpp> #include <mbgl/style/expression/boolean_operator.hpp> +#include <mbgl/util/geometry.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/conversion/function.cpp b/src/mbgl/style/conversion/function.cpp index 1cc49e483a..6aadaad3b3 100644 --- a/src/mbgl/style/conversion/function.cpp +++ b/src/mbgl/style/conversion/function.cpp @@ -1,4 +1,6 @@ #include <mbgl/style/conversion/function.hpp> +#include <mbgl/style/conversion/position.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/style/expression/dsl.hpp> #include <mbgl/style/expression/step.hpp> #include <mbgl/style/expression/interpolate.hpp> @@ -70,6 +72,72 @@ std::unique_ptr<Expression> convertTokenStringToExpression(const std::string& so } } +template <class T> +optional<PropertyExpression<T>> convertFunctionToExpression(const Convertible& value, Error& error, bool convertTokens) { + auto expression = convertFunctionToExpression(expression::valueTypeToExpressionType<T>(), value, error, convertTokens); + if (!expression) { + return nullopt; + } + + optional<T> defaultValue; + + auto defaultValueValue = objectMember(value, "default"); + if (defaultValueValue) { + defaultValue = convert<T>(*defaultValueValue, error); + if (!defaultValue) { + error.message = R"(wrong type for "default": )" + error.message; + return nullopt; + } + } + + return PropertyExpression<T>(std::move(*expression), defaultValue); +} + +template optional<PropertyExpression<AlignmentType>> + convertFunctionToExpression<AlignmentType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<bool>> + convertFunctionToExpression<bool>(const Convertible&, Error&, bool); +template optional<PropertyExpression<CirclePitchScaleType>> + convertFunctionToExpression<CirclePitchScaleType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<float>> + convertFunctionToExpression<float>(const Convertible&, Error&, bool); +template optional<PropertyExpression<HillshadeIlluminationAnchorType>> + convertFunctionToExpression<HillshadeIlluminationAnchorType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<IconTextFitType>> + convertFunctionToExpression<IconTextFitType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<LightAnchorType>> + convertFunctionToExpression<LightAnchorType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<LineCapType>> + convertFunctionToExpression<LineCapType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<LineJoinType>> + convertFunctionToExpression<LineJoinType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<Color>> + convertFunctionToExpression<Color>(const Convertible&, Error&, bool); +template optional<PropertyExpression<Position>> + convertFunctionToExpression<Position>(const Convertible&, Error&, bool); +template optional<PropertyExpression<RasterResamplingType>> + convertFunctionToExpression<RasterResamplingType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<std::array<float, 2>>> + convertFunctionToExpression<std::array<float, 2>>(const Convertible&, Error&, bool); +template optional<PropertyExpression<std::array<float, 4>>> + convertFunctionToExpression<std::array<float, 4>>(const Convertible&, Error&, bool); +template optional<PropertyExpression<std::string>> + convertFunctionToExpression<std::string>(const Convertible&, Error&, bool); +template optional<PropertyExpression<std::vector<float>>> + convertFunctionToExpression<std::vector<float>>(const Convertible&, Error&, bool); +template optional<PropertyExpression<std::vector<std::string>>> + convertFunctionToExpression<std::vector<std::string>>(const Convertible&, Error&, bool); +template optional<PropertyExpression<SymbolAnchorType>> + convertFunctionToExpression<SymbolAnchorType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<SymbolPlacementType>> + convertFunctionToExpression<SymbolPlacementType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<TextJustifyType>> + convertFunctionToExpression<TextJustifyType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<TextTransformType>> + convertFunctionToExpression<TextTransformType>(const Convertible&, Error&, bool); +template optional<PropertyExpression<TranslateAnchorType>> + convertFunctionToExpression<TranslateAnchorType>(const Convertible&, Error&, bool); + // Ad-hoc Converters for double and int64_t. We should replace float with double wholesale, // and promote the int64_t Converter to general use (and it should check that the input is // an integer). diff --git a/src/mbgl/style/conversion/geojson.cpp b/src/mbgl/style/conversion/geojson.cpp index e39a1a80eb..c2d34c5491 100644 --- a/src/mbgl/style/conversion/geojson.cpp +++ b/src/mbgl/style/conversion/geojson.cpp @@ -1,5 +1,6 @@ #include <mbgl/style/conversion/geojson.hpp> #include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/conversion/geojson_options.cpp b/src/mbgl/style/conversion/geojson_options.cpp index 52a5030c34..11bd7cc507 100644 --- a/src/mbgl/style/conversion/geojson_options.cpp +++ b/src/mbgl/style/conversion/geojson_options.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/conversion/geojson_options.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { @@ -77,6 +78,16 @@ optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible } } + const auto lineMetricsValue = objectMember(value, "lineMetrics"); + if (lineMetricsValue) { + if (toBool(*lineMetricsValue)) { + options.lineMetrics = *toBool(*lineMetricsValue); + } else { + error = { "GeoJSON source lineMetrics value must be a boolean" }; + return nullopt; + } + } + return { options }; } diff --git a/src/mbgl/style/conversion/get_json_type.cpp b/src/mbgl/style/conversion/get_json_type.cpp index cd3b4608b1..2e9d35a957 100644 --- a/src/mbgl/style/conversion/get_json_type.cpp +++ b/src/mbgl/style/conversion/get_json_type.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/conversion/get_json_type.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/feature.hpp> namespace mbgl { diff --git a/src/mbgl/style/conversion/json.hpp b/src/mbgl/style/conversion/json.hpp index 7dd2378f6b..3a7bf2b557 100644 --- a/src/mbgl/style/conversion/json.hpp +++ b/src/mbgl/style/conversion/json.hpp @@ -1,4 +1,6 @@ -#include <mbgl/style/conversion.hpp> +#pragma once + +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/style/rapidjson_conversion.hpp> #include <string> diff --git a/src/mbgl/style/conversion/layer.cpp b/src/mbgl/style/conversion/layer.cpp index e18ad923f2..085d7ae4c6 100644 --- a/src/mbgl/style/conversion/layer.cpp +++ b/src/mbgl/style/conversion/layer.cpp @@ -1,7 +1,7 @@ #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/constant.hpp> #include <mbgl/style/conversion/filter.hpp> -#include <mbgl/style/conversion/make_property_setters.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/style/layers/circle_layer.hpp> #include <mbgl/style/layers/fill_layer.hpp> @@ -16,24 +16,6 @@ namespace mbgl { namespace style { namespace conversion { -optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value) { - static const auto setters = makeLayoutPropertySetters(); - auto it = setters.find(name); - if (it == setters.end()) { - return Error { "property not found" }; - } - return it->second(layer, value); -} - -optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value) { - static const auto setters = makePaintPropertySetters(); - auto it = setters.find(name); - if (it == setters.end()) { - return Error { "property not found" }; - } - return it->second(layer, value); -} - optional<Error> setPaintProperties(Layer& layer, const Convertible& value) { auto paintValue = objectMember(value, "paint"); if (!paintValue) { @@ -43,7 +25,7 @@ optional<Error> setPaintProperties(Layer& layer, const Convertible& value) { return { { "paint must be an object" } }; } return eachMember(*paintValue, [&] (const std::string& k, const Convertible& v) { - return setPaintProperty(layer, k, v); + return layer.setPaintProperty(k, v); }); } @@ -210,7 +192,7 @@ optional<std::unique_ptr<Layer>> Converter<std::unique_ptr<Layer>>::operator()(c return nullopt; } optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const Convertible& v) { - return setLayoutProperty(*layer, k, v); + return layer->setLayoutProperty(k, v); }); if (error_) { error = *error_; diff --git a/src/mbgl/style/conversion/light.cpp b/src/mbgl/style/conversion/light.cpp index 7b96c89a1c..e8723216ee 100644 --- a/src/mbgl/style/conversion/light.cpp +++ b/src/mbgl/style/conversion/light.cpp @@ -2,6 +2,7 @@ #include <mbgl/style/conversion/position.hpp> #include <mbgl/style/conversion/property_value.hpp> #include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/conversion/make_property_setters.hpp b/src/mbgl/style/conversion/make_property_setters.hpp deleted file mode 100644 index ada0d53002..0000000000 --- a/src/mbgl/style/conversion/make_property_setters.hpp +++ /dev/null @@ -1,239 +0,0 @@ -#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/heatmap_layer.hpp> -#include <mbgl/style/layers/fill_extrusion_layer.hpp> -#include <mbgl/style/layers/raster_layer.hpp> -#include <mbgl/style/layers/hillshade_layer.hpp> -#include <mbgl/style/layers/background_layer.hpp> - -#include <unordered_map> - -namespace mbgl { -namespace style { -namespace conversion { - -inline auto makeLayoutPropertySetters() { - std::unordered_map<std::string, PropertySetter> result; - - result["visibility"] = &setVisibility; - - - result["line-cap"] = &setProperty<LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap, false, false>; - result["line-join"] = &setProperty<LineLayer, PropertyValue<LineJoinType>, &LineLayer::setLineJoin, true, false>; - result["line-miter-limit"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit, false, false>; - result["line-round-limit"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit, false, false>; - - result["symbol-placement"] = &setProperty<SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement, false, false>; - result["symbol-spacing"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing, false, false>; - result["symbol-avoid-edges"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges, false, false>; - result["icon-allow-overlap"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap, false, false>; - result["icon-ignore-placement"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement, false, false>; - result["icon-optional"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional, false, false>; - result["icon-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment, false, false>; - result["icon-size"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconSize, true, false>; - result["icon-text-fit"] = &setProperty<SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit, false, false>; - result["icon-text-fit-padding"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding, false, false>; - result["icon-image"] = &setProperty<SymbolLayer, PropertyValue<std::string>, &SymbolLayer::setIconImage, true, true>; - result["icon-rotate"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconRotate, true, false>; - result["icon-padding"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding, false, false>; - result["icon-keep-upright"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright, false, false>; - result["icon-offset"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset, true, false>; - result["icon-anchor"] = &setProperty<SymbolLayer, PropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor, true, false>; - result["icon-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment, false, false>; - result["text-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment, false, false>; - result["text-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment, false, false>; - result["text-field"] = &setProperty<SymbolLayer, PropertyValue<std::string>, &SymbolLayer::setTextField, true, true>; - result["text-font"] = &setProperty<SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont, true, false>; - result["text-size"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextSize, true, false>; - result["text-max-width"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxWidth, true, false>; - result["text-line-height"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight, false, false>; - result["text-letter-spacing"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLetterSpacing, true, false>; - result["text-justify"] = &setProperty<SymbolLayer, PropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify, true, false>; - result["text-anchor"] = &setProperty<SymbolLayer, PropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor, true, false>; - result["text-max-angle"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle, false, false>; - result["text-rotate"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextRotate, true, false>; - result["text-padding"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding, false, false>; - result["text-keep-upright"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright, false, false>; - result["text-transform"] = &setProperty<SymbolLayer, PropertyValue<TextTransformType>, &SymbolLayer::setTextTransform, true, false>; - result["text-offset"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset, true, false>; - result["text-allow-overlap"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap, false, false>; - result["text-ignore-placement"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement, false, false>; - result["text-optional"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional, false, false>; - - - - - - - - return result; -} - -inline auto makePaintPropertySetters() { - std::unordered_map<std::string, PropertySetter> result; - - result["fill-antialias"] = &setProperty<FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias, false, false>; - result["fill-antialias-transition"] = &setTransition<FillLayer, &FillLayer::setFillAntialiasTransition>; - result["fill-opacity"] = &setProperty<FillLayer, PropertyValue<float>, &FillLayer::setFillOpacity, true, false>; - result["fill-opacity-transition"] = &setTransition<FillLayer, &FillLayer::setFillOpacityTransition>; - result["fill-color"] = &setProperty<FillLayer, PropertyValue<Color>, &FillLayer::setFillColor, true, false>; - result["fill-color-transition"] = &setTransition<FillLayer, &FillLayer::setFillColorTransition>; - result["fill-outline-color"] = &setProperty<FillLayer, PropertyValue<Color>, &FillLayer::setFillOutlineColor, true, false>; - result["fill-outline-color-transition"] = &setTransition<FillLayer, &FillLayer::setFillOutlineColorTransition>; - result["fill-translate"] = &setProperty<FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate, false, false>; - result["fill-translate-transition"] = &setTransition<FillLayer, &FillLayer::setFillTranslateTransition>; - result["fill-translate-anchor"] = &setProperty<FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor, false, false>; - result["fill-translate-anchor-transition"] = &setTransition<FillLayer, &FillLayer::setFillTranslateAnchorTransition>; - result["fill-pattern"] = &setProperty<FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern, false, false>; - result["fill-pattern-transition"] = &setTransition<FillLayer, &FillLayer::setFillPatternTransition>; - - result["line-opacity"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineOpacity, true, false>; - result["line-opacity-transition"] = &setTransition<LineLayer, &LineLayer::setLineOpacityTransition>; - result["line-color"] = &setProperty<LineLayer, PropertyValue<Color>, &LineLayer::setLineColor, true, false>; - result["line-color-transition"] = &setTransition<LineLayer, &LineLayer::setLineColorTransition>; - result["line-translate"] = &setProperty<LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate, false, false>; - result["line-translate-transition"] = &setTransition<LineLayer, &LineLayer::setLineTranslateTransition>; - result["line-translate-anchor"] = &setProperty<LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor, false, false>; - result["line-translate-anchor-transition"] = &setTransition<LineLayer, &LineLayer::setLineTranslateAnchorTransition>; - result["line-width"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineWidth, true, false>; - result["line-width-transition"] = &setTransition<LineLayer, &LineLayer::setLineWidthTransition>; - result["line-gap-width"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineGapWidth, true, false>; - result["line-gap-width-transition"] = &setTransition<LineLayer, &LineLayer::setLineGapWidthTransition>; - result["line-offset"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineOffset, true, false>; - result["line-offset-transition"] = &setTransition<LineLayer, &LineLayer::setLineOffsetTransition>; - result["line-blur"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineBlur, true, false>; - result["line-blur-transition"] = &setTransition<LineLayer, &LineLayer::setLineBlurTransition>; - result["line-dasharray"] = &setProperty<LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray, false, false>; - result["line-dasharray-transition"] = &setTransition<LineLayer, &LineLayer::setLineDasharrayTransition>; - result["line-pattern"] = &setProperty<LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern, false, false>; - result["line-pattern-transition"] = &setTransition<LineLayer, &LineLayer::setLinePatternTransition>; - - result["icon-opacity"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconOpacity, true, false>; - result["icon-opacity-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconOpacityTransition>; - result["icon-color"] = &setProperty<SymbolLayer, PropertyValue<Color>, &SymbolLayer::setIconColor, true, false>; - result["icon-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconColorTransition>; - result["icon-halo-color"] = &setProperty<SymbolLayer, PropertyValue<Color>, &SymbolLayer::setIconHaloColor, true, false>; - result["icon-halo-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloColorTransition>; - result["icon-halo-width"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconHaloWidth, true, false>; - result["icon-halo-width-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloWidthTransition>; - result["icon-halo-blur"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconHaloBlur, true, false>; - result["icon-halo-blur-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>; - result["icon-translate"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate, false, false>; - result["icon-translate-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconTranslateTransition>; - result["icon-translate-anchor"] = &setProperty<SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor, false, false>; - result["icon-translate-anchor-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>; - result["text-opacity"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextOpacity, true, false>; - result["text-opacity-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextOpacityTransition>; - result["text-color"] = &setProperty<SymbolLayer, PropertyValue<Color>, &SymbolLayer::setTextColor, true, false>; - result["text-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextColorTransition>; - result["text-halo-color"] = &setProperty<SymbolLayer, PropertyValue<Color>, &SymbolLayer::setTextHaloColor, true, false>; - result["text-halo-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloColorTransition>; - result["text-halo-width"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextHaloWidth, true, false>; - result["text-halo-width-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloWidthTransition>; - result["text-halo-blur"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextHaloBlur, true, false>; - result["text-halo-blur-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>; - result["text-translate"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate, false, false>; - result["text-translate-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextTranslateTransition>; - result["text-translate-anchor"] = &setProperty<SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor, false, false>; - result["text-translate-anchor-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>; - - result["circle-radius"] = &setProperty<CircleLayer, PropertyValue<float>, &CircleLayer::setCircleRadius, true, false>; - result["circle-radius-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleRadiusTransition>; - result["circle-color"] = &setProperty<CircleLayer, PropertyValue<Color>, &CircleLayer::setCircleColor, true, false>; - result["circle-color-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleColorTransition>; - result["circle-blur"] = &setProperty<CircleLayer, PropertyValue<float>, &CircleLayer::setCircleBlur, true, false>; - result["circle-blur-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleBlurTransition>; - result["circle-opacity"] = &setProperty<CircleLayer, PropertyValue<float>, &CircleLayer::setCircleOpacity, true, false>; - result["circle-opacity-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleOpacityTransition>; - result["circle-translate"] = &setProperty<CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate, false, false>; - result["circle-translate-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleTranslateTransition>; - result["circle-translate-anchor"] = &setProperty<CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor, false, false>; - result["circle-translate-anchor-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleTranslateAnchorTransition>; - result["circle-pitch-scale"] = &setProperty<CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale, false, false>; - result["circle-pitch-scale-transition"] = &setTransition<CircleLayer, &CircleLayer::setCirclePitchScaleTransition>; - result["circle-pitch-alignment"] = &setProperty<CircleLayer, PropertyValue<AlignmentType>, &CircleLayer::setCirclePitchAlignment, false, false>; - result["circle-pitch-alignment-transition"] = &setTransition<CircleLayer, &CircleLayer::setCirclePitchAlignmentTransition>; - result["circle-stroke-width"] = &setProperty<CircleLayer, PropertyValue<float>, &CircleLayer::setCircleStrokeWidth, true, false>; - result["circle-stroke-width-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>; - result["circle-stroke-color"] = &setProperty<CircleLayer, PropertyValue<Color>, &CircleLayer::setCircleStrokeColor, true, false>; - result["circle-stroke-color-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeColorTransition>; - result["circle-stroke-opacity"] = &setProperty<CircleLayer, PropertyValue<float>, &CircleLayer::setCircleStrokeOpacity, true, false>; - result["circle-stroke-opacity-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>; - - result["heatmap-radius"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapRadius, true, false>; - result["heatmap-radius-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapRadiusTransition>; - result["heatmap-weight"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapWeight, true, false>; - result["heatmap-weight-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapWeightTransition>; - result["heatmap-intensity"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapIntensity, false, false>; - result["heatmap-intensity-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapIntensityTransition>; - result["heatmap-color"] = &setProperty<HeatmapLayer, ColorRampPropertyValue, &HeatmapLayer::setHeatmapColor, false, false>; - result["heatmap-color-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapColorTransition>; - result["heatmap-opacity"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapOpacity, false, false>; - result["heatmap-opacity-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapOpacityTransition>; - - result["fill-extrusion-opacity"] = &setProperty<FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity, false, false>; - result["fill-extrusion-opacity-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>; - result["fill-extrusion-color"] = &setProperty<FillExtrusionLayer, PropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor, true, false>; - result["fill-extrusion-color-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>; - result["fill-extrusion-translate"] = &setProperty<FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate, false, false>; - result["fill-extrusion-translate-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>; - result["fill-extrusion-translate-anchor"] = &setProperty<FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor, false, false>; - result["fill-extrusion-translate-anchor-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>; - result["fill-extrusion-pattern"] = &setProperty<FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern, false, false>; - result["fill-extrusion-pattern-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>; - result["fill-extrusion-height"] = &setProperty<FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight, true, false>; - result["fill-extrusion-height-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>; - result["fill-extrusion-base"] = &setProperty<FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase, true, false>; - result["fill-extrusion-base-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>; - - result["raster-opacity"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity, false, false>; - result["raster-opacity-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterOpacityTransition>; - result["raster-hue-rotate"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate, false, false>; - result["raster-hue-rotate-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterHueRotateTransition>; - result["raster-brightness-min"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin, false, false>; - result["raster-brightness-min-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>; - result["raster-brightness-max"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax, false, false>; - result["raster-brightness-max-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>; - result["raster-saturation"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation, false, false>; - result["raster-saturation-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterSaturationTransition>; - result["raster-contrast"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast, false, false>; - result["raster-contrast-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterContrastTransition>; - result["raster-resampling"] = &setProperty<RasterLayer, PropertyValue<RasterResamplingType>, &RasterLayer::setRasterResampling, false, false>; - result["raster-resampling-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterResamplingTransition>; - result["raster-fade-duration"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration, false, false>; - result["raster-fade-duration-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterFadeDurationTransition>; - - result["hillshade-illumination-direction"] = &setProperty<HillshadeLayer, PropertyValue<float>, &HillshadeLayer::setHillshadeIlluminationDirection, false, false>; - result["hillshade-illumination-direction-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeIlluminationDirectionTransition>; - result["hillshade-illumination-anchor"] = &setProperty<HillshadeLayer, PropertyValue<HillshadeIlluminationAnchorType>, &HillshadeLayer::setHillshadeIlluminationAnchor, false, false>; - result["hillshade-illumination-anchor-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeIlluminationAnchorTransition>; - result["hillshade-exaggeration"] = &setProperty<HillshadeLayer, PropertyValue<float>, &HillshadeLayer::setHillshadeExaggeration, false, false>; - result["hillshade-exaggeration-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeExaggerationTransition>; - result["hillshade-shadow-color"] = &setProperty<HillshadeLayer, PropertyValue<Color>, &HillshadeLayer::setHillshadeShadowColor, false, false>; - result["hillshade-shadow-color-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeShadowColorTransition>; - result["hillshade-highlight-color"] = &setProperty<HillshadeLayer, PropertyValue<Color>, &HillshadeLayer::setHillshadeHighlightColor, false, false>; - result["hillshade-highlight-color-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeHighlightColorTransition>; - result["hillshade-accent-color"] = &setProperty<HillshadeLayer, PropertyValue<Color>, &HillshadeLayer::setHillshadeAccentColor, false, false>; - result["hillshade-accent-color-transition"] = &setTransition<HillshadeLayer, &HillshadeLayer::setHillshadeAccentColorTransition>; - - result["background-color"] = &setProperty<BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor, false, false>; - result["background-color-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>; - result["background-pattern"] = &setProperty<BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern, false, false>; - result["background-pattern-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>; - result["background-opacity"] = &setProperty<BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity, false, false>; - result["background-opacity-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>; - - return result; -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/conversion/make_property_setters.hpp.ejs b/src/mbgl/style/conversion/make_property_setters.hpp.ejs deleted file mode 100644 index fbf2f93fd6..0000000000 --- a/src/mbgl/style/conversion/make_property_setters.hpp.ejs +++ /dev/null @@ -1,46 +0,0 @@ -#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.replace('-', '_') %>_layer.hpp> -<% } -%> - -#include <unordered_map> - -namespace mbgl { -namespace style { -namespace conversion { - -inline auto makeLayoutPropertySetters() { - std::unordered_map<std::string, PropertySetter> result; - - result["visibility"] = &setVisibility; - -<% for (const layer of locals.layers) { -%> -<% for (const property of layer.layoutProperties) { -%> - result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>, <%- property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven' %>, <%- property.name === 'icon-image' || property.name === 'text-field' %>>; -<% } -%> - -<% } -%> - return result; -} - -inline auto makePaintPropertySetters() { - std::unordered_map<std::string, PropertySetter> result; - -<% for (const layer of locals.layers) { -%> -<% for (const property of layer.paintProperties) { -%> - result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>, <%- property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven' %>, <%- property.name === 'icon-image' || property.name === 'text-field' %>>; - result["<%- property.name %>-transition"] = &setTransition<<%- camelize(layer.type) %>Layer, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>Transition>; -<% } -%> - -<% } -%> - return result; -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/conversion/position.cpp b/src/mbgl/style/conversion/position.cpp index a19f57bff3..df6c868ee8 100644 --- a/src/mbgl/style/conversion/position.cpp +++ b/src/mbgl/style/conversion/position.cpp @@ -1,5 +1,6 @@ #include <mbgl/style/conversion/position.hpp> #include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <array> diff --git a/src/mbgl/style/conversion/property_setter.hpp b/src/mbgl/style/conversion/property_setter.hpp deleted file mode 100644 index 3c5c65f96a..0000000000 --- a/src/mbgl/style/conversion/property_setter.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include <mbgl/style/layer.hpp> -#include <mbgl/style/layers/symbol_layer.hpp> -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/color_ramp_property_value.hpp> -#include <mbgl/style/conversion/constant.hpp> -#include <mbgl/style/conversion/property_value.hpp> -#include <mbgl/style/conversion/transition_options.hpp> - -#include <string> - -namespace mbgl { -namespace style { -namespace conversion { - -using PropertySetter = optional<Error> (*) (Layer&, const Convertible&); - -template <class L, class PropertyValue, void (L::*setter)(PropertyValue), bool allowDataExpressions, bool convertTokens> -optional<Error> setProperty(Layer& layer, const Convertible& value) { - auto* typedLayer = layer.as<L>(); - if (!typedLayer) { - return Error { "layer doesn't support this property" }; - } - - Error error; - optional<PropertyValue> typedValue = convert<PropertyValue>(value, error, allowDataExpressions, convertTokens); - if (!typedValue) { - return error; - } - - (typedLayer->*setter)(*typedValue); - return nullopt; -} - -template <class L, void (L::*setter)(const TransitionOptions&)> -optional<Error> setTransition(Layer& layer, const Convertible& value) { - auto* typedLayer = layer.as<L>(); - if (!typedLayer) { - return Error { "layer doesn't support this property" }; - } - - Error error; - optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); - if (!transition) { - return error; - } - - (typedLayer->*setter)(*transition); - return nullopt; -} - -inline optional<Error> setVisibility(Layer& layer, const Convertible& value) { - if (isUndefined(value)) { - layer.setVisibility(VisibilityType::Visible); - return nullopt; - } - - Error error; - optional<VisibilityType> visibility = convert<VisibilityType>(value, error); - if (!visibility) { - return error; - } - - layer.setVisibility(*visibility); - return nullopt; -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/conversion/property_value.cpp b/src/mbgl/style/conversion/property_value.cpp new file mode 100644 index 0000000000..8a93c24767 --- /dev/null +++ b/src/mbgl/style/conversion/property_value.cpp @@ -0,0 +1,83 @@ +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/position.hpp> +#include <mbgl/style/conversion_impl.hpp> + +namespace mbgl { +namespace style { +namespace conversion { + +template <class T> +optional<PropertyValue<T>> Converter<PropertyValue<T>>::operator()(const Convertible& value, Error& error, bool allowDataExpressions, bool convertTokens) const { + using namespace mbgl::style::expression; + + if (isUndefined(value)) { + return PropertyValue<T>(); + } + + optional<PropertyExpression<T>> expression; + + if (isExpression(value)) { + ParsingContext ctx(valueTypeToExpressionType<T>()); + ParseResult parsed = ctx.parseLayerPropertyExpression(value); + if (!parsed) { + error.message = ctx.getCombinedErrors(); + return nullopt; + } + expression = PropertyExpression<T>(std::move(*parsed)); + } else if (isObject(value)) { + expression = convertFunctionToExpression<T>(value, error, convertTokens); + } else { + optional<T> constant = convert<T>(value, error); + if (!constant) { + return nullopt; + } + return convertTokens ? maybeConvertTokens(*constant) : PropertyValue<T>(*constant); + } + + if (!expression) { + return nullopt; + } else if (!allowDataExpressions && !(*expression).isFeatureConstant()) { + error.message = "data expressions not supported"; + return nullopt; + } else if (!(*expression).isFeatureConstant() || !(*expression).isZoomConstant()) { + return { std::move(*expression) }; + } else if ((*expression).getExpression().getKind() == Kind::Literal) { + optional<T> constant = fromExpressionValue<T>( + static_cast<const Literal&>((*expression).getExpression()).getValue()); + if (!constant) { + return nullopt; + } + return PropertyValue<T>(*constant); + } else { + assert(false); + error.message = "expected a literal expression"; + return nullopt; + } +} + +template optional<PropertyValue<bool>> Converter<PropertyValue<bool>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<float>> Converter<PropertyValue<float>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<std::array<float, 2>>> Converter<PropertyValue<std::array<float, 2>>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<std::array<float, 4>>> Converter<PropertyValue<std::array<float, 4>>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<std::vector<float>>> Converter<PropertyValue<std::vector<float>>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<Color>> Converter<PropertyValue<Color>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<std::string>> Converter<PropertyValue<std::string>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<std::vector<std::string>>> Converter<PropertyValue<std::vector<std::string>>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<AlignmentType>> Converter<PropertyValue<AlignmentType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<CirclePitchScaleType>> Converter<PropertyValue<CirclePitchScaleType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<HillshadeIlluminationAnchorType>> Converter<PropertyValue<HillshadeIlluminationAnchorType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<IconTextFitType>> Converter<PropertyValue<IconTextFitType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<LightAnchorType>> Converter<PropertyValue<LightAnchorType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<LineCapType>> Converter<PropertyValue<LineCapType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<LineJoinType>> Converter<PropertyValue<LineJoinType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<Position>> Converter<PropertyValue<Position>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<RasterResamplingType>> Converter<PropertyValue<RasterResamplingType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<SymbolAnchorType>> Converter<PropertyValue<SymbolAnchorType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<SymbolPlacementType>> Converter<PropertyValue<SymbolPlacementType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<TextJustifyType>> Converter<PropertyValue<TextJustifyType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<TextTransformType>> Converter<PropertyValue<TextTransformType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; +template optional<PropertyValue<TranslateAnchorType>> Converter<PropertyValue<TranslateAnchorType>>::operator()(conversion::Convertible const&, conversion::Error&, bool, bool) const; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/source.cpp b/src/mbgl/style/conversion/source.cpp index ce0cb24ce0..5ecbd3b474 100644 --- a/src/mbgl/style/conversion/source.cpp +++ b/src/mbgl/style/conversion/source.cpp @@ -3,6 +3,7 @@ #include <mbgl/style/conversion/geojson.hpp> #include <mbgl/style/conversion/geojson_options.hpp> #include <mbgl/style/conversion/tileset.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/style/sources/geojson_source.hpp> #include <mbgl/style/sources/raster_source.hpp> #include <mbgl/style/sources/raster_dem_source.hpp> diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp index b566af0a18..40575838ea 100644 --- a/src/mbgl/style/conversion/tileset.cpp +++ b/src/mbgl/style/conversion/tileset.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/conversion/tileset.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/geo.hpp> #include <mbgl/math/clamp.hpp> diff --git a/src/mbgl/style/conversion/transition_options.cpp b/src/mbgl/style/conversion/transition_options.cpp index 924032a0c0..6e39dca24f 100644 --- a/src/mbgl/style/conversion/transition_options.cpp +++ b/src/mbgl/style/conversion/transition_options.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/expression/array_assertion.cpp b/src/mbgl/style/expression/array_assertion.cpp index 4049301b0b..9df586bdc3 100644 --- a/src/mbgl/style/expression/array_assertion.cpp +++ b/src/mbgl/style/expression/array_assertion.cpp @@ -1,5 +1,6 @@ #include <mbgl/style/expression/array_assertion.hpp> #include <mbgl/style/expression/check_subtype.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> namespace mbgl { diff --git a/src/mbgl/style/expression/assertion.cpp b/src/mbgl/style/expression/assertion.cpp index 2434d7a2f8..7e93003ac3 100644 --- a/src/mbgl/style/expression/assertion.cpp +++ b/src/mbgl/style/expression/assertion.cpp @@ -1,5 +1,6 @@ #include <mbgl/style/expression/assertion.hpp> #include <mbgl/style/expression/check_subtype.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/expression/at.cpp b/src/mbgl/style/expression/at.cpp index 725e5ddb51..648f247830 100644 --- a/src/mbgl/style/expression/at.cpp +++ b/src/mbgl/style/expression/at.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/expression/at.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> diff --git a/src/mbgl/style/expression/boolean_operator.cpp b/src/mbgl/style/expression/boolean_operator.cpp index 68e96129aa..fa472270ce 100644 --- a/src/mbgl/style/expression/boolean_operator.cpp +++ b/src/mbgl/style/expression/boolean_operator.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/expression/boolean_operator.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/expression/case.cpp b/src/mbgl/style/expression/case.cpp index e885c0ce6b..0c2ff0d7cd 100644 --- a/src/mbgl/style/expression/case.cpp +++ b/src/mbgl/style/expression/case.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/expression/case.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> namespace mbgl { diff --git a/src/mbgl/style/expression/coalesce.cpp b/src/mbgl/style/expression/coalesce.cpp index 0090f16009..cdbf452f7f 100644 --- a/src/mbgl/style/expression/coalesce.cpp +++ b/src/mbgl/style/expression/coalesce.cpp @@ -1,5 +1,6 @@ #include <mbgl/style/expression/coalesce.hpp> #include <mbgl/style/expression/check_subtype.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/expression/coercion.cpp b/src/mbgl/style/expression/coercion.cpp index f5a4d70f66..486658ddda 100644 --- a/src/mbgl/style/expression/coercion.cpp +++ b/src/mbgl/style/expression/coercion.cpp @@ -1,6 +1,7 @@ #include <mbgl/style/expression/coercion.hpp> #include <mbgl/style/expression/check_subtype.hpp> #include <mbgl/style/expression/util.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> namespace mbgl { diff --git a/src/mbgl/style/expression/collator_expression.cpp b/src/mbgl/style/expression/collator_expression.cpp index b27eedbc76..07346633a2 100644 --- a/src/mbgl/style/expression/collator_expression.cpp +++ b/src/mbgl/style/expression/collator_expression.cpp @@ -1,6 +1,7 @@ #include <mbgl/style/expression/collator.hpp> #include <mbgl/style/expression/collator_expression.hpp> #include <mbgl/style/expression/literal.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> namespace mbgl { diff --git a/src/mbgl/style/expression/comparison.cpp b/src/mbgl/style/expression/comparison.cpp index 6179c3ce88..cdcdb5d59c 100644 --- a/src/mbgl/style/expression/comparison.cpp +++ b/src/mbgl/style/expression/comparison.cpp @@ -1,6 +1,7 @@ #include <mbgl/style/expression/collator.hpp> #include <mbgl/style/expression/comparison.hpp> #include <mbgl/style/expression/dsl.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index 4c476a3749..f8c2376cb3 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -3,6 +3,7 @@ #include <mbgl/style/expression/compound_expression.hpp> #include <mbgl/style/expression/check_subtype.hpp> #include <mbgl/style/expression/util.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/math/log2.hpp> #include <mbgl/util/i18n.hpp> diff --git a/src/mbgl/style/expression/interpolate.cpp b/src/mbgl/style/expression/interpolate.cpp index 54fbc6e1d7..8725e9e86d 100644 --- a/src/mbgl/style/expression/interpolate.cpp +++ b/src/mbgl/style/expression/interpolate.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/expression/interpolate.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> namespace mbgl { diff --git a/src/mbgl/style/expression/is_constant.cpp b/src/mbgl/style/expression/is_constant.cpp index 69f27f6fef..3b20f49a86 100644 --- a/src/mbgl/style/expression/is_constant.cpp +++ b/src/mbgl/style/expression/is_constant.cpp @@ -17,7 +17,7 @@ bool isFeatureConstant(const Expression& expression) { return false; } else if (name == "has" && parameterCount && *parameterCount == 1) { return false; - } else if (std::equal(std::begin(filter), std::end(filter) - 1, name.begin())) { + } else if (0 == name.rfind(filter, 0)) { // Legacy filters begin with "filter-" and are never constant. return false; } else if ( @@ -28,7 +28,7 @@ bool isFeatureConstant(const Expression& expression) { return false; } } - + if (expression.getKind() == Kind::CollatorExpression) { // Although the results of a Collator expression with fixed arguments // generally shouldn't change between executions, we can't serialize them diff --git a/src/mbgl/style/expression/is_expression.cpp b/src/mbgl/style/expression/is_expression.cpp index 77212f6a1e..acf074c25b 100644 --- a/src/mbgl/style/expression/is_expression.cpp +++ b/src/mbgl/style/expression/is_expression.cpp @@ -1,8 +1,7 @@ #include <mbgl/style/expression/is_expression.hpp> #include <mbgl/style/expression/compound_expression.hpp> #include <mbgl/style/expression/parsing_context.hpp> - -#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <unordered_set> diff --git a/src/mbgl/style/expression/length.cpp b/src/mbgl/style/expression/length.cpp index ad7a15675a..f1b58d7952 100644 --- a/src/mbgl/style/expression/length.cpp +++ b/src/mbgl/style/expression/length.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/expression/length.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> namespace mbgl { diff --git a/src/mbgl/style/expression/let.cpp b/src/mbgl/style/expression/let.cpp index 242a995b0b..592ceed58a 100644 --- a/src/mbgl/style/expression/let.cpp +++ b/src/mbgl/style/expression/let.cpp @@ -1,5 +1,6 @@ #include <mbgl/style/expression/let.hpp> #include <mbgl/style/conversion/get_json_type.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> namespace mbgl { diff --git a/src/mbgl/style/expression/literal.cpp b/src/mbgl/style/expression/literal.cpp index 345a52de9b..c69341d298 100644 --- a/src/mbgl/style/expression/literal.cpp +++ b/src/mbgl/style/expression/literal.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/expression/literal.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> namespace mbgl { diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp index 4b4984811f..0f05001a97 100644 --- a/src/mbgl/style/expression/match.cpp +++ b/src/mbgl/style/expression/match.cpp @@ -1,6 +1,7 @@ #include <mbgl/style/expression/match.hpp> #include <mbgl/style/expression/check_subtype.hpp> #include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> namespace mbgl { diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp index b3f6b1acee..ef17caed33 100644 --- a/src/mbgl/style/expression/parsing_context.cpp +++ b/src/mbgl/style/expression/parsing_context.cpp @@ -24,6 +24,7 @@ #include <mbgl/style/expression/find_zoom_curve.hpp> #include <mbgl/style/conversion/get_json_type.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> diff --git a/src/mbgl/style/expression/step.cpp b/src/mbgl/style/expression/step.cpp index a1ca0a702e..39b04c04a0 100644 --- a/src/mbgl/style/expression/step.cpp +++ b/src/mbgl/style/expression/step.cpp @@ -1,5 +1,6 @@ #include <mbgl/style/expression/step.hpp> #include <mbgl/style/expression/get_covering_stops.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/string.hpp> #include <cmath> diff --git a/src/mbgl/style/layer.cpp b/src/mbgl/style/layer.cpp index 142fe313cf..e08b71e6b3 100644 --- a/src/mbgl/style/layer.cpp +++ b/src/mbgl/style/layer.cpp @@ -1,6 +1,8 @@ #include <mbgl/style/layer.hpp> #include <mbgl/style/layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion_impl.hpp> namespace mbgl { namespace style { @@ -38,5 +40,23 @@ void Layer::setObserver(LayerObserver* observer_) { observer = observer_ ? observer_ : &nullObserver; } +optional<conversion::Error> Layer::setVisibility(const conversion::Convertible& value) { + using namespace conversion; + + if (isUndefined(value)) { + setVisibility(VisibilityType::Visible); + return nullopt; + } + + Error error; + optional<VisibilityType> visibility = convert<VisibilityType>(value, error); + if (!visibility) { + return error; + } + + setVisibility(*visibility); + return nullopt; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/background_layer.cpp b/src/mbgl/style/layers/background_layer.cpp index 66ab46c078..f2e85ce7e7 100644 --- a/src/mbgl/style/layers/background_layer.cpp +++ b/src/mbgl/style/layers/background_layer.cpp @@ -3,6 +3,13 @@ #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/style/layers/background_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -149,5 +156,143 @@ TransitionOptions BackgroundLayer::getBackgroundOpacityTransition() const { return impl().paint.template get<BackgroundOpacity>().options; } +using namespace conversion; + +optional<Error> BackgroundLayer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, + BackgroundColor, + BackgroundPattern, + BackgroundOpacity, + BackgroundColorTransition, + BackgroundPatternTransition, + BackgroundOpacityTransition, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + case util::hashFNV1a("background-color"): + if (name == "background-color") { + property = Property::BackgroundColor; + } + break; + case util::hashFNV1a("background-color-transition"): + if (name == "background-color-transition") { + property = Property::BackgroundColorTransition; + } + break; + case util::hashFNV1a("background-pattern"): + if (name == "background-pattern") { + property = Property::BackgroundPattern; + } + break; + case util::hashFNV1a("background-pattern-transition"): + if (name == "background-pattern-transition") { + property = Property::BackgroundPatternTransition; + } + break; + case util::hashFNV1a("background-opacity"): + if (name == "background-opacity") { + property = Property::BackgroundOpacity; + } + break; + case util::hashFNV1a("background-opacity-transition"): + if (name == "background-opacity-transition") { + property = Property::BackgroundOpacityTransition; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::BackgroundColor) { + Error error; + optional<PropertyValue<Color>> typedValue = convert<PropertyValue<Color>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setBackgroundColor(*typedValue); + return nullopt; + + } + + if (property == Property::BackgroundPattern) { + Error error; + optional<PropertyValue<std::string>> typedValue = convert<PropertyValue<std::string>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setBackgroundPattern(*typedValue); + return nullopt; + + } + + if (property == Property::BackgroundOpacity) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setBackgroundOpacity(*typedValue); + return nullopt; + + } + + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + + if (property == Property::BackgroundColorTransition) { + setBackgroundColorTransition(*transition); + return nullopt; + } + + if (property == Property::BackgroundPatternTransition) { + setBackgroundPatternTransition(*transition); + return nullopt; + } + + if (property == Property::BackgroundOpacityTransition) { + setBackgroundOpacityTransition(*transition); + return nullopt; + } + + + return Error { "layer doesn't support this property" }; +} + +optional<Error> BackgroundLayer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp index d435ce89e1..c301a83c9e 100644 --- a/src/mbgl/style/layers/circle_layer.cpp +++ b/src/mbgl/style/layers/circle_layer.cpp @@ -3,6 +3,13 @@ #include <mbgl/style/layers/circle_layer.hpp> #include <mbgl/style/layers/circle_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -393,5 +400,344 @@ TransitionOptions CircleLayer::getCircleStrokeOpacityTransition() const { return impl().paint.template get<CircleStrokeOpacity>().options; } +using namespace conversion; + +optional<Error> CircleLayer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, + CircleRadius, + CircleColor, + CircleBlur, + CircleOpacity, + CircleTranslate, + CircleTranslateAnchor, + CirclePitchScale, + CirclePitchAlignment, + CircleStrokeWidth, + CircleStrokeColor, + CircleStrokeOpacity, + CircleRadiusTransition, + CircleColorTransition, + CircleBlurTransition, + CircleOpacityTransition, + CircleTranslateTransition, + CircleTranslateAnchorTransition, + CirclePitchScaleTransition, + CirclePitchAlignmentTransition, + CircleStrokeWidthTransition, + CircleStrokeColorTransition, + CircleStrokeOpacityTransition, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + case util::hashFNV1a("circle-radius"): + if (name == "circle-radius") { + property = Property::CircleRadius; + } + break; + case util::hashFNV1a("circle-radius-transition"): + if (name == "circle-radius-transition") { + property = Property::CircleRadiusTransition; + } + break; + case util::hashFNV1a("circle-color"): + if (name == "circle-color") { + property = Property::CircleColor; + } + break; + case util::hashFNV1a("circle-color-transition"): + if (name == "circle-color-transition") { + property = Property::CircleColorTransition; + } + break; + case util::hashFNV1a("circle-blur"): + if (name == "circle-blur") { + property = Property::CircleBlur; + } + break; + case util::hashFNV1a("circle-blur-transition"): + if (name == "circle-blur-transition") { + property = Property::CircleBlurTransition; + } + break; + case util::hashFNV1a("circle-opacity"): + if (name == "circle-opacity") { + property = Property::CircleOpacity; + } + break; + case util::hashFNV1a("circle-opacity-transition"): + if (name == "circle-opacity-transition") { + property = Property::CircleOpacityTransition; + } + break; + case util::hashFNV1a("circle-translate"): + if (name == "circle-translate") { + property = Property::CircleTranslate; + } + break; + case util::hashFNV1a("circle-translate-transition"): + if (name == "circle-translate-transition") { + property = Property::CircleTranslateTransition; + } + break; + case util::hashFNV1a("circle-translate-anchor"): + if (name == "circle-translate-anchor") { + property = Property::CircleTranslateAnchor; + } + break; + case util::hashFNV1a("circle-translate-anchor-transition"): + if (name == "circle-translate-anchor-transition") { + property = Property::CircleTranslateAnchorTransition; + } + break; + case util::hashFNV1a("circle-pitch-scale"): + if (name == "circle-pitch-scale") { + property = Property::CirclePitchScale; + } + break; + case util::hashFNV1a("circle-pitch-scale-transition"): + if (name == "circle-pitch-scale-transition") { + property = Property::CirclePitchScaleTransition; + } + break; + case util::hashFNV1a("circle-pitch-alignment"): + if (name == "circle-pitch-alignment") { + property = Property::CirclePitchAlignment; + } + break; + case util::hashFNV1a("circle-pitch-alignment-transition"): + if (name == "circle-pitch-alignment-transition") { + property = Property::CirclePitchAlignmentTransition; + } + break; + case util::hashFNV1a("circle-stroke-width"): + if (name == "circle-stroke-width") { + property = Property::CircleStrokeWidth; + } + break; + case util::hashFNV1a("circle-stroke-width-transition"): + if (name == "circle-stroke-width-transition") { + property = Property::CircleStrokeWidthTransition; + } + break; + case util::hashFNV1a("circle-stroke-color"): + if (name == "circle-stroke-color") { + property = Property::CircleStrokeColor; + } + break; + case util::hashFNV1a("circle-stroke-color-transition"): + if (name == "circle-stroke-color-transition") { + property = Property::CircleStrokeColorTransition; + } + break; + case util::hashFNV1a("circle-stroke-opacity"): + if (name == "circle-stroke-opacity") { + property = Property::CircleStrokeOpacity; + } + break; + case util::hashFNV1a("circle-stroke-opacity-transition"): + if (name == "circle-stroke-opacity-transition") { + property = Property::CircleStrokeOpacityTransition; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::CircleRadius || property == Property::CircleBlur || property == Property::CircleOpacity || property == Property::CircleStrokeWidth || property == Property::CircleStrokeOpacity) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::CircleRadius) { + setCircleRadius(*typedValue); + return nullopt; + } + + if (property == Property::CircleBlur) { + setCircleBlur(*typedValue); + return nullopt; + } + + if (property == Property::CircleOpacity) { + setCircleOpacity(*typedValue); + return nullopt; + } + + if (property == Property::CircleStrokeWidth) { + setCircleStrokeWidth(*typedValue); + return nullopt; + } + + if (property == Property::CircleStrokeOpacity) { + setCircleStrokeOpacity(*typedValue); + return nullopt; + } + + } + + if (property == Property::CircleColor || property == Property::CircleStrokeColor) { + Error error; + optional<PropertyValue<Color>> typedValue = convert<PropertyValue<Color>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::CircleColor) { + setCircleColor(*typedValue); + return nullopt; + } + + if (property == Property::CircleStrokeColor) { + setCircleStrokeColor(*typedValue); + return nullopt; + } + + } + + if (property == Property::CircleTranslate) { + Error error; + optional<PropertyValue<std::array<float, 2>>> typedValue = convert<PropertyValue<std::array<float, 2>>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setCircleTranslate(*typedValue); + return nullopt; + + } + + if (property == Property::CircleTranslateAnchor) { + Error error; + optional<PropertyValue<TranslateAnchorType>> typedValue = convert<PropertyValue<TranslateAnchorType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setCircleTranslateAnchor(*typedValue); + return nullopt; + + } + + if (property == Property::CirclePitchScale) { + Error error; + optional<PropertyValue<CirclePitchScaleType>> typedValue = convert<PropertyValue<CirclePitchScaleType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setCirclePitchScale(*typedValue); + return nullopt; + + } + + if (property == Property::CirclePitchAlignment) { + Error error; + optional<PropertyValue<AlignmentType>> typedValue = convert<PropertyValue<AlignmentType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setCirclePitchAlignment(*typedValue); + return nullopt; + + } + + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + + if (property == Property::CircleRadiusTransition) { + setCircleRadiusTransition(*transition); + return nullopt; + } + + if (property == Property::CircleColorTransition) { + setCircleColorTransition(*transition); + return nullopt; + } + + if (property == Property::CircleBlurTransition) { + setCircleBlurTransition(*transition); + return nullopt; + } + + if (property == Property::CircleOpacityTransition) { + setCircleOpacityTransition(*transition); + return nullopt; + } + + if (property == Property::CircleTranslateTransition) { + setCircleTranslateTransition(*transition); + return nullopt; + } + + if (property == Property::CircleTranslateAnchorTransition) { + setCircleTranslateAnchorTransition(*transition); + return nullopt; + } + + if (property == Property::CirclePitchScaleTransition) { + setCirclePitchScaleTransition(*transition); + return nullopt; + } + + if (property == Property::CirclePitchAlignmentTransition) { + setCirclePitchAlignmentTransition(*transition); + return nullopt; + } + + if (property == Property::CircleStrokeWidthTransition) { + setCircleStrokeWidthTransition(*transition); + return nullopt; + } + + if (property == Property::CircleStrokeColorTransition) { + setCircleStrokeColorTransition(*transition); + return nullopt; + } + + if (property == Property::CircleStrokeOpacityTransition) { + setCircleStrokeOpacityTransition(*transition); + return nullopt; + } + + + return Error { "layer doesn't support this property" }; +} + +optional<Error> CircleLayer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/circle_layer_impl.cpp b/src/mbgl/style/layers/circle_layer_impl.cpp index 69f574cd6b..358598e09c 100644 --- a/src/mbgl/style/layers/circle_layer_impl.cpp +++ b/src/mbgl/style/layers/circle_layer_impl.cpp @@ -4,7 +4,7 @@ namespace mbgl { namespace style { bool CircleLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { - assert(dynamic_cast<const CircleLayer::Impl*>(&other)); + assert(other.type == LayerType::Circle); const auto& impl = static_cast<const style::CircleLayer::Impl&>(other); return filter != impl.filter || visibility != impl.visibility || diff --git a/src/mbgl/style/layers/custom_layer.cpp b/src/mbgl/style/layers/custom_layer.cpp index 0e51a70e50..d1ac1a705c 100644 --- a/src/mbgl/style/layers/custom_layer.cpp +++ b/src/mbgl/style/layers/custom_layer.cpp @@ -50,6 +50,16 @@ void CustomLayer::setMaxZoom(float maxZoom) { baseImpl = std::move(impl_); } +using namespace conversion; + +optional<Error> CustomLayer::setPaintProperty(const std::string&, const Convertible&) { + return Error { "layer doesn't support this property" }; +} + +optional<Error> CustomLayer::setLayoutProperty(const std::string&, const Convertible&) { + return Error { "layer doesn't support this property" }; +} + template <> bool Layer::is<CustomLayer>() const { return getType() == LayerType::Custom; diff --git a/src/mbgl/style/layers/fill_extrusion_layer.cpp b/src/mbgl/style/layers/fill_extrusion_layer.cpp index 829a24f354..74cdb9abe6 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer.cpp @@ -3,6 +3,13 @@ #include <mbgl/style/layers/fill_extrusion_layer.hpp> #include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -285,5 +292,254 @@ TransitionOptions FillExtrusionLayer::getFillExtrusionBaseTransition() const { return impl().paint.template get<FillExtrusionBase>().options; } +using namespace conversion; + +optional<Error> FillExtrusionLayer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, + FillExtrusionOpacity, + FillExtrusionColor, + FillExtrusionTranslate, + FillExtrusionTranslateAnchor, + FillExtrusionPattern, + FillExtrusionHeight, + FillExtrusionBase, + FillExtrusionOpacityTransition, + FillExtrusionColorTransition, + FillExtrusionTranslateTransition, + FillExtrusionTranslateAnchorTransition, + FillExtrusionPatternTransition, + FillExtrusionHeightTransition, + FillExtrusionBaseTransition, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + case util::hashFNV1a("fill-extrusion-opacity"): + if (name == "fill-extrusion-opacity") { + property = Property::FillExtrusionOpacity; + } + break; + case util::hashFNV1a("fill-extrusion-opacity-transition"): + if (name == "fill-extrusion-opacity-transition") { + property = Property::FillExtrusionOpacityTransition; + } + break; + case util::hashFNV1a("fill-extrusion-color"): + if (name == "fill-extrusion-color") { + property = Property::FillExtrusionColor; + } + break; + case util::hashFNV1a("fill-extrusion-color-transition"): + if (name == "fill-extrusion-color-transition") { + property = Property::FillExtrusionColorTransition; + } + break; + case util::hashFNV1a("fill-extrusion-translate"): + if (name == "fill-extrusion-translate") { + property = Property::FillExtrusionTranslate; + } + break; + case util::hashFNV1a("fill-extrusion-translate-transition"): + if (name == "fill-extrusion-translate-transition") { + property = Property::FillExtrusionTranslateTransition; + } + break; + case util::hashFNV1a("fill-extrusion-translate-anchor"): + if (name == "fill-extrusion-translate-anchor") { + property = Property::FillExtrusionTranslateAnchor; + } + break; + case util::hashFNV1a("fill-extrusion-translate-anchor-transition"): + if (name == "fill-extrusion-translate-anchor-transition") { + property = Property::FillExtrusionTranslateAnchorTransition; + } + break; + case util::hashFNV1a("fill-extrusion-pattern"): + if (name == "fill-extrusion-pattern") { + property = Property::FillExtrusionPattern; + } + break; + case util::hashFNV1a("fill-extrusion-pattern-transition"): + if (name == "fill-extrusion-pattern-transition") { + property = Property::FillExtrusionPatternTransition; + } + break; + case util::hashFNV1a("fill-extrusion-height"): + if (name == "fill-extrusion-height") { + property = Property::FillExtrusionHeight; + } + break; + case util::hashFNV1a("fill-extrusion-height-transition"): + if (name == "fill-extrusion-height-transition") { + property = Property::FillExtrusionHeightTransition; + } + break; + case util::hashFNV1a("fill-extrusion-base"): + if (name == "fill-extrusion-base") { + property = Property::FillExtrusionBase; + } + break; + case util::hashFNV1a("fill-extrusion-base-transition"): + if (name == "fill-extrusion-base-transition") { + property = Property::FillExtrusionBaseTransition; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::FillExtrusionOpacity) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setFillExtrusionOpacity(*typedValue); + return nullopt; + + } + + if (property == Property::FillExtrusionColor) { + Error error; + optional<PropertyValue<Color>> typedValue = convert<PropertyValue<Color>>(value, error, true, false); + if (!typedValue) { + return error; + } + + setFillExtrusionColor(*typedValue); + return nullopt; + + } + + if (property == Property::FillExtrusionTranslate) { + Error error; + optional<PropertyValue<std::array<float, 2>>> typedValue = convert<PropertyValue<std::array<float, 2>>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setFillExtrusionTranslate(*typedValue); + return nullopt; + + } + + if (property == Property::FillExtrusionTranslateAnchor) { + Error error; + optional<PropertyValue<TranslateAnchorType>> typedValue = convert<PropertyValue<TranslateAnchorType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setFillExtrusionTranslateAnchor(*typedValue); + return nullopt; + + } + + if (property == Property::FillExtrusionPattern) { + Error error; + optional<PropertyValue<std::string>> typedValue = convert<PropertyValue<std::string>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setFillExtrusionPattern(*typedValue); + return nullopt; + + } + + if (property == Property::FillExtrusionHeight || property == Property::FillExtrusionBase) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::FillExtrusionHeight) { + setFillExtrusionHeight(*typedValue); + return nullopt; + } + + if (property == Property::FillExtrusionBase) { + setFillExtrusionBase(*typedValue); + return nullopt; + } + + } + + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + + if (property == Property::FillExtrusionOpacityTransition) { + setFillExtrusionOpacityTransition(*transition); + return nullopt; + } + + if (property == Property::FillExtrusionColorTransition) { + setFillExtrusionColorTransition(*transition); + return nullopt; + } + + if (property == Property::FillExtrusionTranslateTransition) { + setFillExtrusionTranslateTransition(*transition); + return nullopt; + } + + if (property == Property::FillExtrusionTranslateAnchorTransition) { + setFillExtrusionTranslateAnchorTransition(*transition); + return nullopt; + } + + if (property == Property::FillExtrusionPatternTransition) { + setFillExtrusionPatternTransition(*transition); + return nullopt; + } + + if (property == Property::FillExtrusionHeightTransition) { + setFillExtrusionHeightTransition(*transition); + return nullopt; + } + + if (property == Property::FillExtrusionBaseTransition) { + setFillExtrusionBaseTransition(*transition); + return nullopt; + } + + + return Error { "layer doesn't support this property" }; +} + +optional<Error> FillExtrusionLayer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp index d37c2ad29b..357ea3e973 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp @@ -4,7 +4,7 @@ namespace mbgl { namespace style { bool FillExtrusionLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { - assert(dynamic_cast<const FillExtrusionLayer::Impl*>(&other)); + assert(other.type == LayerType::FillExtrusion); const auto& impl = static_cast<const style::FillExtrusionLayer::Impl&>(other); return filter != impl.filter || visibility != impl.visibility || diff --git a/src/mbgl/style/layers/fill_layer.cpp b/src/mbgl/style/layers/fill_layer.cpp index 8eebd54e3c..bdfc000736 100644 --- a/src/mbgl/style/layers/fill_layer.cpp +++ b/src/mbgl/style/layers/fill_layer.cpp @@ -3,6 +3,13 @@ #include <mbgl/style/layers/fill_layer.hpp> #include <mbgl/style/layers/fill_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -285,5 +292,254 @@ TransitionOptions FillLayer::getFillPatternTransition() const { return impl().paint.template get<FillPattern>().options; } +using namespace conversion; + +optional<Error> FillLayer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, + FillAntialias, + FillOpacity, + FillColor, + FillOutlineColor, + FillTranslate, + FillTranslateAnchor, + FillPattern, + FillAntialiasTransition, + FillOpacityTransition, + FillColorTransition, + FillOutlineColorTransition, + FillTranslateTransition, + FillTranslateAnchorTransition, + FillPatternTransition, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + case util::hashFNV1a("fill-antialias"): + if (name == "fill-antialias") { + property = Property::FillAntialias; + } + break; + case util::hashFNV1a("fill-antialias-transition"): + if (name == "fill-antialias-transition") { + property = Property::FillAntialiasTransition; + } + break; + case util::hashFNV1a("fill-opacity"): + if (name == "fill-opacity") { + property = Property::FillOpacity; + } + break; + case util::hashFNV1a("fill-opacity-transition"): + if (name == "fill-opacity-transition") { + property = Property::FillOpacityTransition; + } + break; + case util::hashFNV1a("fill-color"): + if (name == "fill-color") { + property = Property::FillColor; + } + break; + case util::hashFNV1a("fill-color-transition"): + if (name == "fill-color-transition") { + property = Property::FillColorTransition; + } + break; + case util::hashFNV1a("fill-outline-color"): + if (name == "fill-outline-color") { + property = Property::FillOutlineColor; + } + break; + case util::hashFNV1a("fill-outline-color-transition"): + if (name == "fill-outline-color-transition") { + property = Property::FillOutlineColorTransition; + } + break; + case util::hashFNV1a("fill-translate"): + if (name == "fill-translate") { + property = Property::FillTranslate; + } + break; + case util::hashFNV1a("fill-translate-transition"): + if (name == "fill-translate-transition") { + property = Property::FillTranslateTransition; + } + break; + case util::hashFNV1a("fill-translate-anchor"): + if (name == "fill-translate-anchor") { + property = Property::FillTranslateAnchor; + } + break; + case util::hashFNV1a("fill-translate-anchor-transition"): + if (name == "fill-translate-anchor-transition") { + property = Property::FillTranslateAnchorTransition; + } + break; + case util::hashFNV1a("fill-pattern"): + if (name == "fill-pattern") { + property = Property::FillPattern; + } + break; + case util::hashFNV1a("fill-pattern-transition"): + if (name == "fill-pattern-transition") { + property = Property::FillPatternTransition; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::FillAntialias) { + Error error; + optional<PropertyValue<bool>> typedValue = convert<PropertyValue<bool>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setFillAntialias(*typedValue); + return nullopt; + + } + + if (property == Property::FillOpacity) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, true, false); + if (!typedValue) { + return error; + } + + setFillOpacity(*typedValue); + return nullopt; + + } + + if (property == Property::FillColor || property == Property::FillOutlineColor) { + Error error; + optional<PropertyValue<Color>> typedValue = convert<PropertyValue<Color>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::FillColor) { + setFillColor(*typedValue); + return nullopt; + } + + if (property == Property::FillOutlineColor) { + setFillOutlineColor(*typedValue); + return nullopt; + } + + } + + if (property == Property::FillTranslate) { + Error error; + optional<PropertyValue<std::array<float, 2>>> typedValue = convert<PropertyValue<std::array<float, 2>>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setFillTranslate(*typedValue); + return nullopt; + + } + + if (property == Property::FillTranslateAnchor) { + Error error; + optional<PropertyValue<TranslateAnchorType>> typedValue = convert<PropertyValue<TranslateAnchorType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setFillTranslateAnchor(*typedValue); + return nullopt; + + } + + if (property == Property::FillPattern) { + Error error; + optional<PropertyValue<std::string>> typedValue = convert<PropertyValue<std::string>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setFillPattern(*typedValue); + return nullopt; + + } + + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + + if (property == Property::FillAntialiasTransition) { + setFillAntialiasTransition(*transition); + return nullopt; + } + + if (property == Property::FillOpacityTransition) { + setFillOpacityTransition(*transition); + return nullopt; + } + + if (property == Property::FillColorTransition) { + setFillColorTransition(*transition); + return nullopt; + } + + if (property == Property::FillOutlineColorTransition) { + setFillOutlineColorTransition(*transition); + return nullopt; + } + + if (property == Property::FillTranslateTransition) { + setFillTranslateTransition(*transition); + return nullopt; + } + + if (property == Property::FillTranslateAnchorTransition) { + setFillTranslateAnchorTransition(*transition); + return nullopt; + } + + if (property == Property::FillPatternTransition) { + setFillPatternTransition(*transition); + return nullopt; + } + + + return Error { "layer doesn't support this property" }; +} + +optional<Error> FillLayer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/fill_layer_impl.cpp b/src/mbgl/style/layers/fill_layer_impl.cpp index 0dc7ed14d5..a8ba1b693b 100644 --- a/src/mbgl/style/layers/fill_layer_impl.cpp +++ b/src/mbgl/style/layers/fill_layer_impl.cpp @@ -4,7 +4,7 @@ namespace mbgl { namespace style { bool FillLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { - assert(dynamic_cast<const FillLayer::Impl*>(&other)); + assert(other.type == LayerType::Fill); const auto& impl = static_cast<const style::FillLayer::Impl&>(other); return filter != impl.filter || visibility != impl.visibility || diff --git a/src/mbgl/style/layers/heatmap_layer.cpp b/src/mbgl/style/layers/heatmap_layer.cpp index 21016ee509..a90aab7009 100644 --- a/src/mbgl/style/layers/heatmap_layer.cpp +++ b/src/mbgl/style/layers/heatmap_layer.cpp @@ -3,10 +3,13 @@ #include <mbgl/style/layers/heatmap_layer.hpp> #include <mbgl/style/layers/heatmap_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> -// for constructing default heatmap-color ramp expression from style JSON -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> #include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -237,5 +240,191 @@ TransitionOptions HeatmapLayer::getHeatmapOpacityTransition() const { return impl().paint.template get<HeatmapOpacity>().options; } +using namespace conversion; + +optional<Error> HeatmapLayer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, + HeatmapRadius, + HeatmapWeight, + HeatmapIntensity, + HeatmapColor, + HeatmapOpacity, + HeatmapRadiusTransition, + HeatmapWeightTransition, + HeatmapIntensityTransition, + HeatmapColorTransition, + HeatmapOpacityTransition, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + case util::hashFNV1a("heatmap-radius"): + if (name == "heatmap-radius") { + property = Property::HeatmapRadius; + } + break; + case util::hashFNV1a("heatmap-radius-transition"): + if (name == "heatmap-radius-transition") { + property = Property::HeatmapRadiusTransition; + } + break; + case util::hashFNV1a("heatmap-weight"): + if (name == "heatmap-weight") { + property = Property::HeatmapWeight; + } + break; + case util::hashFNV1a("heatmap-weight-transition"): + if (name == "heatmap-weight-transition") { + property = Property::HeatmapWeightTransition; + } + break; + case util::hashFNV1a("heatmap-intensity"): + if (name == "heatmap-intensity") { + property = Property::HeatmapIntensity; + } + break; + case util::hashFNV1a("heatmap-intensity-transition"): + if (name == "heatmap-intensity-transition") { + property = Property::HeatmapIntensityTransition; + } + break; + case util::hashFNV1a("heatmap-color"): + if (name == "heatmap-color") { + property = Property::HeatmapColor; + } + break; + case util::hashFNV1a("heatmap-color-transition"): + if (name == "heatmap-color-transition") { + property = Property::HeatmapColorTransition; + } + break; + case util::hashFNV1a("heatmap-opacity"): + if (name == "heatmap-opacity") { + property = Property::HeatmapOpacity; + } + break; + case util::hashFNV1a("heatmap-opacity-transition"): + if (name == "heatmap-opacity-transition") { + property = Property::HeatmapOpacityTransition; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::HeatmapRadius || property == Property::HeatmapWeight) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::HeatmapRadius) { + setHeatmapRadius(*typedValue); + return nullopt; + } + + if (property == Property::HeatmapWeight) { + setHeatmapWeight(*typedValue); + return nullopt; + } + + } + + if (property == Property::HeatmapIntensity || property == Property::HeatmapOpacity) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::HeatmapIntensity) { + setHeatmapIntensity(*typedValue); + return nullopt; + } + + if (property == Property::HeatmapOpacity) { + setHeatmapOpacity(*typedValue); + return nullopt; + } + + } + + if (property == Property::HeatmapColor) { + Error error; + optional<ColorRampPropertyValue> typedValue = convert<ColorRampPropertyValue>(value, error, false, false); + if (!typedValue) { + return error; + } + + setHeatmapColor(*typedValue); + return nullopt; + + } + + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + + if (property == Property::HeatmapRadiusTransition) { + setHeatmapRadiusTransition(*transition); + return nullopt; + } + + if (property == Property::HeatmapWeightTransition) { + setHeatmapWeightTransition(*transition); + return nullopt; + } + + if (property == Property::HeatmapIntensityTransition) { + setHeatmapIntensityTransition(*transition); + return nullopt; + } + + if (property == Property::HeatmapColorTransition) { + setHeatmapColorTransition(*transition); + return nullopt; + } + + if (property == Property::HeatmapOpacityTransition) { + setHeatmapOpacityTransition(*transition); + return nullopt; + } + + + return Error { "layer doesn't support this property" }; +} + +optional<Error> HeatmapLayer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/heatmap_layer_impl.cpp b/src/mbgl/style/layers/heatmap_layer_impl.cpp index af20888d9d..8fd0cf72b2 100644 --- a/src/mbgl/style/layers/heatmap_layer_impl.cpp +++ b/src/mbgl/style/layers/heatmap_layer_impl.cpp @@ -4,7 +4,7 @@ namespace mbgl { namespace style { bool HeatmapLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { - assert(dynamic_cast<const HeatmapLayer::Impl*>(&other)); + assert(other.type == LayerType::Heatmap); const auto& impl = static_cast<const style::HeatmapLayer::Impl&>(other); return filter != impl.filter || visibility != impl.visibility || diff --git a/src/mbgl/style/layers/heatmap_layer_properties.hpp b/src/mbgl/style/layers/heatmap_layer_properties.hpp index fe7257a78a..4d49a52c72 100644 --- a/src/mbgl/style/layers/heatmap_layer_properties.hpp +++ b/src/mbgl/style/layers/heatmap_layer_properties.hpp @@ -24,7 +24,8 @@ struct HeatmapIntensity : PaintProperty<float> { static float defaultValue() { return 1; } }; -using HeatmapColor = ColorRampProperty; +struct HeatmapColor : ColorRampProperty { +}; struct HeatmapOpacity : PaintProperty<float> { static float defaultValue() { return 1; } diff --git a/src/mbgl/style/layers/hillshade_layer.cpp b/src/mbgl/style/layers/hillshade_layer.cpp index e352ae090c..aed49f6441 100644 --- a/src/mbgl/style/layers/hillshade_layer.cpp +++ b/src/mbgl/style/layers/hillshade_layer.cpp @@ -3,6 +3,13 @@ #include <mbgl/style/layers/hillshade_layer.hpp> #include <mbgl/style/layers/hillshade_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -236,5 +243,213 @@ TransitionOptions HillshadeLayer::getHillshadeAccentColorTransition() const { return impl().paint.template get<HillshadeAccentColor>().options; } +using namespace conversion; + +optional<Error> HillshadeLayer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, + HillshadeIlluminationDirection, + HillshadeIlluminationAnchor, + HillshadeExaggeration, + HillshadeShadowColor, + HillshadeHighlightColor, + HillshadeAccentColor, + HillshadeIlluminationDirectionTransition, + HillshadeIlluminationAnchorTransition, + HillshadeExaggerationTransition, + HillshadeShadowColorTransition, + HillshadeHighlightColorTransition, + HillshadeAccentColorTransition, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + case util::hashFNV1a("hillshade-illumination-direction"): + if (name == "hillshade-illumination-direction") { + property = Property::HillshadeIlluminationDirection; + } + break; + case util::hashFNV1a("hillshade-illumination-direction-transition"): + if (name == "hillshade-illumination-direction-transition") { + property = Property::HillshadeIlluminationDirectionTransition; + } + break; + case util::hashFNV1a("hillshade-illumination-anchor"): + if (name == "hillshade-illumination-anchor") { + property = Property::HillshadeIlluminationAnchor; + } + break; + case util::hashFNV1a("hillshade-illumination-anchor-transition"): + if (name == "hillshade-illumination-anchor-transition") { + property = Property::HillshadeIlluminationAnchorTransition; + } + break; + case util::hashFNV1a("hillshade-exaggeration"): + if (name == "hillshade-exaggeration") { + property = Property::HillshadeExaggeration; + } + break; + case util::hashFNV1a("hillshade-exaggeration-transition"): + if (name == "hillshade-exaggeration-transition") { + property = Property::HillshadeExaggerationTransition; + } + break; + case util::hashFNV1a("hillshade-shadow-color"): + if (name == "hillshade-shadow-color") { + property = Property::HillshadeShadowColor; + } + break; + case util::hashFNV1a("hillshade-shadow-color-transition"): + if (name == "hillshade-shadow-color-transition") { + property = Property::HillshadeShadowColorTransition; + } + break; + case util::hashFNV1a("hillshade-highlight-color"): + if (name == "hillshade-highlight-color") { + property = Property::HillshadeHighlightColor; + } + break; + case util::hashFNV1a("hillshade-highlight-color-transition"): + if (name == "hillshade-highlight-color-transition") { + property = Property::HillshadeHighlightColorTransition; + } + break; + case util::hashFNV1a("hillshade-accent-color"): + if (name == "hillshade-accent-color") { + property = Property::HillshadeAccentColor; + } + break; + case util::hashFNV1a("hillshade-accent-color-transition"): + if (name == "hillshade-accent-color-transition") { + property = Property::HillshadeAccentColorTransition; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::HillshadeIlluminationDirection || property == Property::HillshadeExaggeration) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::HillshadeIlluminationDirection) { + setHillshadeIlluminationDirection(*typedValue); + return nullopt; + } + + if (property == Property::HillshadeExaggeration) { + setHillshadeExaggeration(*typedValue); + return nullopt; + } + + } + + if (property == Property::HillshadeIlluminationAnchor) { + Error error; + optional<PropertyValue<HillshadeIlluminationAnchorType>> typedValue = convert<PropertyValue<HillshadeIlluminationAnchorType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setHillshadeIlluminationAnchor(*typedValue); + return nullopt; + + } + + if (property == Property::HillshadeShadowColor || property == Property::HillshadeHighlightColor || property == Property::HillshadeAccentColor) { + Error error; + optional<PropertyValue<Color>> typedValue = convert<PropertyValue<Color>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::HillshadeShadowColor) { + setHillshadeShadowColor(*typedValue); + return nullopt; + } + + if (property == Property::HillshadeHighlightColor) { + setHillshadeHighlightColor(*typedValue); + return nullopt; + } + + if (property == Property::HillshadeAccentColor) { + setHillshadeAccentColor(*typedValue); + return nullopt; + } + + } + + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + + if (property == Property::HillshadeIlluminationDirectionTransition) { + setHillshadeIlluminationDirectionTransition(*transition); + return nullopt; + } + + if (property == Property::HillshadeIlluminationAnchorTransition) { + setHillshadeIlluminationAnchorTransition(*transition); + return nullopt; + } + + if (property == Property::HillshadeExaggerationTransition) { + setHillshadeExaggerationTransition(*transition); + return nullopt; + } + + if (property == Property::HillshadeShadowColorTransition) { + setHillshadeShadowColorTransition(*transition); + return nullopt; + } + + if (property == Property::HillshadeHighlightColorTransition) { + setHillshadeHighlightColorTransition(*transition); + return nullopt; + } + + if (property == Property::HillshadeAccentColorTransition) { + setHillshadeAccentColorTransition(*transition); + return nullopt; + } + + + return Error { "layer doesn't support this property" }; +} + +optional<Error> HillshadeLayer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs index a9b6d9d02d..b5fb1a97a4 100644 --- a/src/mbgl/style/layers/layer.cpp.ejs +++ b/src/mbgl/style/layers/layer.cpp.ejs @@ -8,12 +8,13 @@ #include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer.hpp> #include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> -<% if (type === 'heatmap') { -%> -// for constructing default heatmap-color ramp expression from style JSON -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> #include <mbgl/style/conversion/json.hpp> -<% } -%> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -175,5 +176,145 @@ TransitionOptions <%- camelize(type) %>Layer::get<%- camelize(property.name) %>T } <% } -%> +using namespace conversion; + +optional<Error> <%- camelize(type) %>Layer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, +<% for (const property of paintProperties) { -%> + <%- camelize(property.name) %>, +<% } -%> +<% for (const property of paintProperties) { -%> + <%- camelize(property.name) %>Transition, +<% } -%> + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + <% for (const property of paintProperties) { -%> +case util::hashFNV1a("<%- property.name %>"): + if (name == "<%- property.name %>") { + property = Property::<%- camelize(property.name) %>; + } + break; + case util::hashFNV1a("<%- property.name %>-transition"): + if (name == "<%- property.name %>-transition") { + property = Property::<%- camelize(property.name) %>Transition; + } + break; + <% } %> + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + <% + const paintConversions = {}; + for (const property of paintProperties) { + const dataDriven = property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven'; + const convertTokens = property.name === 'icon-image' || property.name === 'text-field'; + const conversion = `optional<${propertyValueType(property)}> typedValue = convert<${propertyValueType(property)}>(value, error, ${dataDriven}, ${convertTokens})`; + paintConversions[conversion] = paintConversions[conversion] || []; + paintConversions[conversion].push(property); + } + -%> + <% for (const key in paintConversions) { + const properties = paintConversions[key]; + %> + if (<%- properties.map(p => `property == Property::${camelize(p.name)}`).join(' || ') %>) { + Error error; + <%- key %>; + if (!typedValue) { + return error; + } + <% if (properties.length == 1) { %> + set<%- camelize(properties[0].name) %>(*typedValue); + return nullopt; + <% } else for (const property of properties) { %> + if (property == Property::<%- camelize(property.name) %>) { + set<%- camelize(property.name) %>(*typedValue); + return nullopt; + } + <% } %> + } + <% } %> + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + <% for (const property of paintProperties) { %> + if (property == Property::<%- camelize(property.name) %>Transition) { + set<%- camelize(property.name) %>Transition(*transition); + return nullopt; + } + <% } %> + + return Error { "layer doesn't support this property" }; +} + +optional<Error> <%- camelize(type) %>Layer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, +<% for (const property of layoutProperties) { -%> + <%- camelize(property.name) %>, +<% } -%> + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + <% for (const property of layoutProperties) { %> + case util::hashFNV1a("<%- property.name %>"): + if (name == "<%- property.name %>") { + property = Property::<%- camelize(property.name) %>; + } + break; + <% } %> + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + <% + const layoutConversions = {}; + for (const property of layoutProperties) { + const dataDriven = property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven'; + const convertTokens = property.name === 'icon-image' || property.name === 'text-field'; + const conversion = `optional<${propertyValueType(property)}> typedValue = convert<${propertyValueType(property)}>(value, error, ${dataDriven}, ${convertTokens})`; + layoutConversions[conversion] = layoutConversions[conversion] || []; + layoutConversions[conversion].push(property); + } + -%> + <% for (const key in layoutConversions) { + const properties = layoutConversions[key]; + %> + if (<%- properties.map(p => `property == Property::${camelize(p.name)}`).join(' || ') %>) { + Error error; + <%- key %>; + if (!typedValue) { + return error; + } + <% if (properties.length == 1) { %> + set<%- camelize(properties[0].name) %>(*typedValue); + return nullopt; + <% } else for (const property of properties) { %> + if (property == Property::<%- camelize(property.name) %>) { + set<%- camelize(property.name) %>(*typedValue); + return nullopt; + } + <% } %> + } + <% } %> + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs index 5b774933a6..694d9a62b7 100644 --- a/src/mbgl/style/layers/layer_properties.hpp.ejs +++ b/src/mbgl/style/layers/layer_properties.hpp.ejs @@ -26,7 +26,8 @@ struct <%- camelize(property.name) %> : <%- layoutPropertyType(property, type) % <% } -%> <% for (const property of paintProperties) { -%> <% if (property['property-type'] === 'color-ramp') { -%> -using <%- camelize(property.name) %> = ColorRampProperty; +struct <%- camelize(property.name) %> : ColorRampProperty { +}; <% } else { -%> struct <%- camelize(property.name) %> : <%- paintPropertyType(property, type) %> { static <%- evaluatedType(property) %> defaultValue() { return <%- defaultValue(property) %>; } diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp index 0cda849c0f..1b84c2d73e 100644 --- a/src/mbgl/style/layers/line_layer.cpp +++ b/src/mbgl/style/layers/line_layer.cpp @@ -3,6 +3,13 @@ #include <mbgl/style/layers/line_layer.hpp> #include <mbgl/style/layers/line_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -431,5 +438,391 @@ TransitionOptions LineLayer::getLinePatternTransition() const { return impl().paint.template get<LinePattern>().options; } +using namespace conversion; + +optional<Error> LineLayer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, + LineOpacity, + LineColor, + LineTranslate, + LineTranslateAnchor, + LineWidth, + LineGapWidth, + LineOffset, + LineBlur, + LineDasharray, + LinePattern, + LineOpacityTransition, + LineColorTransition, + LineTranslateTransition, + LineTranslateAnchorTransition, + LineWidthTransition, + LineGapWidthTransition, + LineOffsetTransition, + LineBlurTransition, + LineDasharrayTransition, + LinePatternTransition, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + case util::hashFNV1a("line-opacity"): + if (name == "line-opacity") { + property = Property::LineOpacity; + } + break; + case util::hashFNV1a("line-opacity-transition"): + if (name == "line-opacity-transition") { + property = Property::LineOpacityTransition; + } + break; + case util::hashFNV1a("line-color"): + if (name == "line-color") { + property = Property::LineColor; + } + break; + case util::hashFNV1a("line-color-transition"): + if (name == "line-color-transition") { + property = Property::LineColorTransition; + } + break; + case util::hashFNV1a("line-translate"): + if (name == "line-translate") { + property = Property::LineTranslate; + } + break; + case util::hashFNV1a("line-translate-transition"): + if (name == "line-translate-transition") { + property = Property::LineTranslateTransition; + } + break; + case util::hashFNV1a("line-translate-anchor"): + if (name == "line-translate-anchor") { + property = Property::LineTranslateAnchor; + } + break; + case util::hashFNV1a("line-translate-anchor-transition"): + if (name == "line-translate-anchor-transition") { + property = Property::LineTranslateAnchorTransition; + } + break; + case util::hashFNV1a("line-width"): + if (name == "line-width") { + property = Property::LineWidth; + } + break; + case util::hashFNV1a("line-width-transition"): + if (name == "line-width-transition") { + property = Property::LineWidthTransition; + } + break; + case util::hashFNV1a("line-gap-width"): + if (name == "line-gap-width") { + property = Property::LineGapWidth; + } + break; + case util::hashFNV1a("line-gap-width-transition"): + if (name == "line-gap-width-transition") { + property = Property::LineGapWidthTransition; + } + break; + case util::hashFNV1a("line-offset"): + if (name == "line-offset") { + property = Property::LineOffset; + } + break; + case util::hashFNV1a("line-offset-transition"): + if (name == "line-offset-transition") { + property = Property::LineOffsetTransition; + } + break; + case util::hashFNV1a("line-blur"): + if (name == "line-blur") { + property = Property::LineBlur; + } + break; + case util::hashFNV1a("line-blur-transition"): + if (name == "line-blur-transition") { + property = Property::LineBlurTransition; + } + break; + case util::hashFNV1a("line-dasharray"): + if (name == "line-dasharray") { + property = Property::LineDasharray; + } + break; + case util::hashFNV1a("line-dasharray-transition"): + if (name == "line-dasharray-transition") { + property = Property::LineDasharrayTransition; + } + break; + case util::hashFNV1a("line-pattern"): + if (name == "line-pattern") { + property = Property::LinePattern; + } + break; + case util::hashFNV1a("line-pattern-transition"): + if (name == "line-pattern-transition") { + property = Property::LinePatternTransition; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::LineOpacity || property == Property::LineWidth || property == Property::LineGapWidth || property == Property::LineOffset || property == Property::LineBlur) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::LineOpacity) { + setLineOpacity(*typedValue); + return nullopt; + } + + if (property == Property::LineWidth) { + setLineWidth(*typedValue); + return nullopt; + } + + if (property == Property::LineGapWidth) { + setLineGapWidth(*typedValue); + return nullopt; + } + + if (property == Property::LineOffset) { + setLineOffset(*typedValue); + return nullopt; + } + + if (property == Property::LineBlur) { + setLineBlur(*typedValue); + return nullopt; + } + + } + + if (property == Property::LineColor) { + Error error; + optional<PropertyValue<Color>> typedValue = convert<PropertyValue<Color>>(value, error, true, false); + if (!typedValue) { + return error; + } + + setLineColor(*typedValue); + return nullopt; + + } + + if (property == Property::LineTranslate) { + Error error; + optional<PropertyValue<std::array<float, 2>>> typedValue = convert<PropertyValue<std::array<float, 2>>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setLineTranslate(*typedValue); + return nullopt; + + } + + if (property == Property::LineTranslateAnchor) { + Error error; + optional<PropertyValue<TranslateAnchorType>> typedValue = convert<PropertyValue<TranslateAnchorType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setLineTranslateAnchor(*typedValue); + return nullopt; + + } + + if (property == Property::LineDasharray) { + Error error; + optional<PropertyValue<std::vector<float>>> typedValue = convert<PropertyValue<std::vector<float>>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setLineDasharray(*typedValue); + return nullopt; + + } + + if (property == Property::LinePattern) { + Error error; + optional<PropertyValue<std::string>> typedValue = convert<PropertyValue<std::string>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setLinePattern(*typedValue); + return nullopt; + + } + + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + + if (property == Property::LineOpacityTransition) { + setLineOpacityTransition(*transition); + return nullopt; + } + + if (property == Property::LineColorTransition) { + setLineColorTransition(*transition); + return nullopt; + } + + if (property == Property::LineTranslateTransition) { + setLineTranslateTransition(*transition); + return nullopt; + } + + if (property == Property::LineTranslateAnchorTransition) { + setLineTranslateAnchorTransition(*transition); + return nullopt; + } + + if (property == Property::LineWidthTransition) { + setLineWidthTransition(*transition); + return nullopt; + } + + if (property == Property::LineGapWidthTransition) { + setLineGapWidthTransition(*transition); + return nullopt; + } + + if (property == Property::LineOffsetTransition) { + setLineOffsetTransition(*transition); + return nullopt; + } + + if (property == Property::LineBlurTransition) { + setLineBlurTransition(*transition); + return nullopt; + } + + if (property == Property::LineDasharrayTransition) { + setLineDasharrayTransition(*transition); + return nullopt; + } + + if (property == Property::LinePatternTransition) { + setLinePatternTransition(*transition); + return nullopt; + } + + + return Error { "layer doesn't support this property" }; +} + +optional<Error> LineLayer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, + LineCap, + LineJoin, + LineMiterLimit, + LineRoundLimit, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + + case util::hashFNV1a("line-cap"): + if (name == "line-cap") { + property = Property::LineCap; + } + break; + + case util::hashFNV1a("line-join"): + if (name == "line-join") { + property = Property::LineJoin; + } + break; + + case util::hashFNV1a("line-miter-limit"): + if (name == "line-miter-limit") { + property = Property::LineMiterLimit; + } + break; + + case util::hashFNV1a("line-round-limit"): + if (name == "line-round-limit") { + property = Property::LineRoundLimit; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::LineCap) { + Error error; + optional<PropertyValue<LineCapType>> typedValue = convert<PropertyValue<LineCapType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setLineCap(*typedValue); + return nullopt; + + } + + if (property == Property::LineJoin) { + Error error; + optional<PropertyValue<LineJoinType>> typedValue = convert<PropertyValue<LineJoinType>>(value, error, true, false); + if (!typedValue) { + return error; + } + + setLineJoin(*typedValue); + return nullopt; + + } + + if (property == Property::LineMiterLimit || property == Property::LineRoundLimit) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::LineMiterLimit) { + setLineMiterLimit(*typedValue); + return nullopt; + } + + if (property == Property::LineRoundLimit) { + setLineRoundLimit(*typedValue); + return nullopt; + } + + } + + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/line_layer_impl.cpp b/src/mbgl/style/layers/line_layer_impl.cpp index bee88d6a47..68cd3a8f49 100644 --- a/src/mbgl/style/layers/line_layer_impl.cpp +++ b/src/mbgl/style/layers/line_layer_impl.cpp @@ -4,7 +4,7 @@ namespace mbgl { namespace style { bool LineLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { - assert(dynamic_cast<const LineLayer::Impl*>(&other)); + assert(other.type == LayerType::Line); const auto& impl = static_cast<const style::LineLayer::Impl&>(other); return filter != impl.filter || visibility != impl.visibility || diff --git a/src/mbgl/style/layers/raster_layer.cpp b/src/mbgl/style/layers/raster_layer.cpp index e5b03df0f6..7bd01c92e1 100644 --- a/src/mbgl/style/layers/raster_layer.cpp +++ b/src/mbgl/style/layers/raster_layer.cpp @@ -3,6 +3,13 @@ #include <mbgl/style/layers/raster_layer.hpp> #include <mbgl/style/layers/raster_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -290,5 +297,248 @@ TransitionOptions RasterLayer::getRasterFadeDurationTransition() const { return impl().paint.template get<RasterFadeDuration>().options; } +using namespace conversion; + +optional<Error> RasterLayer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, + RasterOpacity, + RasterHueRotate, + RasterBrightnessMin, + RasterBrightnessMax, + RasterSaturation, + RasterContrast, + RasterResampling, + RasterFadeDuration, + RasterOpacityTransition, + RasterHueRotateTransition, + RasterBrightnessMinTransition, + RasterBrightnessMaxTransition, + RasterSaturationTransition, + RasterContrastTransition, + RasterResamplingTransition, + RasterFadeDurationTransition, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + case util::hashFNV1a("raster-opacity"): + if (name == "raster-opacity") { + property = Property::RasterOpacity; + } + break; + case util::hashFNV1a("raster-opacity-transition"): + if (name == "raster-opacity-transition") { + property = Property::RasterOpacityTransition; + } + break; + case util::hashFNV1a("raster-hue-rotate"): + if (name == "raster-hue-rotate") { + property = Property::RasterHueRotate; + } + break; + case util::hashFNV1a("raster-hue-rotate-transition"): + if (name == "raster-hue-rotate-transition") { + property = Property::RasterHueRotateTransition; + } + break; + case util::hashFNV1a("raster-brightness-min"): + if (name == "raster-brightness-min") { + property = Property::RasterBrightnessMin; + } + break; + case util::hashFNV1a("raster-brightness-min-transition"): + if (name == "raster-brightness-min-transition") { + property = Property::RasterBrightnessMinTransition; + } + break; + case util::hashFNV1a("raster-brightness-max"): + if (name == "raster-brightness-max") { + property = Property::RasterBrightnessMax; + } + break; + case util::hashFNV1a("raster-brightness-max-transition"): + if (name == "raster-brightness-max-transition") { + property = Property::RasterBrightnessMaxTransition; + } + break; + case util::hashFNV1a("raster-saturation"): + if (name == "raster-saturation") { + property = Property::RasterSaturation; + } + break; + case util::hashFNV1a("raster-saturation-transition"): + if (name == "raster-saturation-transition") { + property = Property::RasterSaturationTransition; + } + break; + case util::hashFNV1a("raster-contrast"): + if (name == "raster-contrast") { + property = Property::RasterContrast; + } + break; + case util::hashFNV1a("raster-contrast-transition"): + if (name == "raster-contrast-transition") { + property = Property::RasterContrastTransition; + } + break; + case util::hashFNV1a("raster-resampling"): + if (name == "raster-resampling") { + property = Property::RasterResampling; + } + break; + case util::hashFNV1a("raster-resampling-transition"): + if (name == "raster-resampling-transition") { + property = Property::RasterResamplingTransition; + } + break; + case util::hashFNV1a("raster-fade-duration"): + if (name == "raster-fade-duration") { + property = Property::RasterFadeDuration; + } + break; + case util::hashFNV1a("raster-fade-duration-transition"): + if (name == "raster-fade-duration-transition") { + property = Property::RasterFadeDurationTransition; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::RasterOpacity || property == Property::RasterHueRotate || property == Property::RasterBrightnessMin || property == Property::RasterBrightnessMax || property == Property::RasterSaturation || property == Property::RasterContrast || property == Property::RasterFadeDuration) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::RasterOpacity) { + setRasterOpacity(*typedValue); + return nullopt; + } + + if (property == Property::RasterHueRotate) { + setRasterHueRotate(*typedValue); + return nullopt; + } + + if (property == Property::RasterBrightnessMin) { + setRasterBrightnessMin(*typedValue); + return nullopt; + } + + if (property == Property::RasterBrightnessMax) { + setRasterBrightnessMax(*typedValue); + return nullopt; + } + + if (property == Property::RasterSaturation) { + setRasterSaturation(*typedValue); + return nullopt; + } + + if (property == Property::RasterContrast) { + setRasterContrast(*typedValue); + return nullopt; + } + + if (property == Property::RasterFadeDuration) { + setRasterFadeDuration(*typedValue); + return nullopt; + } + + } + + if (property == Property::RasterResampling) { + Error error; + optional<PropertyValue<RasterResamplingType>> typedValue = convert<PropertyValue<RasterResamplingType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setRasterResampling(*typedValue); + return nullopt; + + } + + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + + if (property == Property::RasterOpacityTransition) { + setRasterOpacityTransition(*transition); + return nullopt; + } + + if (property == Property::RasterHueRotateTransition) { + setRasterHueRotateTransition(*transition); + return nullopt; + } + + if (property == Property::RasterBrightnessMinTransition) { + setRasterBrightnessMinTransition(*transition); + return nullopt; + } + + if (property == Property::RasterBrightnessMaxTransition) { + setRasterBrightnessMaxTransition(*transition); + return nullopt; + } + + if (property == Property::RasterSaturationTransition) { + setRasterSaturationTransition(*transition); + return nullopt; + } + + if (property == Property::RasterContrastTransition) { + setRasterContrastTransition(*transition); + return nullopt; + } + + if (property == Property::RasterResamplingTransition) { + setRasterResamplingTransition(*transition); + return nullopt; + } + + if (property == Property::RasterFadeDurationTransition) { + setRasterFadeDurationTransition(*transition); + return nullopt; + } + + + return Error { "layer doesn't support this property" }; +} + +optional<Error> RasterLayer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index c416c6a6c5..4ea138a7f5 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -3,6 +3,13 @@ #include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/style/layer_observer.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> +#include <mbgl/style/conversion/constant.hpp> +#include <mbgl/style/conversion/property_value.hpp> +#include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion/json.hpp> +#include <mbgl/style/conversion_impl.hpp> +#include <mbgl/util/fnv_hash.hpp> namespace mbgl { namespace style { @@ -1051,5 +1058,937 @@ TransitionOptions SymbolLayer::getTextTranslateAnchorTransition() const { return impl().paint.template get<TextTranslateAnchor>().options; } +using namespace conversion; + +optional<Error> SymbolLayer::setPaintProperty(const std::string& name, const Convertible& value) { + enum class Property { + Unknown, + IconOpacity, + IconColor, + IconHaloColor, + IconHaloWidth, + IconHaloBlur, + IconTranslate, + IconTranslateAnchor, + TextOpacity, + TextColor, + TextHaloColor, + TextHaloWidth, + TextHaloBlur, + TextTranslate, + TextTranslateAnchor, + IconOpacityTransition, + IconColorTransition, + IconHaloColorTransition, + IconHaloWidthTransition, + IconHaloBlurTransition, + IconTranslateTransition, + IconTranslateAnchorTransition, + TextOpacityTransition, + TextColorTransition, + TextHaloColorTransition, + TextHaloWidthTransition, + TextHaloBlurTransition, + TextTranslateTransition, + TextTranslateAnchorTransition, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + case util::hashFNV1a("icon-opacity"): + if (name == "icon-opacity") { + property = Property::IconOpacity; + } + break; + case util::hashFNV1a("icon-opacity-transition"): + if (name == "icon-opacity-transition") { + property = Property::IconOpacityTransition; + } + break; + case util::hashFNV1a("icon-color"): + if (name == "icon-color") { + property = Property::IconColor; + } + break; + case util::hashFNV1a("icon-color-transition"): + if (name == "icon-color-transition") { + property = Property::IconColorTransition; + } + break; + case util::hashFNV1a("icon-halo-color"): + if (name == "icon-halo-color") { + property = Property::IconHaloColor; + } + break; + case util::hashFNV1a("icon-halo-color-transition"): + if (name == "icon-halo-color-transition") { + property = Property::IconHaloColorTransition; + } + break; + case util::hashFNV1a("icon-halo-width"): + if (name == "icon-halo-width") { + property = Property::IconHaloWidth; + } + break; + case util::hashFNV1a("icon-halo-width-transition"): + if (name == "icon-halo-width-transition") { + property = Property::IconHaloWidthTransition; + } + break; + case util::hashFNV1a("icon-halo-blur"): + if (name == "icon-halo-blur") { + property = Property::IconHaloBlur; + } + break; + case util::hashFNV1a("icon-halo-blur-transition"): + if (name == "icon-halo-blur-transition") { + property = Property::IconHaloBlurTransition; + } + break; + case util::hashFNV1a("icon-translate"): + if (name == "icon-translate") { + property = Property::IconTranslate; + } + break; + case util::hashFNV1a("icon-translate-transition"): + if (name == "icon-translate-transition") { + property = Property::IconTranslateTransition; + } + break; + case util::hashFNV1a("icon-translate-anchor"): + if (name == "icon-translate-anchor") { + property = Property::IconTranslateAnchor; + } + break; + case util::hashFNV1a("icon-translate-anchor-transition"): + if (name == "icon-translate-anchor-transition") { + property = Property::IconTranslateAnchorTransition; + } + break; + case util::hashFNV1a("text-opacity"): + if (name == "text-opacity") { + property = Property::TextOpacity; + } + break; + case util::hashFNV1a("text-opacity-transition"): + if (name == "text-opacity-transition") { + property = Property::TextOpacityTransition; + } + break; + case util::hashFNV1a("text-color"): + if (name == "text-color") { + property = Property::TextColor; + } + break; + case util::hashFNV1a("text-color-transition"): + if (name == "text-color-transition") { + property = Property::TextColorTransition; + } + break; + case util::hashFNV1a("text-halo-color"): + if (name == "text-halo-color") { + property = Property::TextHaloColor; + } + break; + case util::hashFNV1a("text-halo-color-transition"): + if (name == "text-halo-color-transition") { + property = Property::TextHaloColorTransition; + } + break; + case util::hashFNV1a("text-halo-width"): + if (name == "text-halo-width") { + property = Property::TextHaloWidth; + } + break; + case util::hashFNV1a("text-halo-width-transition"): + if (name == "text-halo-width-transition") { + property = Property::TextHaloWidthTransition; + } + break; + case util::hashFNV1a("text-halo-blur"): + if (name == "text-halo-blur") { + property = Property::TextHaloBlur; + } + break; + case util::hashFNV1a("text-halo-blur-transition"): + if (name == "text-halo-blur-transition") { + property = Property::TextHaloBlurTransition; + } + break; + case util::hashFNV1a("text-translate"): + if (name == "text-translate") { + property = Property::TextTranslate; + } + break; + case util::hashFNV1a("text-translate-transition"): + if (name == "text-translate-transition") { + property = Property::TextTranslateTransition; + } + break; + case util::hashFNV1a("text-translate-anchor"): + if (name == "text-translate-anchor") { + property = Property::TextTranslateAnchor; + } + break; + case util::hashFNV1a("text-translate-anchor-transition"): + if (name == "text-translate-anchor-transition") { + property = Property::TextTranslateAnchorTransition; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::IconOpacity || property == Property::IconHaloWidth || property == Property::IconHaloBlur || property == Property::TextOpacity || property == Property::TextHaloWidth || property == Property::TextHaloBlur) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::IconOpacity) { + setIconOpacity(*typedValue); + return nullopt; + } + + if (property == Property::IconHaloWidth) { + setIconHaloWidth(*typedValue); + return nullopt; + } + + if (property == Property::IconHaloBlur) { + setIconHaloBlur(*typedValue); + return nullopt; + } + + if (property == Property::TextOpacity) { + setTextOpacity(*typedValue); + return nullopt; + } + + if (property == Property::TextHaloWidth) { + setTextHaloWidth(*typedValue); + return nullopt; + } + + if (property == Property::TextHaloBlur) { + setTextHaloBlur(*typedValue); + return nullopt; + } + + } + + if (property == Property::IconColor || property == Property::IconHaloColor || property == Property::TextColor || property == Property::TextHaloColor) { + Error error; + optional<PropertyValue<Color>> typedValue = convert<PropertyValue<Color>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::IconColor) { + setIconColor(*typedValue); + return nullopt; + } + + if (property == Property::IconHaloColor) { + setIconHaloColor(*typedValue); + return nullopt; + } + + if (property == Property::TextColor) { + setTextColor(*typedValue); + return nullopt; + } + + if (property == Property::TextHaloColor) { + setTextHaloColor(*typedValue); + return nullopt; + } + + } + + if (property == Property::IconTranslate || property == Property::TextTranslate) { + Error error; + optional<PropertyValue<std::array<float, 2>>> typedValue = convert<PropertyValue<std::array<float, 2>>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::IconTranslate) { + setIconTranslate(*typedValue); + return nullopt; + } + + if (property == Property::TextTranslate) { + setTextTranslate(*typedValue); + return nullopt; + } + + } + + if (property == Property::IconTranslateAnchor || property == Property::TextTranslateAnchor) { + Error error; + optional<PropertyValue<TranslateAnchorType>> typedValue = convert<PropertyValue<TranslateAnchorType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::IconTranslateAnchor) { + setIconTranslateAnchor(*typedValue); + return nullopt; + } + + if (property == Property::TextTranslateAnchor) { + setTextTranslateAnchor(*typedValue); + return nullopt; + } + + } + + + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); + if (!transition) { + return error; + } + + if (property == Property::IconOpacityTransition) { + setIconOpacityTransition(*transition); + return nullopt; + } + + if (property == Property::IconColorTransition) { + setIconColorTransition(*transition); + return nullopt; + } + + if (property == Property::IconHaloColorTransition) { + setIconHaloColorTransition(*transition); + return nullopt; + } + + if (property == Property::IconHaloWidthTransition) { + setIconHaloWidthTransition(*transition); + return nullopt; + } + + if (property == Property::IconHaloBlurTransition) { + setIconHaloBlurTransition(*transition); + return nullopt; + } + + if (property == Property::IconTranslateTransition) { + setIconTranslateTransition(*transition); + return nullopt; + } + + if (property == Property::IconTranslateAnchorTransition) { + setIconTranslateAnchorTransition(*transition); + return nullopt; + } + + if (property == Property::TextOpacityTransition) { + setTextOpacityTransition(*transition); + return nullopt; + } + + if (property == Property::TextColorTransition) { + setTextColorTransition(*transition); + return nullopt; + } + + if (property == Property::TextHaloColorTransition) { + setTextHaloColorTransition(*transition); + return nullopt; + } + + if (property == Property::TextHaloWidthTransition) { + setTextHaloWidthTransition(*transition); + return nullopt; + } + + if (property == Property::TextHaloBlurTransition) { + setTextHaloBlurTransition(*transition); + return nullopt; + } + + if (property == Property::TextTranslateTransition) { + setTextTranslateTransition(*transition); + return nullopt; + } + + if (property == Property::TextTranslateAnchorTransition) { + setTextTranslateAnchorTransition(*transition); + return nullopt; + } + + + return Error { "layer doesn't support this property" }; +} + +optional<Error> SymbolLayer::setLayoutProperty(const std::string& name, const Convertible& value) { + if (name == "visibility") { + return Layer::setVisibility(value); + } + + enum class Property { + Unknown, + SymbolPlacement, + SymbolSpacing, + SymbolAvoidEdges, + IconAllowOverlap, + IconIgnorePlacement, + IconOptional, + IconRotationAlignment, + IconSize, + IconTextFit, + IconTextFitPadding, + IconImage, + IconRotate, + IconPadding, + IconKeepUpright, + IconOffset, + IconAnchor, + IconPitchAlignment, + TextPitchAlignment, + TextRotationAlignment, + TextField, + TextFont, + TextSize, + TextMaxWidth, + TextLineHeight, + TextLetterSpacing, + TextJustify, + TextAnchor, + TextMaxAngle, + TextRotate, + TextPadding, + TextKeepUpright, + TextTransform, + TextOffset, + TextAllowOverlap, + TextIgnorePlacement, + TextOptional, + }; + + Property property = Property::Unknown; + switch (util::hashFNV1a(name.c_str())) { + + case util::hashFNV1a("symbol-placement"): + if (name == "symbol-placement") { + property = Property::SymbolPlacement; + } + break; + + case util::hashFNV1a("symbol-spacing"): + if (name == "symbol-spacing") { + property = Property::SymbolSpacing; + } + break; + + case util::hashFNV1a("symbol-avoid-edges"): + if (name == "symbol-avoid-edges") { + property = Property::SymbolAvoidEdges; + } + break; + + case util::hashFNV1a("icon-allow-overlap"): + if (name == "icon-allow-overlap") { + property = Property::IconAllowOverlap; + } + break; + + case util::hashFNV1a("icon-ignore-placement"): + if (name == "icon-ignore-placement") { + property = Property::IconIgnorePlacement; + } + break; + + case util::hashFNV1a("icon-optional"): + if (name == "icon-optional") { + property = Property::IconOptional; + } + break; + + case util::hashFNV1a("icon-rotation-alignment"): + if (name == "icon-rotation-alignment") { + property = Property::IconRotationAlignment; + } + break; + + case util::hashFNV1a("icon-size"): + if (name == "icon-size") { + property = Property::IconSize; + } + break; + + case util::hashFNV1a("icon-text-fit"): + if (name == "icon-text-fit") { + property = Property::IconTextFit; + } + break; + + case util::hashFNV1a("icon-text-fit-padding"): + if (name == "icon-text-fit-padding") { + property = Property::IconTextFitPadding; + } + break; + + case util::hashFNV1a("icon-image"): + if (name == "icon-image") { + property = Property::IconImage; + } + break; + + case util::hashFNV1a("icon-rotate"): + if (name == "icon-rotate") { + property = Property::IconRotate; + } + break; + + case util::hashFNV1a("icon-padding"): + if (name == "icon-padding") { + property = Property::IconPadding; + } + break; + + case util::hashFNV1a("icon-keep-upright"): + if (name == "icon-keep-upright") { + property = Property::IconKeepUpright; + } + break; + + case util::hashFNV1a("icon-offset"): + if (name == "icon-offset") { + property = Property::IconOffset; + } + break; + + case util::hashFNV1a("icon-anchor"): + if (name == "icon-anchor") { + property = Property::IconAnchor; + } + break; + + case util::hashFNV1a("icon-pitch-alignment"): + if (name == "icon-pitch-alignment") { + property = Property::IconPitchAlignment; + } + break; + + case util::hashFNV1a("text-pitch-alignment"): + if (name == "text-pitch-alignment") { + property = Property::TextPitchAlignment; + } + break; + + case util::hashFNV1a("text-rotation-alignment"): + if (name == "text-rotation-alignment") { + property = Property::TextRotationAlignment; + } + break; + + case util::hashFNV1a("text-field"): + if (name == "text-field") { + property = Property::TextField; + } + break; + + case util::hashFNV1a("text-font"): + if (name == "text-font") { + property = Property::TextFont; + } + break; + + case util::hashFNV1a("text-size"): + if (name == "text-size") { + property = Property::TextSize; + } + break; + + case util::hashFNV1a("text-max-width"): + if (name == "text-max-width") { + property = Property::TextMaxWidth; + } + break; + + case util::hashFNV1a("text-line-height"): + if (name == "text-line-height") { + property = Property::TextLineHeight; + } + break; + + case util::hashFNV1a("text-letter-spacing"): + if (name == "text-letter-spacing") { + property = Property::TextLetterSpacing; + } + break; + + case util::hashFNV1a("text-justify"): + if (name == "text-justify") { + property = Property::TextJustify; + } + break; + + case util::hashFNV1a("text-anchor"): + if (name == "text-anchor") { + property = Property::TextAnchor; + } + break; + + case util::hashFNV1a("text-max-angle"): + if (name == "text-max-angle") { + property = Property::TextMaxAngle; + } + break; + + case util::hashFNV1a("text-rotate"): + if (name == "text-rotate") { + property = Property::TextRotate; + } + break; + + case util::hashFNV1a("text-padding"): + if (name == "text-padding") { + property = Property::TextPadding; + } + break; + + case util::hashFNV1a("text-keep-upright"): + if (name == "text-keep-upright") { + property = Property::TextKeepUpright; + } + break; + + case util::hashFNV1a("text-transform"): + if (name == "text-transform") { + property = Property::TextTransform; + } + break; + + case util::hashFNV1a("text-offset"): + if (name == "text-offset") { + property = Property::TextOffset; + } + break; + + case util::hashFNV1a("text-allow-overlap"): + if (name == "text-allow-overlap") { + property = Property::TextAllowOverlap; + } + break; + + case util::hashFNV1a("text-ignore-placement"): + if (name == "text-ignore-placement") { + property = Property::TextIgnorePlacement; + } + break; + + case util::hashFNV1a("text-optional"): + if (name == "text-optional") { + property = Property::TextOptional; + } + break; + + } + + if (property == Property::Unknown) { + return Error { "layer doesn't support this property" }; + } + + + if (property == Property::SymbolPlacement) { + Error error; + optional<PropertyValue<SymbolPlacementType>> typedValue = convert<PropertyValue<SymbolPlacementType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setSymbolPlacement(*typedValue); + return nullopt; + + } + + if (property == Property::SymbolSpacing || property == Property::IconPadding || property == Property::TextLineHeight || property == Property::TextMaxAngle || property == Property::TextPadding) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::SymbolSpacing) { + setSymbolSpacing(*typedValue); + return nullopt; + } + + if (property == Property::IconPadding) { + setIconPadding(*typedValue); + return nullopt; + } + + if (property == Property::TextLineHeight) { + setTextLineHeight(*typedValue); + return nullopt; + } + + if (property == Property::TextMaxAngle) { + setTextMaxAngle(*typedValue); + return nullopt; + } + + if (property == Property::TextPadding) { + setTextPadding(*typedValue); + return nullopt; + } + + } + + if (property == Property::SymbolAvoidEdges || property == Property::IconAllowOverlap || property == Property::IconIgnorePlacement || property == Property::IconOptional || property == Property::IconKeepUpright || property == Property::TextKeepUpright || property == Property::TextAllowOverlap || property == Property::TextIgnorePlacement || property == Property::TextOptional) { + Error error; + optional<PropertyValue<bool>> typedValue = convert<PropertyValue<bool>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::SymbolAvoidEdges) { + setSymbolAvoidEdges(*typedValue); + return nullopt; + } + + if (property == Property::IconAllowOverlap) { + setIconAllowOverlap(*typedValue); + return nullopt; + } + + if (property == Property::IconIgnorePlacement) { + setIconIgnorePlacement(*typedValue); + return nullopt; + } + + if (property == Property::IconOptional) { + setIconOptional(*typedValue); + return nullopt; + } + + if (property == Property::IconKeepUpright) { + setIconKeepUpright(*typedValue); + return nullopt; + } + + if (property == Property::TextKeepUpright) { + setTextKeepUpright(*typedValue); + return nullopt; + } + + if (property == Property::TextAllowOverlap) { + setTextAllowOverlap(*typedValue); + return nullopt; + } + + if (property == Property::TextIgnorePlacement) { + setTextIgnorePlacement(*typedValue); + return nullopt; + } + + if (property == Property::TextOptional) { + setTextOptional(*typedValue); + return nullopt; + } + + } + + if (property == Property::IconRotationAlignment || property == Property::IconPitchAlignment || property == Property::TextPitchAlignment || property == Property::TextRotationAlignment) { + Error error; + optional<PropertyValue<AlignmentType>> typedValue = convert<PropertyValue<AlignmentType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::IconRotationAlignment) { + setIconRotationAlignment(*typedValue); + return nullopt; + } + + if (property == Property::IconPitchAlignment) { + setIconPitchAlignment(*typedValue); + return nullopt; + } + + if (property == Property::TextPitchAlignment) { + setTextPitchAlignment(*typedValue); + return nullopt; + } + + if (property == Property::TextRotationAlignment) { + setTextRotationAlignment(*typedValue); + return nullopt; + } + + } + + if (property == Property::IconSize || property == Property::IconRotate || property == Property::TextSize || property == Property::TextMaxWidth || property == Property::TextLetterSpacing || property == Property::TextRotate) { + Error error; + optional<PropertyValue<float>> typedValue = convert<PropertyValue<float>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::IconSize) { + setIconSize(*typedValue); + return nullopt; + } + + if (property == Property::IconRotate) { + setIconRotate(*typedValue); + return nullopt; + } + + if (property == Property::TextSize) { + setTextSize(*typedValue); + return nullopt; + } + + if (property == Property::TextMaxWidth) { + setTextMaxWidth(*typedValue); + return nullopt; + } + + if (property == Property::TextLetterSpacing) { + setTextLetterSpacing(*typedValue); + return nullopt; + } + + if (property == Property::TextRotate) { + setTextRotate(*typedValue); + return nullopt; + } + + } + + if (property == Property::IconTextFit) { + Error error; + optional<PropertyValue<IconTextFitType>> typedValue = convert<PropertyValue<IconTextFitType>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setIconTextFit(*typedValue); + return nullopt; + + } + + if (property == Property::IconTextFitPadding) { + Error error; + optional<PropertyValue<std::array<float, 4>>> typedValue = convert<PropertyValue<std::array<float, 4>>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setIconTextFitPadding(*typedValue); + return nullopt; + + } + + if (property == Property::IconImage || property == Property::TextField) { + Error error; + optional<PropertyValue<std::string>> typedValue = convert<PropertyValue<std::string>>(value, error, true, true); + if (!typedValue) { + return error; + } + + if (property == Property::IconImage) { + setIconImage(*typedValue); + return nullopt; + } + + if (property == Property::TextField) { + setTextField(*typedValue); + return nullopt; + } + + } + + if (property == Property::IconOffset || property == Property::TextOffset) { + Error error; + optional<PropertyValue<std::array<float, 2>>> typedValue = convert<PropertyValue<std::array<float, 2>>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::IconOffset) { + setIconOffset(*typedValue); + return nullopt; + } + + if (property == Property::TextOffset) { + setTextOffset(*typedValue); + return nullopt; + } + + } + + if (property == Property::IconAnchor || property == Property::TextAnchor) { + Error error; + optional<PropertyValue<SymbolAnchorType>> typedValue = convert<PropertyValue<SymbolAnchorType>>(value, error, true, false); + if (!typedValue) { + return error; + } + + if (property == Property::IconAnchor) { + setIconAnchor(*typedValue); + return nullopt; + } + + if (property == Property::TextAnchor) { + setTextAnchor(*typedValue); + return nullopt; + } + + } + + if (property == Property::TextFont) { + Error error; + optional<PropertyValue<std::vector<std::string>>> typedValue = convert<PropertyValue<std::vector<std::string>>>(value, error, true, false); + if (!typedValue) { + return error; + } + + setTextFont(*typedValue); + return nullopt; + + } + + if (property == Property::TextJustify) { + Error error; + optional<PropertyValue<TextJustifyType>> typedValue = convert<PropertyValue<TextJustifyType>>(value, error, true, false); + if (!typedValue) { + return error; + } + + setTextJustify(*typedValue); + return nullopt; + + } + + if (property == Property::TextTransform) { + Error error; + optional<PropertyValue<TextTransformType>> typedValue = convert<PropertyValue<TextTransformType>>(value, error, true, false); + if (!typedValue) { + return error; + } + + setTextTransform(*typedValue); + return nullopt; + + } + + + return Error { "layer doesn't support this property" }; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp index b59768725d..753b2fa184 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.cpp +++ b/src/mbgl/style/layers/symbol_layer_impl.cpp @@ -4,7 +4,7 @@ namespace mbgl { namespace style { bool SymbolLayer::Impl::hasLayoutDifference(const Layer::Impl& other) const { - assert(dynamic_cast<const SymbolLayer::Impl*>(&other)); + assert(other.type == LayerType::Symbol); const auto& impl = static_cast<const style::SymbolLayer::Impl&>(other); return filter != impl.filter || visibility != impl.visibility || diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index 8d14d7972c..114a666f08 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -1,13 +1,12 @@ #include <mbgl/style/parser.hpp> #include <mbgl/style/layer_impl.hpp> -#include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/rapidjson_conversion.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/coordinate.hpp> #include <mbgl/style/conversion/source.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/light.hpp> #include <mbgl/style/conversion/transition_options.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/string.hpp> @@ -274,31 +273,12 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique } std::vector<FontStack> Parser::fontStacks() const { - std::set<FontStack> result; - + std::vector<Immutable<Layer::Impl>> impls; + impls.reserve(layers.size()); for (const auto& layer : layers) { - if (layer->is<SymbolLayer>() && !layer->as<SymbolLayer>()->getTextField().isUndefined()) { - layer->as<SymbolLayer>()->getTextFont().match( - [&] (Undefined) { - result.insert({"Open Sans Regular", "Arial Unicode MS Regular"}); - }, - [&] (const FontStack& constant) { - result.insert(constant); - }, - [&] (const auto& function) { - for (const auto& value : function.possibleOutputs()) { - if (value) { - result.insert(*value); - } else { - Log::Warning(Event::ParseStyle, "Layer '%s' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression.", layer->getID().c_str()); - break; - } - } - } - ); - } + impls.emplace_back(layer->baseImpl); } - + std::set<FontStack> result = mbgl::fontStacks(impls); return std::vector<FontStack>(result.begin(), result.end()); } diff --git a/src/mbgl/style/rapidjson_conversion.hpp b/src/mbgl/style/rapidjson_conversion.hpp index 79bd9c928b..be335101e9 100644 --- a/src/mbgl/style/rapidjson_conversion.hpp +++ b/src/mbgl/style/rapidjson_conversion.hpp @@ -1,7 +1,7 @@ #pragma once #include <mbgl/util/rapidjson.hpp> -#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mapbox/geojson.hpp> #include <mapbox/geojson/rapidjson.hpp> diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index fd6d7d3013..5ec3909d3e 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -64,6 +64,7 @@ GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON) vtOptions.extent = util::EXTENT; vtOptions.buffer = ::round(scale * options.buffer); vtOptions.tolerance = scale * options.tolerance; + vtOptions.lineMetrics = options.lineMetrics; data = std::make_unique<GeoJSONVTData>(geoJSON, vtOptions); } } diff --git a/src/mbgl/text/collision_index.cpp b/src/mbgl/text/collision_index.cpp index 091840a371..dae789a196 100644 --- a/src/mbgl/text/collision_index.cpp +++ b/src/mbgl/text/collision_index.cpp @@ -62,6 +62,21 @@ bool CollisionIndex::isOffscreen(const CollisionBox& box) const { bool CollisionIndex::isInsideGrid(const CollisionBox& box) const { return box.px2 >= 0 && box.px1 < gridRightBoundary && box.py2 >= 0 && box.py1 < gridBottomBoundary; } + +CollisionTileBoundaries CollisionIndex::projectTileBoundaries(const mat4& posMatrix) const { + Point<float> topLeft = projectPoint(posMatrix, { 0, 0 }); + Point<float> bottomRight = projectPoint(posMatrix, { util::EXTENT, util::EXTENT }); + + return {{ topLeft.x, topLeft.y, bottomRight.x, bottomRight.y }}; + +} + +bool CollisionIndex::isInsideTile(const CollisionBox& box, const CollisionTileBoundaries& tileBoundaries) const { + // This check is only well defined when the tile boundaries are axis-aligned + // We are relying on it only being used in MapMode::Tile, where that is always the case + + return box.px1 >= tileBoundaries[0] && box.py1 >= tileBoundaries[1] && box.px2 < tileBoundaries[2] && box.py2 < tileBoundaries[3]; +} std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature, @@ -73,7 +88,8 @@ std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature, const float fontSize, const bool allowOverlap, const bool pitchWithMap, - const bool collisionDebug) { + const bool collisionDebug, + const optional<CollisionTileBoundaries>& avoidEdges) { if (!feature.alongLine) { CollisionBox& box = feature.boxes.front(); const auto projectedPoint = projectAndGetPerspectiveRatio(posMatrix, box.anchor); @@ -82,15 +98,17 @@ std::pair<bool,bool> CollisionIndex::placeFeature(CollisionFeature& feature, box.py1 = box.y1 * tileToViewport + projectedPoint.first.y; box.px2 = box.x2 * tileToViewport + projectedPoint.first.x; box.py2 = box.y2 * tileToViewport + projectedPoint.first.y; + - if (!isInsideGrid(box) || + if ((avoidEdges && !isInsideTile(box, *avoidEdges)) || + !isInsideGrid(box) || (!allowOverlap && collisionGrid.hitTest({{ box.px1, box.py1 }, { box.px2, box.py2 }}))) { return { false, false }; } return {true, isOffscreen(box)}; } else { - return placeLineFeature(feature, posMatrix, labelPlaneMatrix, textPixelRatio, symbol, scale, fontSize, allowOverlap, pitchWithMap, collisionDebug); + return placeLineFeature(feature, posMatrix, labelPlaneMatrix, textPixelRatio, symbol, scale, fontSize, allowOverlap, pitchWithMap, collisionDebug, avoidEdges); } } @@ -103,7 +121,8 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature, const float fontSize, const bool allowOverlap, const bool pitchWithMap, - const bool collisionDebug) { + const bool collisionDebug, + const optional<CollisionTileBoundaries>& avoidEdges) { const auto tileUnitAnchorPoint = symbol.anchorPoint; const auto projectedAnchor = projectAnchor(posMatrix, tileUnitAnchorPoint); @@ -202,15 +221,14 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature, entirelyOffscreen &= isOffscreen(circle); inGrid |= isInsideGrid(circle); - if (!allowOverlap) { - if (collisionGrid.hitTest({{circle.px, circle.py}, circle.radius})) { - if (!collisionDebug) { - return {false, false}; - } else { - // Don't early exit if we're showing the debug circles because we still want to calculate - // which circles are in use - collisionDetected = true; - } + if ((avoidEdges && !isInsideTile(circle, *avoidEdges)) || + (!allowOverlap && collisionGrid.hitTest({{circle.px, circle.py}, circle.radius}))) { + if (!collisionDebug) { + return {false, false}; + } else { + // Don't early exit if we're showing the debug circles because we still want to calculate + // which circles are in use + collisionDetected = true; } } } diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp index b2be4c6ade..78782fe61c 100644 --- a/src/mbgl/text/collision_index.hpp +++ b/src/mbgl/text/collision_index.hpp @@ -3,13 +3,18 @@ #include <mbgl/geometry/feature_index.hpp> #include <mbgl/text/collision_feature.hpp> #include <mbgl/util/grid_index.hpp> +#include <mbgl/util/optional.hpp> #include <mbgl/map/transform_state.hpp> +#include <array> + namespace mbgl { class PlacedSymbol; struct TileDistance; + +using CollisionTileBoundaries = std::array<float,4>; class CollisionIndex { public: @@ -26,15 +31,19 @@ public: const float fontSize, const bool allowOverlap, const bool pitchWithMap, - const bool collisionDebug); + const bool collisionDebug, + const optional<CollisionTileBoundaries>& avoidEdges); void insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId); std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> queryRenderedSymbols(const ScreenLineString&) const; + + CollisionTileBoundaries projectTileBoundaries(const mat4& posMatrix) const; private: bool isOffscreen(const CollisionBox&) const; bool isInsideGrid(const CollisionBox&) const; + bool isInsideTile(const CollisionBox&, const CollisionTileBoundaries& tileBoundaries) const; std::pair<bool,bool> placeLineFeature(CollisionFeature& feature, const mat4& posMatrix, @@ -45,7 +54,8 @@ private: const float fontSize, const bool allowOverlap, const bool pitchWithMap, - const bool collisionDebug); + const bool collisionDebug, + const optional<CollisionTileBoundaries>& avoidEdges); float approximateTileDistance(const TileDistance& tileDistance, const float lastSegmentAngle, const float pixelsToTileUnits, const float cameraToAnchorDistance, const bool pitchWithMap); diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp index 3130418908..8e7cfe5ba7 100644 --- a/src/mbgl/text/glyph_manager.cpp +++ b/src/mbgl/text/glyph_manager.cpp @@ -5,6 +5,7 @@ #include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/util/tiny_sdf.hpp> +#include <mbgl/util/std.hpp> namespace mbgl { @@ -153,4 +154,10 @@ void GlyphManager::removeRequestor(GlyphRequestor& requestor) { } } +void GlyphManager::evict(const std::set<FontStack>& keep) { + util::erase_if(entries, [&] (const auto& entry) { + return keep.count(entry.first) == 0; + }); +} + } // namespace mbgl diff --git a/src/mbgl/text/glyph_manager.hpp b/src/mbgl/text/glyph_manager.hpp index 84db2c4be5..642471bbf2 100644 --- a/src/mbgl/text/glyph_manager.hpp +++ b/src/mbgl/text/glyph_manager.hpp @@ -42,6 +42,9 @@ public: void setObserver(GlyphManagerObserver*); + // Remove glyphs for all but the supplied font stacks. + void evict(const std::set<FontStack>&); + private: Glyph generateLocalSDF(const FontStack& fontStack, GlyphID glyphID); diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 16dd94b374..0747133bd2 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -48,7 +48,7 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri if (!renderTile.tile.isRenderable()) { continue; } - assert(dynamic_cast<GeometryTile*>(&renderTile.tile)); + assert(renderTile.tile.kind == Tile::Kind::Geometry); GeometryTile& geometryTile = static_cast<GeometryTile&>(renderTile.tile); auto bucket = renderTile.tile.getBucket<SymbolBucket>(*symbolLayer.baseImpl); @@ -110,6 +110,13 @@ void Placement::placeLayerBucket( auto partiallyEvaluatedTextSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom()); auto partiallyEvaluatedIconSize = bucket.iconSizeBinder->evaluateForZoom(state.getZoom()); + optional<CollisionTileBoundaries> avoidEdges; + if (mapMode == MapMode::Tile && + (bucket.layout.get<style::SymbolAvoidEdges>() || + bucket.layout.get<style::SymbolPlacement>() == style::SymbolPlacementType::Line)) { + avoidEdges = collisionIndex.projectTileBoundaries(posMatrix); + } + for (auto& symbolInstance : bucket.symbolInstances) { if (seenCrossTileIDs.count(symbolInstance.crossTileID) == 0) { @@ -133,7 +140,7 @@ void Placement::placeLayerBucket( placedSymbol, scale, fontSize, bucket.layout.get<style::TextAllowOverlap>(), bucket.layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map, - showCollisionBoxes); + showCollisionBoxes, avoidEdges); placeText = placed.first; offscreen &= placed.second; } @@ -147,7 +154,7 @@ void Placement::placeLayerBucket( placedSymbol, scale, fontSize, bucket.layout.get<style::IconAllowOverlap>(), bucket.layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map, - showCollisionBoxes); + showCollisionBoxes, avoidEdges); placeIcon = placed.first; offscreen &= placed.second; } @@ -254,9 +261,16 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>& JointOpacityState duplicateOpacityState(false, false, true); + const bool textAllowOverlap = bucket.layout.get<style::TextAllowOverlap>(); + const bool iconAllowOverlap = bucket.layout.get<style::IconAllowOverlap>(); + + // If allow-overlap is true, we can show symbols before placement runs on them + // But we have to wait for placement if we potentially depend on a paired icon/text + // with allow-overlap: false. + // See https://github.com/mapbox/mapbox-gl-native/issues/12483 JointOpacityState defaultOpacityState( - bucket.layout.get<style::TextAllowOverlap>(), - bucket.layout.get<style::IconAllowOverlap>(), + textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || bucket.layout.get<style::IconOptional>()), + iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || bucket.layout.get<style::TextOptional>()), true); for (SymbolInstance& symbolInstance : bucket.symbolInstances) { diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index d686d8440b..90d4d07895 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -44,7 +44,7 @@ using namespace style; GeometryTile::GeometryTile(const OverscaledTileID& id_, std::string sourceID_, const TileParameters& parameters) - : Tile(id_), + : Tile(Kind::Geometry, id_), sourceID(std::move(sourceID_)), mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())), worker(parameters.workerScheduler, diff --git a/src/mbgl/tile/raster_dem_tile.cpp b/src/mbgl/tile/raster_dem_tile.cpp index f29861ee71..751f69bad9 100644 --- a/src/mbgl/tile/raster_dem_tile.cpp +++ b/src/mbgl/tile/raster_dem_tile.cpp @@ -15,7 +15,7 @@ namespace mbgl { RasterDEMTile::RasterDEMTile(const OverscaledTileID& id_, const TileParameters& parameters, const Tileset& tileset) - : Tile(id_), + : Tile(Kind::RasterDEM, id_), loader(*this, id_, parameters, tileset), mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())), worker(parameters.workerScheduler, diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp index cc71c04ba1..1346f87ae5 100644 --- a/src/mbgl/tile/raster_tile.cpp +++ b/src/mbgl/tile/raster_tile.cpp @@ -15,7 +15,7 @@ namespace mbgl { RasterTile::RasterTile(const OverscaledTileID& id_, const TileParameters& parameters, const Tileset& tileset) - : Tile(id_), + : Tile(Kind::Raster, id_), loader(*this, id_, parameters, tileset), mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())), worker(parameters.workerScheduler, diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index b95944f10e..5a69df5b43 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -9,7 +9,7 @@ namespace mbgl { static TileObserver nullObserver; -Tile::Tile(OverscaledTileID id_) : id(std::move(id_)), observer(&nullObserver) { +Tile::Tile(Kind kind_, OverscaledTileID id_) : kind(kind_), id(std::move(id_)), observer(&nullObserver) { } Tile::~Tile() = default; @@ -27,6 +27,14 @@ void Tile::setTriedCache() { } void Tile::dumpDebugLogs() const { + std::string kindString; + switch (kind) { + case Kind::Geometry: kindString = "Geometry"; break; + case Kind::Raster: kindString = "Raster"; break; + case Kind::RasterDEM: kindString = "RasterDEM"; break; + default: kindString = "Unknown"; break; + } + Log::Info(Event::General, "Tile::Kind: %s", kindString.c_str()); 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"); diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 5cf74abff5..70b2aa0371 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -35,7 +35,13 @@ class Context; class Tile : private util::noncopyable { public: - Tile(OverscaledTileID); + enum class Kind : uint8_t { + Geometry, + Raster, + RasterDEM + }; + + Tile(Kind, OverscaledTileID); virtual ~Tile(); void setObserver(TileObserver* observer); @@ -119,6 +125,7 @@ public: void dumpDebugLogs() const; + const Kind kind; OverscaledTileID id; optional<Timestamp> modified; optional<Timestamp> expires; diff --git a/src/mbgl/util/fnv_hash.hpp b/src/mbgl/util/fnv_hash.hpp new file mode 100644 index 0000000000..87d4661c56 --- /dev/null +++ b/src/mbgl/util/fnv_hash.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace mbgl { +namespace util { + +inline constexpr uint64_t hashFNV1a(const char * str, uint64_t value = 0xcbf29ce484222325) { + return str[0] ? hashFNV1a(str + 1, (value ^ uint64_t(str[0])) * 0x100000001b3) : value; +} + +} // end namespace util +} // end namespace mbgl diff --git a/src/mbgl/util/font_stack.cpp b/src/mbgl/util/font_stack.cpp index fb3b1b60a2..177d5e6f31 100644 --- a/src/mbgl/util/font_stack.cpp +++ b/src/mbgl/util/font_stack.cpp @@ -1,10 +1,14 @@ #include <mbgl/util/font_stack.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> #include <boost/functional/hash.hpp> #include <boost/algorithm/string/join.hpp> namespace mbgl { +using namespace style; + std::string fontStackToString(const FontStack& fontStack) { return boost::algorithm::join(fontStack, ","); } @@ -13,4 +17,40 @@ std::size_t FontStackHash::operator()(const FontStack& fontStack) const { return boost::hash_range(fontStack.begin(), fontStack.end()); } +std::set<FontStack> fontStacks(const std::vector<Immutable<style::Layer::Impl>>& layers) { + std::set<FontStack> result; + + for (const auto& layer : layers) { + if (layer->type != LayerType::Symbol) { + continue; + } + + const SymbolLayer::Impl& impl = dynamic_cast<const SymbolLayer::Impl&>(*layer); + if (impl.layout.get<TextField>().isUndefined()) { + continue; + } + + impl.layout.get<TextFont>().match( + [&] (Undefined) { + result.insert({"Open Sans Regular", "Arial Unicode MS Regular"}); + }, + [&] (const FontStack& constant) { + result.insert(constant); + }, + [&] (const auto& function) { + for (const auto& value : function.possibleOutputs()) { + if (value) { + result.insert(*value); + } else { + Log::Warning(Event::ParseStyle, "Layer '%s' has an invalid value for text-font and will not render text. Output values must be contained as literals within the expression.", impl.id.c_str()); + break; + } + } + } + ); + } + + return result; +} + } // namespace mbgl diff --git a/src/mbgl/util/logging.cpp b/src/mbgl/util/logging.cpp index d322bd3670..fd3f14825d 100644 --- a/src/mbgl/util/logging.cpp +++ b/src/mbgl/util/logging.cpp @@ -35,7 +35,7 @@ void Log::record(EventSeverity severity, Event event, const char* format, ...) { vsnprintf(msg, sizeof(msg), format, args); va_end(args); - record(severity, event, -1, msg); + record(severity, event, -1, std::string{ msg }); } void Log::record(EventSeverity severity, Event event, int64_t code, const char* format, ...) { diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index 3f39e53d40..f58d1270bd 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -4,6 +4,7 @@ #include <mbgl/map/transform_state.hpp> #include <mbgl/util/tile_cover_impl.hpp> #include <mbgl/util/tile_coordinate.hpp> +#include <mbgl/math/log2.hpp> #include <functional> #include <list> diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp index 07257851ac..fea1f87106 100644 --- a/test/api/annotations.test.cpp +++ b/test/api/annotations.test.cpp @@ -59,6 +59,18 @@ TEST(Annotations, SymbolAnnotation) { // } } +TEST(Annotations, SymbolAnnotationTileBoundary) { + // Almost exactly the same as SymbolAnnotation test above, but offset my fractions of a degree + // tests precision issue from https://github.com/mapbox/mapbox-gl-native/issues/12472 + AnnotationTest test; + + test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); + test.map.addAnnotationImage(namedMarker("default_marker")); + test.map.addAnnotation(SymbolAnnotation { Point<double>(0.000000000000001, 0.00000000000001), "default_marker" }); + test.map.setZoom(10); + test.checkRendering("point_annotation"); +} + TEST(Annotations, LineAnnotation) { AnnotationTest test; @@ -475,3 +487,4 @@ TEST(Annotations, ChangeMaxZoom) { test.map.setZoom(test.map.getMaxZoom()); test.checkRendering("line_annotation_max_zoom"); } + diff --git a/test/fixtures/offline_database/corrupt-delayed.db b/test/fixtures/offline_database/corrupt-delayed.db Binary files differnew file mode 100644 index 0000000000..04989dbf36 --- /dev/null +++ b/test/fixtures/offline_database/corrupt-delayed.db diff --git a/test/fixtures/offline_database/corrupt-immediate.db b/test/fixtures/offline_database/corrupt-immediate.db Binary files differnew file mode 100644 index 0000000000..8909c402b2 --- /dev/null +++ b/test/fixtures/offline_database/corrupt-immediate.db diff --git a/test/fixtures/style_parser/text-font.info.json b/test/fixtures/style_parser/text-font.info.json index 0fb9658269..dba9c707df 100644 --- a/test/fixtures/style_parser/text-font.info.json +++ b/test/fixtures/style_parser/text-font.info.json @@ -1,14 +1,14 @@ { "default": { "log": [ - [1, "WARNING", "ParseStyle", "Layer 'invalid expression - get' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], - [1, "WARNING", "ParseStyle", "Layer 'invalid expression - case' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], - [1, "WARNING", "ParseStyle", "Layer 'invalid expression - match' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], - [1, "WARNING", "ParseStyle", "Layer 'invalid expression - at' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], - [1, "WARNING", "ParseStyle", "Layer 'invalid expression - coalesce' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], - [1, "WARNING", "ParseStyle", "Layer 'invalid expression - step' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], - [1, "WARNING", "ParseStyle", "Layer 'invalid expression - let/var' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."], - [1, "WARNING", "ParseStyle", "Layer 'invalid expression - identity function' has an invalid value for text-font and will not work offline. Output values must be contained as literals within the expression."] + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - get' has an invalid value for text-font and will not render text. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - case' has an invalid value for text-font and will not render text. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - match' has an invalid value for text-font and will not render text. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - at' has an invalid value for text-font and will not render text. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - coalesce' has an invalid value for text-font and will not render text. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - step' has an invalid value for text-font and will not render text. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - let/var' has an invalid value for text-font and will not render text. Output values must be contained as literals within the expression."], + [1, "WARNING", "ParseStyle", "Layer 'invalid expression - identity function' has an invalid value for text-font and will not render text. Output values must be contained as literals within the expression."] ] } } diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 8e2d9cb9cd..cb45c2900b 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -207,7 +207,7 @@ TEST(Map, SetStyleInvalidJSON) { EXPECT_TRUE(fail); auto observer = Log::removeObserver(); - auto flo = dynamic_cast<FixtureLogObserver*>(observer.get()); + auto flo = static_cast<FixtureLogObserver*>(observer.get()); EXPECT_EQ(1u, flo->count({ EventSeverity::Error, Event::ParseStyle, -1, "Failed to parse style: 0 - Invalid value." })); auto unchecked = flo->unchecked(); diff --git a/test/src/mbgl/test/fixture_log_observer.cpp b/test/src/mbgl/test/fixture_log_observer.cpp index d8a4b9edce..d768c0284a 100644 --- a/test/src/mbgl/test/fixture_log_observer.cpp +++ b/test/src/mbgl/test/fixture_log_observer.cpp @@ -32,7 +32,9 @@ bool FixtureLog::Observer::onRecord(EventSeverity severity, const std::string& msg) { std::lock_guard<std::mutex> lock(messagesMutex); - messages.emplace_back(severity, event, code, msg); + if (severity != EventSeverity::Debug) { + messages.emplace_back(severity, event, code, msg); + } return true; } @@ -48,7 +50,7 @@ size_t FixtureLog::Observer::count(const Message& message, bool substring) const size_t message_count = 0; for (const auto& msg : messages) { - if (msg.matches(message, substring)) { + if (!msg.checked && msg.matches(message, substring)) { message_count++; msg.checked = true; } diff --git a/test/src/mbgl/test/sqlite3_test_fs.cpp b/test/src/mbgl/test/sqlite3_test_fs.cpp new file mode 100644 index 0000000000..16d411faff --- /dev/null +++ b/test/src/mbgl/test/sqlite3_test_fs.cpp @@ -0,0 +1,320 @@ +#ifndef __QT__ // Qt doesn't expose SQLite VFS + +#include <mbgl/test/sqlite3_test_fs.hpp> + +#include <sqlite3.h> + +#include <stdexcept> +#include <cstdio> +#include <cstring> +#include <cstdlib> +#include <cassert> + +static bool sqlite3_test_fs_debug = false; +static bool sqlite3_test_fs_io = true; +static bool sqlite3_test_fs_file_open = true; +static bool sqlite3_test_fs_file_create = true; +static int64_t sqlite3_test_fs_read_limit = -1; +static int64_t sqlite3_test_fs_write_limit = -1; + +struct File { + sqlite3_file base; + sqlite3_file* real; +}; + +static int sqlite3_test_fs_close(sqlite3_file* pFile) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: close(%p)\n", pFile); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + File* file = (File*)pFile; + const int rc = file->real->pMethods->xClose(file->real); + if (rc == SQLITE_OK) { + sqlite3_free((void*)file->base.pMethods); + file->base.pMethods = 0; + } + return rc; +} + +static int sqlite3_test_fs_read(sqlite3_file* pFile, void* zBuf, int iAmt, sqlite3_int64 iOfst) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: read(%p, amount=%d, offset=%lld)\n", pFile, iAmt, iOfst); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + if (sqlite3_test_fs_read_limit >= 0) { + if (iAmt > sqlite3_test_fs_read_limit) { + iAmt = 0; + return SQLITE_IOERR; + } + sqlite3_test_fs_read_limit -= iAmt; + } + File* file = (File*)pFile; + return file->real->pMethods->xRead(file->real, zBuf, iAmt, iOfst); +} + +static int sqlite3_test_fs_write(sqlite3_file* pFile, const void* zBuf, int iAmt, sqlite3_int64 iOfst) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: write(%p, amount=%d, offset=%lld)\n", pFile, iAmt, iOfst); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + if (sqlite3_test_fs_write_limit >= 0) { + if (iAmt > sqlite3_test_fs_write_limit) { + iAmt = 0; + return SQLITE_FULL; + } + sqlite3_test_fs_write_limit -= iAmt; + } + File* file = (File*)pFile; + return file->real->pMethods->xWrite(file->real, zBuf, iAmt, iOfst); +} + +static int sqlite3_test_fs_truncate(sqlite3_file* pFile, sqlite3_int64 size) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: truncate(%p, size=%lld)\n", pFile, size); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + File* file = (File*)pFile; + return file->real->pMethods->xTruncate(file->real, size); +} + +static int sqlite3_test_fs_sync(sqlite3_file* pFile, int flags) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: sync(%p, flags=%d)\n", pFile, flags); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + File* file = (File*)pFile; + return file->real->pMethods->xSync(file->real, flags); +} + +static int sqlite3_test_fs_file_size(sqlite3_file* pFile, sqlite3_int64* pSize) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: file_size(%p)\n", pFile); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + File* file = (File*)pFile; + return file->real->pMethods->xFileSize(file->real, pSize); +} + +static int sqlite3_test_fs_lock(sqlite3_file* pFile, int eLock) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: lock(%p, %d)\n", pFile, eLock); + } + File* file = (File*)pFile; + return file->real->pMethods->xLock(file->real, eLock); +} + +static int sqlite3_test_fs_unlock(sqlite3_file* pFile, int eLock) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: unlock(%p, %d)\n", pFile, eLock); + } + File* file = (File*)pFile; + return file->real->pMethods->xUnlock(file->real, eLock); +} + +static int sqlite3_test_fs_check_reserved_lock(sqlite3_file* pFile, int* pResOut) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: check_reserved_lock(%p)\n", pFile); + } + File* file = (File*)pFile; + return file->real->pMethods->xCheckReservedLock(file->real, pResOut); +} + +static int sqlite3_test_fs_file_control(sqlite3_file* pFile, int op, void* pArg) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: file_control(%p, op=%d)\n", pFile, op); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + File* file = (File*)pFile; + return file->real->pMethods->xFileControl(file->real, op, pArg); +} + +static int sqlite3_test_fs_sector_size(sqlite3_file* pFile) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: sector_size(%p)\n", pFile); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + File* file = (File*)pFile; + return file->real->pMethods->xSectorSize(file->real); +} + +static int sqlite3_test_fs_device_characteristics(sqlite3_file* pFile) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: device_characteristics(%p)\n", pFile); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + File* file = (File*)pFile; + return file->real->pMethods->xDeviceCharacteristics(file->real); +} + +static int sqlite3_test_fs_open(sqlite3_vfs* vfs, const char* zName, sqlite3_file* pFile, int flags, int* pOutFlags) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: open(name=%s, flags=%d) -> %p\n", zName, flags, pFile); + } + if (!sqlite3_test_fs_io) { + pFile->pMethods = NULL; + return SQLITE_AUTH; + } + if (!sqlite3_test_fs_file_open) { + pFile->pMethods = NULL; + return SQLITE_CANTOPEN; + } + + File* file = (File*)pFile; + sqlite3_vfs* unix_fs = (sqlite3_vfs*)vfs->pAppData; + file->real = (sqlite3_file*)&file[1]; + + if (!sqlite3_test_fs_file_create) { + int res; + const int result = unix_fs->xAccess(vfs, zName, SQLITE_ACCESS_EXISTS, &res); + if (result != SQLITE_OK) { + pFile->pMethods = NULL; + return result; + } + if (res != 1) { + pFile->pMethods = NULL; + return SQLITE_CANTOPEN; + } + } + + const int status = unix_fs->xOpen(unix_fs, zName, file->real, flags, pOutFlags); + if (file->real->pMethods) { + sqlite3_io_methods* methods = (sqlite3_io_methods*)sqlite3_malloc(sizeof(sqlite3_io_methods)); + memset(methods, 0, sizeof(sqlite3_io_methods)); + methods->iVersion = 1; + methods->xClose = sqlite3_test_fs_close; + methods->xRead = sqlite3_test_fs_read; + methods->xWrite = sqlite3_test_fs_write; + methods->xTruncate = sqlite3_test_fs_truncate; + methods->xSync = sqlite3_test_fs_sync; + methods->xFileSize = sqlite3_test_fs_file_size; + methods->xLock = sqlite3_test_fs_lock; + methods->xUnlock = sqlite3_test_fs_unlock; + methods->xCheckReservedLock = sqlite3_test_fs_check_reserved_lock; + methods->xFileControl = sqlite3_test_fs_file_control; + methods->xSectorSize = sqlite3_test_fs_sector_size; + methods->xDeviceCharacteristics = sqlite3_test_fs_device_characteristics; + pFile->pMethods = methods; + } + return status; +} + +static int sqlite3_test_fs_delete(sqlite3_vfs* vfs, const char *zPath, int dirSync) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: delete(name=%s, sync=%d)\n", zPath, dirSync); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + sqlite3_vfs* unix_fs = (sqlite3_vfs*)vfs->pAppData; + return unix_fs->xDelete(unix_fs, zPath, dirSync); +} + +static int sqlite3_test_fs_access(sqlite3_vfs* vfs, const char *zPath, int flags, int *pResOut) { + if (sqlite3_test_fs_debug) { + fprintf(stderr, "SQLite3: access(name=%s, flags=%d)\n", zPath, flags); + } + if (!sqlite3_test_fs_io) { + return SQLITE_AUTH; + } + sqlite3_vfs* unix_fs = (sqlite3_vfs*)vfs->pAppData; + return unix_fs->xAccess(unix_fs, zPath, flags, pResOut); +} + +namespace mbgl { +namespace test { + +SQLite3TestFS::SQLite3TestFS() { + sqlite3_vfs* unix_fs = sqlite3_vfs_find("unix"); + if (unix_fs == 0) { + abort(); + } + + sqlite3_vfs* test_fs = (sqlite3_vfs*)sqlite3_malloc(sizeof(sqlite3_vfs)); + if (test_fs == 0) { + abort(); + } + memset(test_fs, 0, sizeof(sqlite3_vfs)); + test_fs->iVersion = 1; + test_fs->szOsFile = unix_fs->szOsFile + sizeof(File); + test_fs->mxPathname = unix_fs->mxPathname; + test_fs->zName = "test_fs"; + test_fs->pAppData = unix_fs; + test_fs->xOpen = sqlite3_test_fs_open; + test_fs->xDelete = sqlite3_test_fs_delete; + test_fs->xAccess = sqlite3_test_fs_access; + test_fs->xFullPathname = unix_fs->xFullPathname; + test_fs->xDlOpen = unix_fs->xDlOpen; + test_fs->xDlError = unix_fs->xDlError; + test_fs->xDlSym = unix_fs->xDlSym; + test_fs->xDlClose = unix_fs->xDlClose; + test_fs->xRandomness = unix_fs->xRandomness; + test_fs->xSleep = unix_fs->xSleep; + test_fs->xCurrentTime = unix_fs->xCurrentTime; + test_fs->xGetLastError = unix_fs->xGetLastError; + + sqlite3_vfs_register(test_fs, 0); +} + +SQLite3TestFS::~SQLite3TestFS() { + reset(); + sqlite3_vfs* test_fs = sqlite3_vfs_find("test_fs"); + if (test_fs) { + sqlite3_vfs_unregister(test_fs); + } +} + +void SQLite3TestFS::setDebug(bool value) { + sqlite3_test_fs_debug = value; +} + +void SQLite3TestFS::allowIO(bool value) { + sqlite3_test_fs_io = value; +} + +void SQLite3TestFS::allowFileOpen(bool value) { + sqlite3_test_fs_file_open = value; +} + +void SQLite3TestFS::allowFileCreate(bool value) { + sqlite3_test_fs_file_create = value; +} + +void SQLite3TestFS::setReadLimit(int64_t value) { + sqlite3_test_fs_read_limit = value; +} + +void SQLite3TestFS::setWriteLimit(int64_t value) { + sqlite3_test_fs_write_limit = value; +} + +void SQLite3TestFS::reset() { + setDebug(false); + allowIO(true); + allowFileOpen(true); + allowFileCreate(true); + setReadLimit(-1); + setWriteLimit(-1); +} + +} // namespace test +} // namespace mbgl + +#endif // __QT__ diff --git a/test/src/mbgl/test/sqlite3_test_fs.hpp b/test/src/mbgl/test/sqlite3_test_fs.hpp new file mode 100644 index 0000000000..00351f49ac --- /dev/null +++ b/test/src/mbgl/test/sqlite3_test_fs.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include <cstdint> + +namespace mbgl { +namespace test { + +class SQLite3TestFS { +public: + SQLite3TestFS(); + ~SQLite3TestFS(); + + // When enabled, the VFS will log all I/O operations to stdout. + void setDebug(bool); + + // Allow any type of I/O. Will fail with SQLITE_AUTH if set to false. This is useful to simulate + // scenarios where the OS blocks an entire app's I/O, e.g. when it's in the background. + void allowIO(bool); + + // Allow files to be opened. Will fail with SQLITE_CANTOPEN if set to false. + void allowFileOpen(bool); + + // Allow files to be created. Will fail with SQLITE_CANTOPEN if set to false. + void allowFileCreate(bool); + + // Allow N bytes to be read, then fail reads with SQLITE_IOERR. -1 == unlimited + // This limit is global, not per file. + void setReadLimit(int64_t); + + // Allow N bytes to be written, then fail writes with SQLITE_FULL. -1 == unlimited + // This limit is global, not per file. + void setWriteLimit(int64_t); + + // Reset all restrictions. + void reset(); +}; + +} // namespace test +} // namespace mbgl diff --git a/test/storage/offline.test.cpp b/test/storage/offline.test.cpp index 59aebebaba..90f9570320 100644 --- a/test/storage/offline.test.cpp +++ b/test/storage/offline.test.cpp @@ -6,58 +6,30 @@ using namespace mbgl; using SourceType = mbgl::style::SourceType; -static const LatLngBounds sanFrancisco = - LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); -static const LatLngBounds sanFranciscoWrapped = - LatLngBounds::hull({ 37.6609, 238.5744 }, { 37.8271, 238.3204 }); - -TEST(OfflineTilePyramidRegionDefinition, TileCoverEmpty) { - OfflineTilePyramidRegionDefinition region("", LatLngBounds::empty(), 0, 20, 1.0); - - EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, { 0, 22 })); -} - -TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomIntersection) { - OfflineTilePyramidRegionDefinition region("", sanFrancisco, 2, 2, 1.0); - - EXPECT_EQ((std::vector<CanonicalTileID>{ { 2, 0, 1 } }), - region.tileCover(SourceType::Vector, 512, { 0, 22 })); - - EXPECT_EQ((std::vector<CanonicalTileID>{}), region.tileCover(SourceType::Vector, 512, { 3, 22 })); -} - -TEST(OfflineTilePyramidRegionDefinition, TileCoverTileSize) { - OfflineTilePyramidRegionDefinition region("", LatLngBounds::world(), 0, 0, 1.0); - - EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }), - 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, { 0, 22 })); -} - -TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomRounding) { - OfflineTilePyramidRegionDefinition region("", sanFrancisco, 0.6, 0.7, 1.0); - - EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }), - region.tileCover(SourceType::Vector, 512, { 0, 22 })); - - EXPECT_EQ((std::vector<CanonicalTileID>{ { 1, 0, 0 } }), - region.tileCover(SourceType::Raster, 512, { 0, 22 })); +TEST(OfflineTilePyramidRegionDefinition, EncodeDecode) { + OfflineTilePyramidRegionDefinition region("mapbox://style", LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }), 0, 20, 1.0); + + auto encoded = encodeOfflineRegionDefinition(region); + auto decoded = decodeOfflineRegionDefinition(encoded).get<OfflineTilePyramidRegionDefinition>(); + + EXPECT_EQ(decoded.styleURL, region.styleURL); + EXPECT_EQ(decoded.minZoom, region.minZoom); + EXPECT_EQ(decoded.maxZoom, region.maxZoom); + EXPECT_EQ(decoded.pixelRatio, region.pixelRatio); + EXPECT_EQ(decoded.bounds.southwest(), region.bounds.southwest()); + EXPECT_EQ(decoded.bounds.northeast(), region.bounds.northeast()); } -TEST(OfflineTilePyramidRegionDefinition, TileCoverWrapped) { - OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 0, 1.0); - - EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }), - region.tileCover(SourceType::Vector, 512, { 0, 22 })); -} - -TEST(OfflineTilePyramidRegionDefinition, TileCount) { - OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 22, 1.0); - - //These numbers match the count from tileCover().size(). - EXPECT_EQ(38424u, region.tileCount(SourceType::Vector, 512, { 10, 18 })); - EXPECT_EQ(9675240u, region.tileCount(SourceType::Vector, 512, { 3, 22 })); +TEST(OfflineGeometryRegionDefinition, EncodeDecode) { + OfflineGeometryRegionDefinition region("mapbox://style", Point<double>(-122.5744, 37.6609), 0, 2, 1.0); + + auto encoded = encodeOfflineRegionDefinition(region); + auto decoded = decodeOfflineRegionDefinition(encoded).get<OfflineGeometryRegionDefinition>(); + + EXPECT_EQ(decoded.styleURL, region.styleURL); + EXPECT_EQ(decoded.minZoom, region.minZoom); + EXPECT_EQ(decoded.maxZoom, region.maxZoom); + EXPECT_EQ(decoded.pixelRatio, region.pixelRatio); + EXPECT_EQ(decoded.geometry, region.geometry); } diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index 10343ec305..2ed72e0d8c 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -1,5 +1,6 @@ #include <mbgl/test/util.hpp> #include <mbgl/test/fixture_log_observer.hpp> +#include <mbgl/test/sqlite3_test_fs.hpp> #include <mbgl/storage/offline_database.hpp> #include <mbgl/storage/resource.hpp> @@ -13,47 +14,235 @@ using namespace std::literals::string_literals; using namespace mbgl; +using mapbox::sqlite::ResultCode; static constexpr const char* filename = "test/fixtures/offline_database/offline.db"; +#ifndef __QT__ // Qt doesn't expose the ability to register virtual file system handlers. +static constexpr const char* filename_test_fs = "file:test/fixtures/offline_database/offline.db?vfs=test_fs"; +#endif -TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Create)) { - FixtureLog log; +static void deleteDatabaseFiles() { + // Delete leftover journaling files as well. util::deleteFile(filename); + util::deleteFile(filename + "-wal"s); + util::deleteFile(filename + "-journal"s); +} + +static FixtureLog::Message error(ResultCode code, const char* message) { + return { EventSeverity::Error, Event::Database, static_cast<int64_t>(code), message }; +} + +static __attribute__((unused)) FixtureLog::Message warning(ResultCode code, const char* message) { + return { EventSeverity::Warning, Event::Database, static_cast<int64_t>(code), message }; +} + +static int databasePageCount(const std::string& path) { + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); + mapbox::sqlite::Statement stmt{ db, "pragma page_count" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get<int>(0); +} + +static int databaseUserVersion(const std::string& path) { + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); + mapbox::sqlite::Statement stmt{ db, "pragma user_version" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get<int>(0); +} + +static std::string databaseJournalMode(const std::string& path) { + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); + mapbox::sqlite::Statement stmt{ db, "pragma journal_mode" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get<std::string>(0); +} + +static int databaseSyncMode(const std::string& path) { + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); + mapbox::sqlite::Statement stmt{ db, "pragma synchronous" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get<int>(0); +} + +static std::vector<std::string> databaseTableColumns(const std::string& path, const std::string& name) { + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); + const auto sql = std::string("pragma table_info(") + name + ")"; + mapbox::sqlite::Statement stmt{ db, sql.c_str() }; + mapbox::sqlite::Query query{ stmt }; + std::vector<std::string> columns; + while (query.run()) { + columns.push_back(query.get<std::string>(1)); + } + return columns; +} + +namespace fixture { + +const Resource resource{ Resource::Style, "mapbox://test" }; +const Resource tile = Resource::tile("mapbox://test", 1, 0, 0, 0, Tileset::Scheme::XYZ); +const Response response = [] { + Response res; + res.data = std::make_shared<std::string>("first"); + return res; +}(); + +} // namespace +TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Create)) { + FixtureLog log; + deleteDatabaseFiles(); OfflineDatabase db(filename); EXPECT_FALSE(bool(db.get({ Resource::Unknown, "mapbox://test" }))); EXPECT_EQ(0u, log.uncheckedCount()); } +#ifndef __QT__ // Qt doesn't expose the ability to register virtual file system handlers. +TEST(OfflineDatabase, TEST_REQUIRES_WRITE(CreateFail)) { + FixtureLog log; + deleteDatabaseFiles(); + test::SQLite3TestFS fs; + + // Opening the database will fail because our mock VFS returns a SQLITE_CANTOPEN error because + // it is not allowed to create the file. The OfflineDatabase object should handle this gracefully + // and treat it like an empty cache that can't be written to. + fs.allowFileCreate(false); + OfflineDatabase db(filename_test_fs); + EXPECT_EQ(1u, log.count(warning(ResultCode::CantOpen, "Can't open database: unable to open database file"))); + + EXPECT_EQ(0u, log.uncheckedCount()); + + // We can try to insert things into the cache, but since the cache database isn't open, it won't be stored. + for (const auto& res : { fixture::resource, fixture::tile }) { + EXPECT_EQ(std::make_pair(false, uint64_t(0)), db.put(res, fixture::response)); + EXPECT_EQ(1u, log.count(warning(ResultCode::CantOpen, "Can't write resource: unable to open database file"))); + EXPECT_EQ(0u, log.uncheckedCount()); + } + + // We can also still "query" the database even though it is not open, and we will always get an empty result. + for (const auto& res : { fixture::resource, fixture::tile }) { + EXPECT_FALSE(bool(db.get(res))); + EXPECT_EQ(1u, log.count(warning(ResultCode::CantOpen, "Can't update timestamp: unable to open database file"))); + EXPECT_EQ(1u, log.count(warning(ResultCode::CantOpen, "Can't read resource: unable to open database file"))); + EXPECT_EQ(0u, log.uncheckedCount()); + } + + // Now, we're "freeing up" some space on the disk, and try to insert and query again. This time, we should + // be opening the datbase, creating the schema, and writing the data so that we can retrieve it again. + fs.allowFileCreate(true); + for (const auto& res : { fixture::resource, fixture::tile }) { + EXPECT_EQ(std::make_pair(true, uint64_t(5)), db.put(res, fixture::response)); + auto result = db.get(res); + EXPECT_EQ(0u, log.uncheckedCount()); + ASSERT_TRUE(result && result->data); + EXPECT_EQ("first", *result->data); + } + + // Next, set the file system to read only mode and try to read the data again. While we can't + // write anymore, we should still be able to read, and the query that tries to update the last + // accessed timestamp may fail without crashing. + fs.allowFileCreate(false); + fs.setWriteLimit(0); + for (const auto& res : { fixture::resource, fixture::tile }) { + auto result = db.get(res); + EXPECT_EQ(1u, log.count(warning(ResultCode::CantOpen, "Can't update timestamp: unable to open database file"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + ASSERT_TRUE(result && result->data); + EXPECT_EQ("first", *result->data); + } + fs.setDebug(false); + + // We're allowing SQLite to create a journal file, but restrict the number of bytes it + // can write so that it can start writing the journal file, but eventually fails during the + // timestamp update. + fs.allowFileCreate(true); + fs.setWriteLimit(8192); + for (const auto& res : { fixture::resource, fixture::tile }) { + auto result = db.get(res); + EXPECT_EQ(1u, log.count(warning(ResultCode::Full, "Can't update timestamp: database or disk is full"))); + EXPECT_EQ(0u, log.uncheckedCount()); + ASSERT_TRUE(result && result->data); + EXPECT_EQ("first", *result->data); + } + + // Lastly, we're disabling all I/O to simulate a backgrounded app that is restricted from doing + // any disk I/O at all. + fs.setWriteLimit(-1); + fs.allowIO(false); + for (const auto& res : { fixture::resource, fixture::tile }) { + // First, try reading. + auto result = db.get(res); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't update timestamp: authorization denied"))); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't read resource: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + EXPECT_FALSE(result); + + // Now try inserting. + EXPECT_EQ(std::make_pair(false, uint64_t(0)), db.put(res, fixture::response)); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't write resource: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + } + + // Allow deleting the database. + fs.reset(); +} +#endif // __QT__ + TEST(OfflineDatabase, TEST_REQUIRES_WRITE(SchemaVersion)) { FixtureLog log; - util::deleteFile(filename); + deleteDatabaseFiles(); { mapbox::sqlite::Database db = mapbox::sqlite::Database::open(filename, mapbox::sqlite::ReadWriteCreate); + db.setBusyTimeout(Milliseconds(1000)); db.exec("PRAGMA user_version = 1"); } + { + OfflineDatabase db(filename); + } + + EXPECT_EQ(6, databaseUserVersion(filename)); + OfflineDatabase db(filename); + // Now try inserting and reading back to make sure we have a valid database. + for (const auto& res : { fixture::resource, fixture::tile }) { + EXPECT_EQ(std::make_pair(true, uint64_t(5)), db.put(res, fixture::response)); + EXPECT_EQ(0u, log.uncheckedCount()); + auto result = db.get(res); + EXPECT_EQ(0u, log.uncheckedCount()); + ASSERT_TRUE(result && result->data); + EXPECT_EQ("first", *result->data); + } EXPECT_EQ(0u, log.uncheckedCount()); } TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Invalid)) { FixtureLog log; - util::deleteFile(filename); + deleteDatabaseFiles(); util::write_file(filename, "this is an invalid file"); OfflineDatabase db(filename); - -#ifndef __QT__ - // Only non-Qt platforms are setting a logger on the SQLite object. - EXPECT_EQ(1u, log.count({ EventSeverity::Info, Event::Database, static_cast<int64_t>(mapbox::sqlite::ResultCode::NotADB), - "statement aborts at 1: [PRAGMA user_version] file is encrypted or is not a database" }, true)); -#endif + // Checking two possibilities for the error string because it apparently changes between SQLite versions. + EXPECT_EQ(1u, log.count(error(ResultCode::NotADB, "Can't open database: file is encrypted or is not a database"), true) + + log.count(error(ResultCode::NotADB, "Can't open database: file is not a database"), true)); EXPECT_EQ(1u, log.count({ EventSeverity::Warning, Event::Database, -1, "Removing existing incompatible offline database" })); - EXPECT_EQ(0u, log.uncheckedCount()); + + // Now try inserting and reading back to make sure we have a valid database. + for (const auto& res : { fixture::resource, fixture::tile }) { + EXPECT_EQ(std::make_pair(true, uint64_t(5)), db.put(res, fixture::response)); + EXPECT_EQ(0u, log.uncheckedCount()); + auto result = db.get(res); + EXPECT_EQ(0u, log.uncheckedCount()); + ASSERT_TRUE(result && result->data); + EXPECT_EQ("first", *result->data); + } } TEST(OfflineDatabase, PutDoesNotStoreConnectionErrors) { @@ -114,7 +303,7 @@ TEST(OfflineDatabase, PutResource) { TEST(OfflineDatabase, TEST_REQUIRES_WRITE(GetResourceFromOfflineRegion)) { FixtureLog log; - util::deleteFile(filename); + deleteDatabaseFiles(); util::copyFile(filename, "test/fixtures/offline_database/satellite_test.db"); OfflineDatabase db(filename, mapbox::sqlite::ReadOnly); @@ -222,30 +411,39 @@ TEST(OfflineDatabase, PutTileNotFound) { TEST(OfflineDatabase, CreateRegion) { FixtureLog log; OfflineDatabase db(":memory:"); - OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; + OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; OfflineRegionMetadata metadata {{ 1, 2, 3 }}; - OfflineRegion region = db.createRegion(definition, metadata); - - EXPECT_EQ(definition.styleURL, region.getDefinition().styleURL); - EXPECT_EQ(definition.bounds, region.getDefinition().bounds); - EXPECT_EQ(definition.minZoom, region.getDefinition().minZoom); - EXPECT_EQ(definition.maxZoom, region.getDefinition().maxZoom); - EXPECT_EQ(definition.pixelRatio, region.getDefinition().pixelRatio); - EXPECT_EQ(metadata, region.getMetadata()); + auto region = db.createRegion(definition, metadata); + ASSERT_TRUE(region); EXPECT_EQ(0u, log.uncheckedCount()); + + region->getDefinition().match( + [&](OfflineTilePyramidRegionDefinition& def) { + EXPECT_EQ(definition.styleURL, def.styleURL); + EXPECT_EQ(definition.bounds, def.bounds); + EXPECT_EQ(definition.minZoom, def.minZoom); + EXPECT_EQ(definition.maxZoom, def.maxZoom); + EXPECT_EQ(definition.pixelRatio, def.pixelRatio); + }, [](auto&) { + EXPECT_FALSE(false); + } + ); + EXPECT_EQ(metadata, region->getMetadata()); } TEST(OfflineDatabase, UpdateMetadata) { FixtureLog log; OfflineDatabase db(":memory:"); - OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; + OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; OfflineRegionMetadata metadata {{ 1, 2, 3 }}; - OfflineRegion region = db.createRegion(definition, metadata); + auto region = db.createRegion(definition, metadata); + ASSERT_TRUE(region); OfflineRegionMetadata newmetadata {{ 4, 5, 6 }}; - db.updateMetadata(region.getID(), newmetadata); - EXPECT_EQ(db.listRegions().at(0).getMetadata(), newmetadata); + db.updateMetadata(region->getID(), newmetadata); + auto regions = db.listRegions().value(); + EXPECT_EQ(regions.at(0).getMetadata(), newmetadata); EXPECT_EQ(0u, log.uncheckedCount()); } @@ -253,19 +451,27 @@ TEST(OfflineDatabase, UpdateMetadata) { TEST(OfflineDatabase, ListRegions) { FixtureLog log; OfflineDatabase db(":memory:"); - OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; + OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; OfflineRegionMetadata metadata {{ 1, 2, 3 }}; - OfflineRegion region = db.createRegion(definition, metadata); - std::vector<OfflineRegion> regions = db.listRegions(); + auto region = db.createRegion(definition, metadata); + ASSERT_TRUE(region); + auto regions = db.listRegions().value(); 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); - EXPECT_EQ(definition.minZoom, regions.at(0).getDefinition().minZoom); - EXPECT_EQ(definition.maxZoom, regions.at(0).getDefinition().maxZoom); - EXPECT_EQ(definition.pixelRatio, regions.at(0).getDefinition().pixelRatio); + + EXPECT_EQ(region->getID(), regions.at(0).getID()); + regions.at(0).getDefinition().match( + [&](OfflineTilePyramidRegionDefinition& def) { + EXPECT_EQ(definition.styleURL, def.styleURL); + EXPECT_EQ(definition.bounds, def.bounds); + EXPECT_EQ(definition.minZoom, def.minZoom); + EXPECT_EQ(definition.maxZoom, def.maxZoom); + EXPECT_EQ(definition.pixelRatio, def.pixelRatio); + }, + [&](auto&) { + EXPECT_FALSE(false); + }); EXPECT_EQ(metadata, regions.at(0).getMetadata()); EXPECT_EQ(0u, log.uncheckedCount()); @@ -274,37 +480,44 @@ TEST(OfflineDatabase, ListRegions) { TEST(OfflineDatabase, GetRegionDefinition) { FixtureLog log; OfflineDatabase db(":memory:"); - OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; + OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; OfflineRegionMetadata metadata {{ 1, 2, 3 }}; - OfflineRegion region = db.createRegion(definition, metadata); - OfflineRegionDefinition result = db.getRegionDefinition(region.getID()); - - EXPECT_EQ(definition.styleURL, result.styleURL); - EXPECT_EQ(definition.bounds, result.bounds); - EXPECT_EQ(definition.minZoom, result.minZoom); - EXPECT_EQ(definition.maxZoom, result.maxZoom); - EXPECT_EQ(definition.pixelRatio, result.pixelRatio); - EXPECT_EQ(0u, log.uncheckedCount()); + + auto region = db.createRegion(definition, metadata); + db.getRegionDefinition(region->getID())->match( + [&](OfflineTilePyramidRegionDefinition& result) { + EXPECT_EQ(definition.styleURL, result.styleURL); + EXPECT_EQ(definition.bounds, result.bounds); + EXPECT_EQ(definition.minZoom, result.minZoom); + EXPECT_EQ(definition.maxZoom, result.maxZoom); + EXPECT_EQ(definition.pixelRatio, result.pixelRatio); + }, + [&](auto&) { + EXPECT_FALSE(false); + } + ); } TEST(OfflineDatabase, DeleteRegion) { FixtureLog log; OfflineDatabase db(":memory:"); - OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; + OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; OfflineRegionMetadata metadata {{ 1, 2, 3 }}; - OfflineRegion region = db.createRegion(definition, metadata); + auto region = db.createRegion(definition, metadata); + ASSERT_TRUE(region); Response response; response.noContent = true; - db.putRegionResource(region.getID(), Resource::style("http://example.com/"), response); - db.putRegionResource(region.getID(), Resource::tile("http://example.com/", 1.0, 0, 0, 0, Tileset::Scheme::XYZ), response); + db.putRegionResource(region->getID(), Resource::style("http://example.com/"), response); + db.putRegionResource(region->getID(), Resource::tile("http://example.com/", 1.0, 0, 0, 0, Tileset::Scheme::XYZ), response); - db.deleteRegion(std::move(region)); + db.deleteRegion(std::move(*region)); - ASSERT_EQ(0u, db.listRegions().size()); + auto regions = db.listRegions().value(); + ASSERT_EQ(0u, regions.size()); EXPECT_EQ(0u, log.uncheckedCount()); } @@ -312,40 +525,39 @@ TEST(OfflineDatabase, DeleteRegion) { TEST(OfflineDatabase, CreateRegionInfiniteMaxZoom) { FixtureLog log; OfflineDatabase db(":memory:"); - OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; + OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; OfflineRegionMetadata metadata; - OfflineRegion region = db.createRegion(definition, metadata); - - EXPECT_EQ(0, region.getDefinition().minZoom); - EXPECT_EQ(INFINITY, region.getDefinition().maxZoom); + auto region = db.createRegion(definition, metadata); + ASSERT_TRUE(region); EXPECT_EQ(0u, log.uncheckedCount()); + + region->getDefinition().match([&](auto& def) { + EXPECT_EQ(0, def.minZoom); + EXPECT_EQ(INFINITY, def.maxZoom); + }); } TEST(OfflineDatabase, TEST_REQUIRES_WRITE(ConcurrentUse)) { FixtureLog log; - util::deleteFile(filename); + deleteDatabaseFiles(); OfflineDatabase db1(filename); EXPECT_EQ(0u, log.uncheckedCount()); OfflineDatabase db2(filename); - Resource resource { Resource::Style, "http://example.com/" }; - Response response; - response.noContent = true; - std::thread thread1([&] { for (auto i = 0; i < 100; i++) { - db1.put(resource, response); - EXPECT_TRUE(bool(db1.get(resource))); + db1.put(fixture::resource, fixture::response); + EXPECT_TRUE(bool(db1.get(fixture::resource))); } }); std::thread thread2([&] { for (auto i = 0; i < 100; i++) { - db2.put(resource, response); - EXPECT_TRUE(bool(db2.get(resource))); + db2.put(fixture::resource, fixture::response); + EXPECT_TRUE(bool(db2.get(fixture::resource))); } }); @@ -406,14 +618,15 @@ TEST(OfflineDatabase, PutEvictsLeastRecentlyUsedResources) { TEST(OfflineDatabase, PutRegionResourceDoesNotEvict) { FixtureLog log; OfflineDatabase db(":memory:", 1024 * 100); - OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; - OfflineRegion region = db.createRegion(definition, OfflineRegionMetadata()); + OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; + auto region = db.createRegion(definition, OfflineRegionMetadata()); + ASSERT_TRUE(region); Response response; response.data = randomString(1024); for (uint32_t i = 1; i <= 100; i++) { - db.putRegionResource(region.getID(), Resource::style("http://example.com/"s + util::toString(i)), response); + db.putRegionResource(region->getID(), Resource::style("http://example.com/"s + util::toString(i)), response); } EXPECT_TRUE(bool(db.get(Resource::style("http://example.com/1")))); @@ -442,34 +655,38 @@ TEST(OfflineDatabase, PutFailsWhenEvictionInsuffices) { TEST(OfflineDatabase, GetRegionCompletedStatus) { FixtureLog log; OfflineDatabase db(":memory:"); - OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; + OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; OfflineRegionMetadata metadata; - OfflineRegion region = db.createRegion(definition, metadata); + auto region = db.createRegion(definition, metadata); + ASSERT_TRUE(region); - OfflineRegionStatus status1 = db.getRegionCompletedStatus(region.getID()); - EXPECT_EQ(0u, status1.completedResourceCount); - EXPECT_EQ(0u, status1.completedResourceSize); - EXPECT_EQ(0u, status1.completedTileCount); - EXPECT_EQ(0u, status1.completedTileSize); + auto status1 = db.getRegionCompletedStatus(region->getID()); + ASSERT_TRUE(status1); + 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"); - uint64_t styleSize = db.putRegionResource(region.getID(), Resource::style("http://example.com/"), response); + uint64_t styleSize = db.putRegionResource(region->getID(), Resource::style("http://example.com/"), response); - OfflineRegionStatus status2 = db.getRegionCompletedStatus(region.getID()); - EXPECT_EQ(1u, status2.completedResourceCount); - EXPECT_EQ(styleSize, status2.completedResourceSize); - EXPECT_EQ(0u, status2.completedTileCount); - EXPECT_EQ(0u, status2.completedTileSize); + auto status2 = db.getRegionCompletedStatus(region->getID()); + ASSERT_TRUE(status2); + EXPECT_EQ(1u, status2->completedResourceCount); + EXPECT_EQ(styleSize, status2->completedResourceSize); + 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, Tileset::Scheme::XYZ), response); + uint64_t tileSize = db.putRegionResource(region->getID(), Resource::tile("http://example.com/", 1.0, 0, 0, 0, Tileset::Scheme::XYZ), response); - OfflineRegionStatus status3 = db.getRegionCompletedStatus(region.getID()); - EXPECT_EQ(2u, status3.completedResourceCount); - EXPECT_EQ(styleSize + tileSize, status3.completedResourceSize); - EXPECT_EQ(1u, status3.completedTileCount); - EXPECT_EQ(tileSize, status3.completedTileSize); + auto status3 = db.getRegionCompletedStatus(region->getID()); + ASSERT_TRUE(status3); + EXPECT_EQ(2u, status3->completedResourceCount); + EXPECT_EQ(styleSize + tileSize, status3->completedResourceSize); + EXPECT_EQ(1u, status3->completedTileCount); + EXPECT_EQ(tileSize, status3->completedTileSize); EXPECT_EQ(0u, log.uncheckedCount()); } @@ -477,22 +694,23 @@ TEST(OfflineDatabase, GetRegionCompletedStatus) { TEST(OfflineDatabase, HasRegionResource) { FixtureLog log; OfflineDatabase db(":memory:", 1024 * 100); - OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; - OfflineRegion region = db.createRegion(definition, OfflineRegionMetadata()); + OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; + auto region = db.createRegion(definition, OfflineRegionMetadata()); + ASSERT_TRUE(region); - EXPECT_FALSE(bool(db.hasRegionResource(region.getID(), Resource::style("http://example.com/1")))); - EXPECT_FALSE(bool(db.hasRegionResource(region.getID(), Resource::style("http://example.com/20")))); + EXPECT_FALSE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/1")))); + EXPECT_FALSE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/20")))); Response response; response.data = randomString(1024); for (uint32_t i = 1; i <= 100; i++) { - db.putRegionResource(region.getID(), Resource::style("http://example.com/"s + util::toString(i)), response); + db.putRegionResource(region->getID(), Resource::style("http://example.com/"s + util::toString(i)), response); } - EXPECT_TRUE(bool(db.hasRegionResource(region.getID(), Resource::style("http://example.com/1")))); - EXPECT_TRUE(bool(db.hasRegionResource(region.getID(), Resource::style("http://example.com/20")))); - EXPECT_EQ(1024, *(db.hasRegionResource(region.getID(), Resource::style("http://example.com/20")))); + EXPECT_TRUE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/1")))); + EXPECT_TRUE(bool(db.hasRegionResource(region->getID(), Resource::style("http://example.com/20")))); + EXPECT_EQ(1024, *(db.hasRegionResource(region->getID(), Resource::style("http://example.com/20")))); EXPECT_EQ(0u, log.uncheckedCount()); } @@ -500,8 +718,9 @@ TEST(OfflineDatabase, HasRegionResource) { TEST(OfflineDatabase, HasRegionResourceTile) { FixtureLog log; OfflineDatabase db(":memory:", 1024 * 100); - OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; - OfflineRegion region = db.createRegion(definition, OfflineRegionMetadata()); + OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; + auto region = db.createRegion(definition, OfflineRegionMetadata()); + ASSERT_TRUE(region); Resource resource { Resource::Tile, "http://example.com/" }; resource.tileData = Resource::TileData { @@ -515,15 +734,16 @@ TEST(OfflineDatabase, HasRegionResourceTile) { response.data = std::make_shared<std::string>("first"); - EXPECT_FALSE(bool(db.hasRegionResource(region.getID(), resource))); - db.putRegionResource(region.getID(), resource, response); - EXPECT_TRUE(bool(db.hasRegionResource(region.getID(), resource))); - EXPECT_EQ(5, *(db.hasRegionResource(region.getID(), resource))); + EXPECT_FALSE(bool(db.hasRegionResource(region->getID(), resource))); + db.putRegionResource(region->getID(), resource, response); + EXPECT_TRUE(bool(db.hasRegionResource(region->getID(), resource))); + EXPECT_EQ(5, *(db.hasRegionResource(region->getID(), resource))); - OfflineRegion anotherRegion = db.createRegion(definition, OfflineRegionMetadata()); - EXPECT_LT(region.getID(), anotherRegion.getID()); - EXPECT_TRUE(bool(db.hasRegionResource(anotherRegion.getID(), resource))); - EXPECT_EQ(5, *(db.hasRegionResource(anotherRegion.getID(), resource))); + auto anotherRegion = db.createRegion(definition, OfflineRegionMetadata()); + ASSERT_TRUE(anotherRegion); + EXPECT_LT(region->getID(), anotherRegion->getID()); + EXPECT_TRUE(bool(db.hasRegionResource(anotherRegion->getID(), resource))); + EXPECT_EQ(5, *(db.hasRegionResource(anotherRegion->getID(), resource))); EXPECT_EQ(0u, log.uncheckedCount()); @@ -532,11 +752,13 @@ TEST(OfflineDatabase, HasRegionResourceTile) { TEST(OfflineDatabase, OfflineMapboxTileCount) { FixtureLog log; OfflineDatabase db(":memory:"); - OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; + OfflineTilePyramidRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 }; OfflineRegionMetadata metadata; - OfflineRegion region1 = db.createRegion(definition, metadata); - OfflineRegion region2 = db.createRegion(definition, metadata); + auto region1 = db.createRegion(definition, metadata); + ASSERT_TRUE(region1); + auto region2 = db.createRegion(definition, metadata); + ASSERT_TRUE(region2); Resource nonMapboxTile = Resource::tile("http://example.com/", 1.0, 0, 0, 0, Tileset::Scheme::XYZ); Resource mapboxTile1 = Resource::tile("mapbox://tiles/1", 1.0, 0, 0, 0, Tileset::Scheme::XYZ); @@ -549,27 +771,27 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) { 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); + db.putRegionResource(region1->getID(), Resource::style("http://example.com/"), response); EXPECT_EQ(0u, db.getOfflineMapboxTileCount()); // Count stays the same after putting a non-Mapbox tile. - db.putRegionResource(region1.getID(), nonMapboxTile, response); + db.putRegionResource(region1->getID(), nonMapboxTile, response); EXPECT_EQ(0u, db.getOfflineMapboxTileCount()); // Count increases after putting a Mapbox tile not used by another region. - db.putRegionResource(region1.getID(), mapboxTile1, response); + db.putRegionResource(region1->getID(), mapboxTile1, response); EXPECT_EQ(1u, db.getOfflineMapboxTileCount()); // Count stays the same after putting a Mapbox tile used by another region. - db.putRegionResource(region2.getID(), mapboxTile1, response); + db.putRegionResource(region2->getID(), mapboxTile1, response); 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); + db.putRegionResource(region2->getID(), mapboxTile1, response); 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)); + db.deleteRegion(std::move(*region2)); EXPECT_EQ(1u, db.getOfflineMapboxTileCount()); // Count stays the same after the putting a non-offline Mapbox tile. @@ -577,11 +799,11 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) { EXPECT_EQ(1u, db.getOfflineMapboxTileCount()); // Count increases after putting a pre-existing, but non-offline Mapbox tile. - db.putRegionResource(region1.getID(), mapboxTile2, response); + db.putRegionResource(region1->getID(), mapboxTile2, response); 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)); + db.deleteRegion(std::move(*region1)); EXPECT_EQ(0u, db.getOfflineMapboxTileCount()); EXPECT_EQ(0u, log.uncheckedCount()); @@ -591,8 +813,9 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) { TEST(OfflineDatabase, BatchInsertion) { FixtureLog log; OfflineDatabase db(":memory:", 1024 * 100); - OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; - OfflineRegion region = db.createRegion(definition, OfflineRegionMetadata()); + OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; + auto region = db.createRegion(definition, OfflineRegionMetadata()); + ASSERT_TRUE(region); Response response; response.data = randomString(1024); @@ -603,7 +826,7 @@ TEST(OfflineDatabase, BatchInsertion) { } OfflineRegionStatus status; - db.putRegionResources(region.getID(), resources, status); + db.putRegionResources(region->getID(), resources, status); for (uint32_t i = 1; i <= 100; i++) { EXPECT_TRUE(bool(db.get(Resource::style("http://example.com/"s + util::toString(i))))); @@ -616,87 +839,46 @@ TEST(OfflineDatabase, BatchInsertionMapboxTileCountExceeded) { FixtureLog log; OfflineDatabase db(":memory:", 1024 * 100); db.setOfflineMapboxTileCountLimit(1); - OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; - OfflineRegion region = db.createRegion(definition, OfflineRegionMetadata()); - + OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 }; + auto region = db.createRegion(definition, OfflineRegionMetadata()); + ASSERT_TRUE(region); + Response response; response.data = randomString(1024); std::list<std::tuple<Resource, Response>> resources; - + resources.emplace_back(Resource::style("http://example.com/"), response); resources.emplace_back(Resource::tile("mapbox://tiles/1", 1.0, 0, 0, 0, Tileset::Scheme::XYZ), response); resources.emplace_back(Resource::tile("mapbox://tiles/2", 1.0, 0, 0, 0, Tileset::Scheme::XYZ), response); - + OfflineRegionStatus status; try { - db.putRegionResources(region.getID(), resources, status); + db.putRegionResources(region->getID(), resources, status); EXPECT_FALSE(true); } catch (const MapboxTileLimitExceededException&) { // Expected } - - EXPECT_EQ(status.completedTileCount, 1u); - EXPECT_EQ(status.completedResourceCount, 2u); - EXPECT_EQ(db.getRegionCompletedStatus(region.getID()).completedTileCount, 1u); - EXPECT_EQ(db.getRegionCompletedStatus(region.getID()).completedResourceCount, 2u); - EXPECT_EQ(0u, log.uncheckedCount()); -} + EXPECT_EQ(0u, status.completedTileCount); + EXPECT_EQ(0u, status.completedResourceCount); + const auto completedStatus = db.getRegionCompletedStatus(region->getID()).value(); + EXPECT_EQ(1u, completedStatus.completedTileCount); + EXPECT_EQ(2u, completedStatus.completedResourceCount); -static int databasePageCount(const std::string& path) { - mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt{ db, "pragma page_count" }; - mapbox::sqlite::Query query{ stmt }; - query.run(); - return query.get<int>(0); -} - -static int databaseUserVersion(const std::string& path) { - mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt{ db, "pragma user_version" }; - mapbox::sqlite::Query query{ stmt }; - query.run(); - return query.get<int>(0); -} - -static std::string databaseJournalMode(const std::string& path) { - mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt{ db, "pragma journal_mode" }; - mapbox::sqlite::Query query{ stmt }; - query.run(); - return query.get<std::string>(0); -} - -static int databaseSyncMode(const std::string& path) { - mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt{ db, "pragma synchronous" }; - mapbox::sqlite::Query query{ stmt }; - query.run(); - return query.get<int>(0); -} - -static std::vector<std::string> databaseTableColumns(const std::string& path, const std::string& name) { - mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); - const auto sql = std::string("pragma table_info(") + name + ")"; - mapbox::sqlite::Statement stmt{ db, sql.c_str() }; - mapbox::sqlite::Query query{ stmt }; - std::vector<std::string> columns; - while (query.run()) { - columns.push_back(query.get<std::string>(1)); - } - return columns; + EXPECT_EQ(0u, log.uncheckedCount()); } TEST(OfflineDatabase, MigrateFromV2Schema) { // v2.db is a v2 database containing a single offline region with a small number of resources. FixtureLog log; - util::deleteFile(filename); + deleteDatabaseFiles(); util::copyFile(filename, "test/fixtures/offline_database/v2.db"); { OfflineDatabase db(filename, 0); auto regions = db.listRegions(); - for (auto& region : regions) { + ASSERT_TRUE(regions); + for (auto& region : regions.value()) { db.deleteRegion(std::move(region)); } } @@ -711,12 +893,12 @@ TEST(OfflineDatabase, MigrateFromV2Schema) { TEST(OfflineDatabase, MigrateFromV3Schema) { // v3.db is a v3 database, migrated from v2. FixtureLog log; - util::deleteFile(filename); + deleteDatabaseFiles(); util::copyFile(filename, "test/fixtures/offline_database/v3.db"); { OfflineDatabase db(filename, 0); - auto regions = db.listRegions(); + auto regions = db.listRegions().value(); for (auto& region : regions) { db.deleteRegion(std::move(region)); } @@ -730,12 +912,12 @@ TEST(OfflineDatabase, MigrateFromV3Schema) { TEST(OfflineDatabase, MigrateFromV4Schema) { // v4.db is a v4 database, migrated from v2 & v3. This database used `journal_mode = WAL` and `synchronous = NORMAL`. FixtureLog log; - util::deleteFile(filename); + deleteDatabaseFiles(); util::copyFile(filename, "test/fixtures/offline_database/v4.db"); { OfflineDatabase db(filename, 0); - auto regions = db.listRegions(); + auto regions = db.listRegions().value(); for (auto& region : regions) { db.deleteRegion(std::move(region)); } @@ -756,12 +938,12 @@ TEST(OfflineDatabase, MigrateFromV4Schema) { TEST(OfflineDatabase, MigrateFromV5Schema) { // v5.db is a v5 database, migrated from v2, v3 & v4. FixtureLog log; - util::deleteFile(filename); + deleteDatabaseFiles(); util::copyFile(filename, "test/fixtures/offline_database/v5.db"); { OfflineDatabase db(filename, 0); - auto regions = db.listRegions(); + auto regions = db.listRegions().value(); for (auto& region : regions) { db.deleteRegion(std::move(region)); } @@ -804,3 +986,132 @@ TEST(OfflineDatabase, DowngradeSchema) { EXPECT_EQ(1u, log.count({ EventSeverity::Warning, Event::Database, -1, "Removing existing incompatible offline database" })); EXPECT_EQ(0u, log.uncheckedCount()); } + +TEST(OfflineDatabase, CorruptDatabaseOnOpen) { + FixtureLog log; + util::deleteFile(filename); + util::copyFile(filename, "test/fixtures/offline_database/corrupt-immediate.db"); + + // This database is corrupt in a way that will prevent opening the database. + OfflineDatabase db(filename); + EXPECT_EQ(1u, log.count(error(ResultCode::Corrupt, "Can't open database: database disk image is malformed"), true)); + EXPECT_EQ(1u, log.count({ EventSeverity::Warning, Event::Database, -1, "Removing existing incompatible offline database" })); + EXPECT_EQ(0u, log.uncheckedCount()); + + // Now try inserting and reading back to make sure we have a valid database. + for (const auto& res : { fixture::resource, fixture::tile }) { + EXPECT_EQ(std::make_pair(true, uint64_t(5)), db.put(res, fixture::response)); + EXPECT_EQ(0u, log.uncheckedCount()); + auto result = db.get(res); + EXPECT_EQ(0u, log.uncheckedCount()); + ASSERT_TRUE(result && result->data); + EXPECT_EQ("first", *result->data); + } +} + +TEST(OfflineDatabase, CorruptDatabaseOnQuery) { + FixtureLog log; + util::deleteFile(filename); + util::copyFile(filename, "test/fixtures/offline_database/corrupt-delayed.db"); + + // This database is corrupt in a way that won't manifest itself until we start querying it, + // so just opening it will not cause an error. + OfflineDatabase db(filename); + + // Just opening this corrupt database should not have produced an error yet, since + // PRAGMA user_version still succeeds with this database. + EXPECT_EQ(0u, log.uncheckedCount()); + + // The first request fails because the database is corrupt and has to be recreated. + EXPECT_EQ(nullopt, db.get(fixture::tile)); + EXPECT_EQ(1u, log.count(error(ResultCode::Corrupt, "Can't read resource: database disk image is malformed"), true)); + EXPECT_EQ(1u, log.count({ EventSeverity::Warning, Event::Database, -1, "Removing existing incompatible offline database" })); + EXPECT_EQ(0u, log.uncheckedCount()); + + // Now try inserting and reading back to make sure we have a valid database. + for (const auto& res : { fixture::resource, fixture::tile }) { + EXPECT_EQ(std::make_pair(true, uint64_t(5)), db.put(res, fixture::response)); + EXPECT_EQ(0u, log.uncheckedCount()); + auto result = db.get(res); + EXPECT_EQ(0u, log.uncheckedCount()); + ASSERT_TRUE(result && result->data); + EXPECT_EQ("first", *result->data); + } +} + +#ifndef __QT__ // Qt doesn't expose the ability to register virtual file system handlers. +TEST(OfflineDatabase, TEST_REQUIRES_WRITE(DisallowedIO)) { + FixtureLog log; + deleteDatabaseFiles(); + test::SQLite3TestFS fs; + + OfflineDatabase db(filename_test_fs); + EXPECT_EQ(0u, log.uncheckedCount()); + + // First, create a region object so that we can try deleting it later. + OfflineTilePyramidRegionDefinition definition( + "mapbox://style", LatLngBounds::hull({ 37.66, -122.57 }, { 37.83, -122.32 }), 0, 8, 2); + auto region = db.createRegion(definition, {}); + ASSERT_TRUE(region); + + // Now forbid any type of IO on the database and test that none of the calls crashes. + fs.allowIO(false); + + EXPECT_EQ(nullopt, db.get(fixture::resource)); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't update timestamp: authorization denied"))); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't read resource: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_EQ(std::make_pair(false, uint64_t(0)), db.put(fixture::resource, fixture::response)); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't write resource: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_FALSE(db.listRegions()); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't list regions: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_FALSE(db.createRegion(definition, {})); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't create region: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_FALSE(db.updateMetadata(region->getID(), {})); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't update region metadata: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_EQ(nullopt, db.getRegionResource(region->getID(), fixture::resource)); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't update timestamp: authorization denied"))); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't read region resource: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_EQ(nullopt, db.hasRegionResource(region->getID(), fixture::resource)); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't query region resource: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_EQ(0u, db.putRegionResource(region->getID(), fixture::resource, fixture::response)); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't write region resource: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + OfflineRegionStatus status; + db.putRegionResources(region->getID(), { std::make_tuple(fixture::resource, fixture::response) }, status); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't write region resources: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_FALSE(db.getRegionDefinition(region->getID())); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't load region: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_FALSE(db.getRegionCompletedStatus(region->getID())); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't get region status: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_TRUE(db.deleteRegion(std::move(*region))); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't delete region: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), db.getOfflineMapboxTileCount()); + EXPECT_EQ(1u, log.count(warning(ResultCode::Auth, "Can't get offline Mapbox tile count: authorization denied"))); + EXPECT_EQ(0u, log.uncheckedCount()); + + fs.reset(); +} +#endif // __QT__ diff --git a/test/storage/offline_download.test.cpp b/test/storage/offline_download.test.cpp index 57780eba40..492e68e869 100644 --- a/test/storage/offline_download.test.cpp +++ b/test/storage/offline_download.test.cpp @@ -1,5 +1,7 @@ #include <mbgl/test/stub_file_source.hpp> #include <mbgl/test/fake_file_source.hpp> +#include <mbgl/test/fixture_log_observer.hpp> +#include <mbgl/test/sqlite3_test_fs.hpp> #include <mbgl/storage/offline.hpp> #include <mbgl/storage/offline_database.hpp> @@ -10,11 +12,29 @@ #include <mbgl/util/compression.hpp> #include <mbgl/util/string.hpp> +#include <sqlite3.hpp> #include <gtest/gtest.h> #include <iostream> using namespace mbgl; using namespace std::literals::string_literals; +using mapbox::sqlite::ResultCode; + +#ifndef __QT__ // Qt doesn't expose the ability to register virtual file system handlers. +static constexpr const char* filename = "test/fixtures/offline_download/offline.db"; +static constexpr const char* filename_test_fs = "file:test/fixtures/offline_download/offline.db?vfs=test_fs"; + +static void deleteDatabaseFiles() { + // Delete leftover journaling files as well. + util::deleteFile(filename); + util::deleteFile(filename + "-wal"s); + util::deleteFile(filename + "-journal"s); +} + +static FixtureLog::Message warning(ResultCode code, const char* message) { + return { EventSeverity::Warning, Event::Database, static_cast<int64_t>(code), message }; +} +#endif class MockObserver : public OfflineRegionObserver { public: @@ -37,13 +57,16 @@ public: class OfflineTest { public: + OfflineTest(const std::string& path = ":memory:") : db(path) { + } + util::RunLoop loop; StubFileSource fileSource; - OfflineDatabase db { ":memory:" }; + OfflineDatabase db; std::size_t size = 0; - OfflineRegion createRegion() { - OfflineRegionDefinition definition { "", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 1.0 }; + auto createRegion() { + OfflineTilePyramidRegionDefinition definition { "", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 1.0 }; OfflineRegionMetadata metadata; return db.createRegion(definition, metadata); } @@ -60,9 +83,10 @@ public: TEST(OfflineDownload, NoSubresources) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -100,9 +124,10 @@ TEST(OfflineDownload, NoSubresources) { TEST(OfflineDownload, InlineSource) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -140,9 +165,10 @@ TEST(OfflineDownload, InlineSource) { TEST(OfflineDownload, GeoJSONSource) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -175,9 +201,10 @@ TEST(OfflineDownload, GeoJSONSource) { TEST(OfflineDownload, Activate) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -250,9 +277,10 @@ TEST(OfflineDownload, Activate) { TEST(OfflineDownload, DoesNotFloodTheFileSourceWithRequests) { FakeFileSource fileSource; OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, fileSource); @@ -272,9 +300,10 @@ TEST(OfflineDownload, DoesNotFloodTheFileSourceWithRequests) { TEST(OfflineDownload, GetStatusNoResources) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); OfflineRegionStatus status = download.getStatus(); @@ -289,9 +318,10 @@ TEST(OfflineDownload, GetStatusNoResources) { TEST(OfflineDownload, GetStatusStyleComplete) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -311,9 +341,10 @@ TEST(OfflineDownload, GetStatusStyleComplete) { TEST(OfflineDownload, GetStatusStyleAndSourceComplete) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -337,9 +368,10 @@ TEST(OfflineDownload, GetStatusStyleAndSourceComplete) { TEST(OfflineDownload, RequestError) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -365,9 +397,10 @@ TEST(OfflineDownload, RequestError) { TEST(OfflineDownload, RequestErrorsAreRetried) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -398,9 +431,10 @@ TEST(OfflineDownload, RequestErrorsAreRetried) { TEST(OfflineDownload, TileCountLimitExceededNoTileResponse) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -440,9 +474,10 @@ TEST(OfflineDownload, TileCountLimitExceededNoTileResponse) { TEST(OfflineDownload, TileCountLimitExceededWithTileResponse) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -494,9 +529,10 @@ TEST(OfflineDownload, TileCountLimitExceededWithTileResponse) { TEST(OfflineDownload, WithPreviouslyExistingTile) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -528,9 +564,10 @@ TEST(OfflineDownload, WithPreviouslyExistingTile) { TEST(OfflineDownload, ReactivatePreviouslyCompletedDownload) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -556,7 +593,7 @@ TEST(OfflineDownload, ReactivatePreviouslyCompletedDownload) { test.loop.run(); OfflineDownload redownload( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -595,9 +632,10 @@ TEST(OfflineDownload, ReactivatePreviouslyCompletedDownload) { TEST(OfflineDownload, Deactivate) { OfflineTest test; - OfflineRegion region = test.createRegion(); + auto region = test.createRegion(); + ASSERT_TRUE(region); OfflineDownload download( - region.getID(), + region->getID(), OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), test.db, test.fileSource); @@ -621,3 +659,56 @@ TEST(OfflineDownload, Deactivate) { test.loop.run(); } + +#ifndef __QT__ // Qt doesn't expose the ability to register virtual file system handlers. +TEST(OfflineDownload, DiskFull) { + FixtureLog log; + deleteDatabaseFiles(); + test::SQLite3TestFS fs; + + OfflineTest test{ filename_test_fs }; + EXPECT_EQ(0u, log.uncheckedCount()); + + auto region = test.createRegion(); + ASSERT_TRUE(region); + EXPECT_EQ(0u, log.uncheckedCount()); + + // Simulate a full disk. + fs.setWriteLimit(8192); + + OfflineDownload download( + region->getID(), + OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0), + test.db, test.fileSource); + + bool hasRequestedStyle = false; + + test.fileSource.styleResponse = [&] (const Resource& resource) { + EXPECT_EQ("http://127.0.0.1:3000/style.json", resource.url); + hasRequestedStyle = true; + return test.response("empty.style.json"); + }; + + auto observer = std::make_unique<MockObserver>(); + + observer->statusChangedFn = [&] (OfflineRegionStatus status) { + EXPECT_EQ(OfflineRegionDownloadState::Active, status.downloadState); + EXPECT_EQ(0u, status.completedResourceCount); + EXPECT_EQ(0u, status.completedResourceSize); + EXPECT_EQ(hasRequestedStyle, status.requiredResourceCountIsPrecise); + EXPECT_FALSE(status.complete()); + + if (hasRequestedStyle) { + EXPECT_EQ(1u, log.count(warning(ResultCode::Full, "Can't write region resources: database or disk is full"))); + EXPECT_EQ(0u, log.uncheckedCount()); + test.loop.stop(); + } + }; + + download.setObserver(std::move(observer)); + download.setState(OfflineRegionDownloadState::Active); + + test.loop.run(); + EXPECT_EQ(0u, log.uncheckedCount()); +} +#endif // __QT__ diff --git a/test/storage/sqlite.test.cpp b/test/storage/sqlite.test.cpp index 22958c8bed..cdbb7f26d7 100644 --- a/test/storage/sqlite.test.cpp +++ b/test/storage/sqlite.test.cpp @@ -38,12 +38,6 @@ TEST(SQLite, TEST_REQUIRES_WRITE(TryOpen)) { auto result = mapbox::sqlite::Database::tryOpen("test/fixtures/offline_database/foobar123.db", mapbox::sqlite::ReadOnly); ASSERT_TRUE(result.is<mapbox::sqlite::Exception>()); ASSERT_EQ(result.get<mapbox::sqlite::Exception>().code, mapbox::sqlite::ResultCode::CantOpen); - -#ifndef __QT__ - // Only non-Qt platforms are setting a logger on the SQLite object. - EXPECT_EQ(1u, log.count({ EventSeverity::Info, Event::Database, static_cast<int64_t>(mapbox::sqlite::ResultCode::CantOpen), "cannot open file" }, true)); - EXPECT_EQ(1u, log.count({ EventSeverity::Info, Event::Database, static_cast<int64_t>(mapbox::sqlite::ResultCode::CantOpen), "No such file or directory" }, true)); -#endif EXPECT_EQ(0u, log.uncheckedCount()); } diff --git a/test/style/conversion/geojson_options.test.cpp b/test/style/conversion/geojson_options.test.cpp index 4c5a0c9aa4..181189775b 100644 --- a/test/style/conversion/geojson_options.test.cpp +++ b/test/style/conversion/geojson_options.test.cpp @@ -32,6 +32,7 @@ TEST(GeoJSONOptions, RetainsDefaults) { ASSERT_EQ(converted.maxzoom, defaults.maxzoom); ASSERT_EQ(converted.buffer, defaults.buffer); ASSERT_EQ(converted.tolerance, defaults.tolerance); + ASSERT_EQ(converted.lineMetrics, defaults.lineMetrics); // Supercluster ASSERT_EQ(converted.cluster, defaults.cluster); @@ -47,7 +48,8 @@ TEST(GeoJSONOptions, FullConversion) { "tolerance": 3, "cluster": true, "clusterRadius": 4, - "clusterMaxZoom": 5 + "clusterMaxZoom": 5, + "lineMetrics": true })JSON", error); // GeoJSON-VT @@ -55,6 +57,7 @@ TEST(GeoJSONOptions, FullConversion) { ASSERT_EQ(converted.maxzoom, 1); ASSERT_EQ(converted.buffer, 2); ASSERT_EQ(converted.tolerance, 3); + ASSERT_TRUE(converted.lineMetrics); // Supercluster ASSERT_EQ(converted.cluster, true); diff --git a/test/style/conversion/light.test.cpp b/test/style/conversion/light.test.cpp index f111e40ff3..092c476277 100644 --- a/test/style/conversion/light.test.cpp +++ b/test/style/conversion/light.test.cpp @@ -1,9 +1,9 @@ #include <mbgl/test/util.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/constant.hpp> #include <mbgl/style/conversion/light.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/style/position.hpp> #include <mbgl/util/color.hpp> #include <mbgl/util/chrono.hpp> diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp index 0b46facf42..4c2ec5cf92 100644 --- a/test/style/expression/expression.test.cpp +++ b/test/style/expression/expression.test.cpp @@ -1,6 +1,6 @@ #include <mbgl/test/util.hpp> #include <mbgl/util/io.hpp> -#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion_impl.hpp> #include <mbgl/util/rapidjson.hpp> #include <mbgl/style/rapidjson_conversion.hpp> #include <mbgl/style/expression/is_expression.hpp> @@ -29,7 +29,11 @@ TEST(Expression, IsExpression) { for(auto& entry : allExpressions.GetObject()) { const std::string name { entry.name.GetString(), entry.name.GetStringLength() }; - if (name == "line-progress" || name == "feature-state") { + if (name == "line-progress" || + name == "feature-state" || + name == "interpolate-hcl" || + name == "interpolate-lab" || + name == "format") { // Not yet implemented continue; } diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index e04a569203..9678fe0895 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -251,3 +251,7 @@ TEST(Filter, ZoomExpressionNested) { TEST(Filter, Internal) { filter(R"(["filter-==","class","snow"])"); } + +TEST(Filter, Short) { + filter(R"(["==", ["id"], "foo"])"); +} diff --git a/test/util/peer.test.cpp b/test/util/peer.test.cpp new file mode 100644 index 0000000000..aa4dae5a88 --- /dev/null +++ b/test/util/peer.test.cpp @@ -0,0 +1,194 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/util/peer.hpp> + +using namespace mbgl::util; + +class TestType { +public: + TestType() : i1(0), i2(1) { + str[0] = 'a'; + } + + //Detect moves + TestType(TestType&& t): i1(t.i1+1), i2(t.i2+2) { + str[0] = t.str[0]+1; + } + + TestType(const TestType&) = delete; + TestType& operator=(const TestType&) = delete; + + int i1; + int i2; + char str[256]; +}; + +bool IsStackAllocated (const peer& a, const void* obj1) { + uintptr_t a_ptr = (uintptr_t)(&a); + uintptr_t obj = (uintptr_t)(obj1); + return (obj >= a_ptr && obj < a_ptr + sizeof(peer)); +}; + +TEST(Peer, Empty) { + EXPECT_FALSE(peer().has_value()); +} + +TEST(Peer, BasicTypes) { + peer i = 3; + EXPECT_TRUE(i.has_value()); + EXPECT_TRUE(i.get<decltype(3)>() == 3); + + auto iValue = i.get<decltype(3)>(); + EXPECT_TRUE(iValue == 3); + + EXPECT_TRUE(peer(4).has_value()); + EXPECT_TRUE(peer(4).get<decltype(4)>() == 4); + + peer f = 6.2f; + EXPECT_TRUE(f.has_value()); + EXPECT_TRUE(f.get<decltype(6.2f)>() == 6.2f); + + const float fValue = f.get<decltype(6.2f)>(); + EXPECT_TRUE(fValue == 6.2f); + + EXPECT_TRUE(peer(1.0f).has_value()); + EXPECT_TRUE(peer(1.0f).get<decltype(1.0f)>() == 1.0f); + + peer c = 'z'; + EXPECT_TRUE(c.has_value()); + EXPECT_TRUE(c.get<decltype('z')>() == 'z'); + + EXPECT_TRUE(peer('z').has_value()); + EXPECT_TRUE(peer('z').get<decltype('z')>() == 'z'); +} + +TEST(Peer, BasicTypes_Move) { + peer i = 3; + EXPECT_TRUE(i.has_value()); + + peer f = 6.2f; + EXPECT_TRUE(f.has_value()); + + f = std::move(i); + EXPECT_FALSE(i.has_value()); + + EXPECT_TRUE(f.has_value()); + EXPECT_TRUE(f.get<decltype(3)>() == 3); +} + +TEST(Peer, SmallType) { + struct T { + T(int32_t* p_) : p(p_) { + (*p)++; + } + + T(T&& t) noexcept : p(t.p) { + (*p)++; + } + + ~T() { + (*p)--; + } + + T(const T&) = delete; + T& operator=(const T&) = delete; + + int32_t* p; + }; + + int32_t p = 0; + + { + peer u1 = peer(T(&p)); + EXPECT_EQ(p, 1); + + auto u2(std::move(u1)); + EXPECT_EQ(p, 1); + } + + EXPECT_EQ(p, 0); +} + +// peer is not able to receive large types, unless we increase storage_t +// capacity. +//TEST(Peer, LargeType) { +// TestType t1; +// peer u1 = peer(std::move(t1)); +// EXPECT_TRUE(u1.has_value()); +// +// //TestType should be moved into owning peer +// EXPECT_EQ(u1.get<TestType>().i1, 1); +// +// auto u2(std::move(u1)); +// EXPECT_FALSE(u1.has_value()); +// +// //TestType should not be moved when owning peer is moved; +// EXPECT_EQ(u2.get<TestType>().i1, 1); +// +// //TestType should be moved out of owning peer +// // Note: two moves are involved in returning the moved value +// // First out of the peer, and then in the return statement +// auto t2 = std::move(u2.get<TestType>()); +// EXPECT_FALSE(u2.has_value()); +// EXPECT_EQ(t2.i1, 3); +//} + +TEST(Peer, Pointer) { + auto t1 = new TestType(); + + auto u1 = peer(std::move(t1)); + EXPECT_TRUE(u1.has_value()); + + //Only the pointer should be moved + TestType * t2 = u1.get<TestType *>(); + EXPECT_EQ(t2->i1, 0); + + peer u2(4); + std::swap(u2, u1); + + EXPECT_TRUE(u1.has_value()); + + EXPECT_TRUE(u2.has_value()); + + t2 = u2.get<TestType *>(); + EXPECT_EQ(t2->i1, 0); + delete t2; +} + + +TEST(Peer, UniquePtr) { + auto t1 = std::make_unique<TestType>(); + auto u1 = peer(std::move(t1)); + + EXPECT_EQ(t1.get(), nullptr); + EXPECT_TRUE(u1.has_value()); + + auto t2 = std::move(u1.take<TestType>()); + EXPECT_FALSE(u1.has_value()); + (void)t2; + + peer u2; + TestType * t3 = new TestType(); + u2 = std::unique_ptr<TestType>(t3); + EXPECT_TRUE(u2.has_value()); +} + +TEST(Peer, SharedPtr) { + + std::shared_ptr<int> shared(new int(3)); + std::weak_ptr<int> weak = shared; + peer u1 = 0; + + EXPECT_EQ(weak.use_count(), 1); + peer u2 = shared; + EXPECT_EQ(weak.use_count(), 2); + + u1 = std::move(u2); + EXPECT_EQ(weak.use_count(), 2); + u2.swap(u1); + EXPECT_EQ(weak.use_count(), 2); + u2 = 0; + EXPECT_EQ(weak.use_count(), 1); + shared = nullptr; + EXPECT_EQ(weak.use_count(), 0); +} diff --git a/test/util/unique_any.test.cpp b/test/util/unique_any.test.cpp deleted file mode 100644 index 9b622cd284..0000000000 --- a/test/util/unique_any.test.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#include <mbgl/test/util.hpp> - -#include <mbgl/util/unique_any.hpp> - -using namespace mbgl::util; - -class TestType { -public: - TestType() : i1(0), i2(1) { - str[0] = 'a'; - } - - //Detect moves - TestType(TestType&& t): i1(t.i1+1), i2(t.i2+2) { - str[0] = t.str[0]+1; - } - - TestType(const TestType&) = delete; - TestType& operator=(const TestType&) = delete; - - int i1; - int i2; - char str[256]; -}; - -bool IsStackAllocated (const unique_any& a, const void* obj1) { - uintptr_t a_ptr = (uintptr_t)(&a); - uintptr_t obj = (uintptr_t)(obj1); - return (obj >= a_ptr && obj < a_ptr + sizeof(unique_any)); -}; - -TEST(UniqueAny, Empty) { - EXPECT_FALSE(unique_any().has_value()); - EXPECT_TRUE(unique_any().type() == typeid(void)); - EXPECT_THROW(any_cast<int>(unique_any()), bad_any_cast); -} - -TEST(UniqueAny, BasicTypes) { - unique_any i = 3; - EXPECT_TRUE(i.has_value()); - EXPECT_TRUE(i.type() == typeid(int)); - EXPECT_TRUE(IsStackAllocated(i, any_cast<int>(&i))); - - auto iValue = any_cast<int>(i); - EXPECT_TRUE(iValue == 3); - - EXPECT_TRUE(unique_any(4).has_value()); - EXPECT_TRUE(unique_any(4).type() == typeid(int)); - - unique_any f = 6.2f; - EXPECT_TRUE(f.has_value()); - EXPECT_TRUE(f.type() == typeid(float)); - EXPECT_TRUE(IsStackAllocated(f, any_cast<float>(&f))); - - const float fValue = any_cast<const float>(f); - EXPECT_TRUE(fValue == 6.2f); - - EXPECT_TRUE(unique_any(1.0f).has_value()); - EXPECT_TRUE(unique_any(1.0f).type() == typeid(float)); - - unique_any c = 'z'; - EXPECT_TRUE(c.has_value()); - EXPECT_TRUE(c.type() == typeid(char)); - EXPECT_TRUE(IsStackAllocated(c, any_cast<char>(&c))); - - EXPECT_THROW(any_cast<float>(c), bad_any_cast); - - EXPECT_TRUE(unique_any('4').has_value()); - EXPECT_TRUE(unique_any('4').type() == typeid(char)); -} - -TEST(UniqueAny, BasicTypes_Move) { - unique_any i = 3; - EXPECT_TRUE(i.has_value()); - EXPECT_TRUE(i.type() == typeid(int)); - - unique_any f = 6.2f; - EXPECT_TRUE(f.has_value()); - EXPECT_TRUE(f.type() == typeid(float)); - - f = std::move(i); - EXPECT_FALSE(i.has_value()); - EXPECT_TRUE(i.type() == typeid(void)); - - EXPECT_TRUE(f.has_value()); - EXPECT_TRUE(f.type() == typeid(int)); - -} - -TEST(UniqueAny, SmallType) { - struct T { - T(int32_t* p_) : p(p_) { - (*p)++; - } - - T(T&& t) noexcept : p(t.p) { - (*p)++; - } - - ~T() { - (*p)--; - } - - T(const T&) = delete; - T& operator=(const T&) = delete; - - int32_t* p; - }; - - int32_t p = 0; - - { - unique_any u1 = unique_any(T(&p)); - EXPECT_EQ(p, 1); - - auto u2(std::move(u1)); - EXPECT_EQ(p, 1); - } - - EXPECT_EQ(p, 0); -} - -TEST(UniqueAny, LargeType) { - TestType t1; - unique_any u1 = unique_any(std::move(t1)); - EXPECT_TRUE(u1.has_value()); - EXPECT_TRUE(u1.type() == typeid(TestType)); - EXPECT_FALSE(IsStackAllocated(u1, any_cast<TestType>(&u1))); - - //TestType should be moved into owning unique_any - EXPECT_EQ(any_cast<TestType>(&u1)->i1, 1); - - auto u2(std::move(u1)); - EXPECT_TRUE(u2.type() == typeid(TestType)); - EXPECT_TRUE(u1.type() == typeid(void)); - - //TestType should not be moved when owning unique_any is moved; - EXPECT_EQ(any_cast<TestType>(&u2)->i1, 1); - - //TestType should be moved out of owning unique_any - // Note: two moves are involved in returning the moved value - // First out of the unique_any, and then in the return statement - auto t2 = any_cast<TestType>(std::move(u2)); - EXPECT_EQ(t2.i1, 3); - EXPECT_TRUE(u2.type() == typeid(void)); -} - -TEST(UniqueAny, Pointer) { - auto t1 = new TestType(); - - auto u1 = unique_any(std::move(t1)); - EXPECT_TRUE(u1.has_value()); - EXPECT_TRUE(u1.type() == typeid(TestType *)); - EXPECT_TRUE(IsStackAllocated(u1, any_cast<TestType *>(&u1))); - - //Only the pointer should be moved - TestType * t2 = *any_cast<TestType *>(&u1); - EXPECT_EQ(t2->i1, 0); - - unique_any u2(4); - std::swap(u2, u1); - - EXPECT_TRUE(u1.has_value()); - EXPECT_TRUE(u1.type() == typeid(int)); - - EXPECT_TRUE(u2.has_value()); - EXPECT_TRUE(u2.type() == typeid(TestType *)); - - t2 = *any_cast<TestType *>(&u2); - EXPECT_EQ(t2->i1, 0); - delete t2; -} - - -TEST(UniqueAny, UniquePtr) { - auto t1 = std::make_unique<TestType>(); - auto u1 = unique_any(std::move(t1)); - - EXPECT_EQ(t1.get(), nullptr); - EXPECT_TRUE(u1.has_value()); - EXPECT_TRUE(u1.type() == typeid(std::unique_ptr<TestType>)); - - EXPECT_TRUE(IsStackAllocated(u1, any_cast<std::unique_ptr<TestType>>(&u1))); - - auto t2 = any_cast<std::unique_ptr<TestType> >(std::move(u1)); - EXPECT_FALSE(u1.has_value()); - - unique_any u2; - TestType * t3 = new TestType(); - u2 = std::unique_ptr<TestType>(t3); - EXPECT_TRUE(u2.has_value()); - EXPECT_TRUE(any_cast<std::unique_ptr<TestType>>(&u2)->get() == t3); -} - -TEST(UniqueAny, SharedPtr) { - - std::shared_ptr<int> shared(new int(3)); - std::weak_ptr<int> weak = shared; - unique_any u1 = 0; - - EXPECT_THROW(any_cast<float>(u1), bad_any_cast); - - EXPECT_EQ(weak.use_count(), 1); - unique_any u2 = shared; - EXPECT_EQ(weak.use_count(), 2); - - EXPECT_EQ(any_cast<std::unique_ptr<int>>(&u1), nullptr); - EXPECT_FALSE(IsStackAllocated(u1, any_cast<std::shared_ptr<TestType>>(&u1))); - - u1 = std::move(u2); - EXPECT_EQ(weak.use_count(), 2); - u2.swap(u1); - EXPECT_EQ(weak.use_count(), 2); - u2 = 0; - EXPECT_EQ(weak.use_count(), 1); - shared = nullptr; - EXPECT_EQ(weak.use_count(), 0); -} diff --git a/vendor/expected/LICENSE.txt b/vendor/expected/LICENSE.txt new file mode 100644 index 0000000000..36b7cd93cd --- /dev/null +++ b/vendor/expected/LICENSE.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/expected/files.txt b/vendor/expected/files.txt new file mode 100644 index 0000000000..258044be9a --- /dev/null +++ b/vendor/expected/files.txt @@ -0,0 +1 @@ +include/expected.hpp diff --git a/vendor/expected/include/expected.hpp b/vendor/expected/include/expected.hpp new file mode 100644 index 0000000000..70298d7237 --- /dev/null +++ b/vendor/expected/include/expected.hpp @@ -0,0 +1,1708 @@ +// This version targets C++11 and later. +// +// Copyright (C) 2016-2018 Martin Moene. +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// expected lite is based on: +// A proposal to add a utility class to represent expected monad +// by Vicente J. Botet Escriba and Pierre Talbot. http:://wg21.link/p0323 + +#ifndef NONSTD_EXPECTED_LITE_HPP +#define NONSTD_EXPECTED_LITE_HPP + +#include <cassert> +#include <exception> +#include <functional> +#include <initializer_list> +#include <new> +#include <stdexcept> +#include <system_error> +#include <type_traits> +#include <utility> + +#define expected_lite_VERSION "0.1.0" + +// expected-lite configuration +// +// DXXXXR0: -- +// N4015 : -2 (2014-05-26) +// N4109 : -1 (2014-06-29) +// P0323R0: 0 (2016-05-28) +// P0323R1: 1 (2016-10-12) +// -------: +// P0323R2: 2 (2017-06-15) +// P0323R3: 3 (2017-10-15) +// P0323R4: 4 (2017-11-26) +// P0323R5: 5 (2018-02-08) +// +// expected-lite uses 2 and higher + +#ifndef nsel_P0323R +# define nsel_P0323R 5 +#endif + +// Compiler detection (C++20 is speculative): +// Note: MSVC supports C++14 since it supports C++17. + +#ifdef _MSVC_LANG +# define type_MSVC_LANG _MSVC_LANG +#else +# define type_MSVC_LANG 0 +#endif + +#define type_CPP11 (__cplusplus == 201103L ) +#define nsel_CPP11_OR_GREATER (__cplusplus >= 201103L || type_MSVC_LANG >= 201103L ) +#define nsel_CPP14_OR_GREATER (__cplusplus >= 201402L || type_MSVC_LANG >= 201703L ) +#define nsel_CPP17_OR_GREATER (__cplusplus >= 201703L || type_MSVC_LANG >= 201703L ) +#define nsel_CPP20_OR_GREATER (__cplusplus >= 202000L || type_MSVC_LANG >= 202000L ) + +#if nsel_CPP14_OR_GREATER +# define nsel_constexpr14 constexpr +#else +# define nsel_constexpr14 /*constexpr*/ +#endif + +#if nsel_CPP17_OR_GREATER +# define nsel_inline17 inline +#else +# define nsel_inline17 /*inline*/ +#endif + +// Method enabling + +#define nsel_REQUIRES(...) \ + typename std::enable_if<__VA_ARGS__, void*>::type = 0 + +#define nsel_REQUIRES_0(...) \ + template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 > + +#define nsel_REQUIRES_T(...) \ + typename = typename std::enable_if< (__VA_ARGS__), nonstd::expected_detail::enabler >::type + +// Clang, GNUC, MSVC warning suppression macros: + +#if defined(_MSC_VER) && !defined(__clang__) +# define nsel_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900)) ) +#else +# define nsel_COMPILER_MSVC_VERSION 0 +#endif + +#ifdef __clang__ +# pragma clang diagnostic push +#elif defined __GNUC__ +# pragma GCC diagnostic push +#endif // __clang__ + +#if nsel_COMPILER_MSVC_VERSION >= 140 +# pragma warning( push ) +# define nsel_DISABLE_MSVC_WARNINGS(codes) __pragma( warning(disable: codes) ) +#else +# define nsel_DISABLE_MSVC_WARNINGS(codes) +#endif + +#ifdef __clang__ +# define nsel_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") +#elif defined __GNUC__ +# define nsel_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") +#elif nsel_COMPILER_MSVC_VERSION >= 140 +# define nsel_RESTORE_WARNINGS() __pragma( warning( pop ) ) +#else +# define nsel_RESTORE_WARNINGS() +#endif + +// Suppress the following MSVC (GSL) warnings: +// - C26409: Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11) + +nsel_DISABLE_MSVC_WARNINGS( 26409 ) + +namespace nonstd { + +namespace std20 { + +// type traits C++20: + +template< typename T > +struct remove_cvref +{ + typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type; +}; + +} // namespace std20 + +// forward declaration: + +template< typename T, typename E > +class expected; + +namespace expected_detail { + +/// for nsel_REQUIRES_T + +enum class enabler{}; + +/// discriminated union to hold value or 'error'. + +template< typename T, typename E > +union storage_t +{ + friend class expected<T,E>; + +private: + using value_type = T; + using error_type = E; + + // no-op construction + storage_t() {} + ~storage_t() {} + + void construct_value( value_type const & v ) + { + new( &m_value ) value_type( v ); + } + + void construct_value( value_type && v ) + { + new( &m_value ) value_type( std::move( v ) ); + } + + void destruct_value() + { + m_value.~value_type(); + } + + void construct_error( error_type const & e ) + { + new( &m_error ) error_type( e ); + } + + void construct_error( error_type && e ) + { + new( &m_error ) error_type( std::move( e ) ); + } + + void destruct_error() + { + m_error.~error_type(); + } + + constexpr value_type const & value() const & + { + return m_value; + } + + value_type & value() & + { + return m_value; + } + + constexpr value_type const && value() const && + { + return std::move( m_value ); + } + + nsel_constexpr14 value_type && value() && + { + return std::move( m_value ); + } + + value_type const * value_ptr() const + { + return &m_value; + } + + value_type * value_ptr() + { + return &m_value; + } + + error_type const & error() const + { + return m_error; + } + + error_type & error() + { + return m_error; + } + +private: + value_type m_value; + error_type m_error; +}; + +/// discriminated union to hold only 'error'. + +template< typename E > +union storage_t<void, E> +{ + friend class expected<void,E>; + +private: + using value_type = void; + using error_type = E; + + // no-op construction + storage_t() {} + ~storage_t() {} + + void construct_error( error_type const & e ) + { + new( &m_error ) error_type( e ); + } + + void construct_error( error_type && e ) + { + new( &m_error ) error_type( std::move( e ) ); + } + + void destruct_error() + { + m_error.~error_type(); + } + + error_type const & error() const + { + return m_error; + } + + error_type & error() + { + return m_error; + } + +private: + error_type m_error; +}; + +} // namespace expected_detail + +/// // x.x.3 Unexpected object type; unexpected_type; C++17 and later can also use aliased type unexpected. + +#if nsel_P0323R <= 2 +template< typename E = std::exception_ptr > +class unexpected_type +#else +template< typename E > +class unexpected_type +#endif // nsel_P0323R +{ +public: + using error_type = E; + + unexpected_type() = delete; + constexpr unexpected_type( unexpected_type const &) = default; + constexpr unexpected_type( unexpected_type &&) = default; + nsel_constexpr14 unexpected_type& operator=( unexpected_type const &) = default; + nsel_constexpr14 unexpected_type& operator=( unexpected_type &&) = default; + + template< typename E2, nsel_REQUIRES_T( + std::is_constructible<E,E2&&>::value ) + > + constexpr explicit unexpected_type( E2 && error ) + : m_error( std::forward<E2>( error ) ) + {} + + template< typename E2 > + constexpr explicit unexpected_type( unexpected_type<E2> const & error, nsel_REQUIRES( + std::is_constructible<E,E2 const &>::value + && !std::is_convertible<E2 const &, E>::value /*=> explicit */ ) + ) + : m_error( error ) + {} + + template< typename E2 > + constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> const & error, nsel_REQUIRES( + std::is_constructible<E,E2 const &>::value + && std::is_convertible<E2 const &, E>::value /*=> non-explicit */ ) + ) + : m_error( error ) + {} + + template< typename E2 > + constexpr explicit unexpected_type( unexpected_type<E2> && error, nsel_REQUIRES( + std::is_constructible<E,E2&&>::value + && !std::is_convertible<E2&&, E>::value /*=> explicit */ ) + ) + : m_error( error ) + {} + + template< typename E2 > + constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> && error, nsel_REQUIRES( + std::is_constructible<E,E2&&>::value + && std::is_convertible<E2&&, E>::value /*=> non-explicit */ ) + ) + : m_error( error ) + {} + + nsel_constexpr14 E & value() & noexcept + { + return m_error; + } + + constexpr E const & value() const & noexcept + { + return m_error; + } + + nsel_constexpr14 E && value() && noexcept + { + return std::move( m_error ); + } + + constexpr E const && value() const && noexcept + { + return std::move( m_error ); + } + +// , nsel_REQUIRES( +// std::is_move_constructible<E>::value +// && std::is_swappable<E>::value ) + + void swap( unexpected_type & rhs ) noexcept ( +#if nsel_CPP17_OR_GREATER + std::is_nothrow_move_constructible<E>::value + && std::is_nothrow_swappable<E&>::value +#else + std::is_nothrow_move_constructible<E>::value + && noexcept ( std::swap( std::declval<E&>(), std::declval<E&>() ) ) +#endif + ) + { + using std::swap; + swap( m_error, rhs.m_error ); + } + +private: + error_type m_error; +}; + +/// class unexpected_type, std::exception_ptr specialization (P0323R2) + +#if nsel_P0323R <= 2 + +template<> +class unexpected_type< std::exception_ptr > +{ +public: + using error_type = std::exception_ptr; + + unexpected_type() = delete; + + ~unexpected_type(){} + + explicit unexpected_type( std::exception_ptr const & error ) + : m_error( error ) + {} + + explicit unexpected_type(std::exception_ptr && error ) + : m_error( std::move( error ) ) + {} + + template< typename E > + explicit unexpected_type( E error ) + : m_error( std::make_exception_ptr( error ) ) + {} + + std::exception_ptr const & value() const + { + return m_error; + } + + std::exception_ptr & value() + { + return m_error; + } + +private: + std::exception_ptr m_error; +}; + +#endif // nsel_P0323R + +/// x.x.4, Unexpected equality operators + +template< typename E > +constexpr bool operator==( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return x.value() == y.value(); +} + +template< typename E > +constexpr bool operator!=( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return ! ( x == y ); +} + +#if nsel_P0323R <= 2 + +template< typename E > +constexpr bool operator<( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return x.value() < y.value(); +} + +template< typename E > +constexpr bool operator>( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return ( y < x ); +} + +template< typename E > +constexpr bool operator<=( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return ! ( y < x ); +} + +template< typename E > +constexpr bool operator>=( unexpected_type<E> const & x, unexpected_type<E> const & y ) +{ + return ! ( x < y ); +} + +/// x.x.5 Specialized algorithms + +template< typename E > +void swap( unexpected_type<E> & x, unexpected_type<E> & y) noexcept ( noexcept ( x.swap(y) ) ) +{ + x.swap( y ); +} + +// unexpected: relational operators for std::exception_ptr: + +inline constexpr bool operator<( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ ) +{ + return false; +} + +inline constexpr bool operator>( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ ) +{ + return false; +} + +inline constexpr bool operator<=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y ) +{ + return ( x == y ); +} + +inline constexpr bool operator>=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y ) +{ + return ( x == y ); +} + +#endif // nsel_P0323R + +// unexpected: traits + +#if nsel_P0323R <= 3 + +template <typename E> +struct is_unexpected : std::false_type {}; + +template <typename E> +struct is_unexpected< unexpected_type<E> > : std::true_type {}; + +#endif // nsel_P0323R + +// unexpected: factory + +// keep make_unexpected() removed in p0323r2 for pre-C++17: + +template< typename E> +nsel_constexpr14 auto +make_unexpected( E && v) -> unexpected_type< typename std::decay<E>::type > +{ + return unexpected_type< typename std::decay<E>::type >( v ); +} + +#if nsel_P0323R <= 3 + +/*nsel_constexpr14*/ auto inline +make_unexpected_from_current_exception() -> unexpected_type< std::exception_ptr > +{ + return unexpected_type< std::exception_ptr >( std::current_exception() ); +} + +#endif // nsel_P0323R + +/// in-place tag: construct a value in-place (should come from std::experimental::optional) + +struct in_place_t{}; + +nsel_inline17 constexpr in_place_t in_place{}; + +/// unexpect tag, in_place_unexpected tag: construct an error + +struct unexpect_t{}; +using in_place_unexpected_t = unexpect_t; + +nsel_inline17 constexpr unexpect_t unexpect{}; +nsel_inline17 constexpr unexpect_t in_place_unexpected{}; + +/// expected access error + +template< typename E > +class bad_expected_access; + +template <> +class bad_expected_access< void > : public std::exception +{ +public: + explicit bad_expected_access() + : std::exception() + {} +}; + +template< typename E > +class bad_expected_access : public bad_expected_access< void > +{ +public: + using error_type = E; + + explicit bad_expected_access( error_type error ) + : m_error( error ) + {} + + virtual char const * what() const noexcept override + { + return "bad_expected_access"; + } + + nsel_constexpr14 error_type & error() & + { + return m_error; + } + + constexpr error_type const & error() const & + { + return m_error; + } + + nsel_constexpr14 error_type && error() && + { + return std::move( m_error ); + } + + constexpr error_type const && error() const && + { + return std::move( m_error ); + } + +private: + error_type m_error; +}; + +/// class error_traits + +template< typename Error > +struct error_traits +{ + static void rethrow( Error const & e ) + { + throw bad_expected_access<Error>{ e }; + } +}; + +template<> +struct error_traits< std::exception_ptr > +{ + static void rethrow( std::exception_ptr const & e ) + { + std::rethrow_exception( e ); + } +}; + +template<> +struct error_traits< std::error_code > +{ + static void rethrow( std::error_code const & e ) + { + throw std::system_error( e ); + } +}; + +/// class expected + +#if nsel_P0323R <= 2 +template< typename T, typename E = std::exception_ptr > +class expected +#else +template< typename T, typename E > +class expected +#endif // nsel_P0323R +{ +public: + using value_type = T; + using error_type = E; + using unexpected_type = nonstd::unexpected_type<E>; + + template< typename U > + struct rebind + { + using type = expected<U, error_type>; + }; + + // x.x.4.1 constructors + + nsel_REQUIRES_0( + std::is_default_constructible<T>::value + ) + nsel_constexpr14 expected() noexcept + ( + std::is_nothrow_default_constructible<T>::value + ) + : has_value_( true ) + { + contained.construct_value( value_type() ); + } + + nsel_constexpr14 expected( expected const & rhs +// , nsel_REQUIRES( +// std::is_copy_constructible<T>::value +// && std::is_copy_constructible<E>::value ) + ) + : has_value_( rhs.has_value_ ) + { + if ( has_value() ) contained.construct_value( rhs.contained.value() ); + else contained.construct_error( rhs.contained.error() ); + } + + nsel_constexpr14 expected( expected && rhs +// , nsel_REQUIRES( +// std::is_move_constructible<T>::value +// && std::is_move_constructible<E>::value ) + ) noexcept ( + std::is_nothrow_move_constructible<T>::value + && std::is_nothrow_move_constructible<E>::value + ) + : has_value_( rhs.has_value_ ) + { + if ( has_value() ) contained.construct_value( std::move( rhs.contained.value() ) ); + else contained.construct_error( std::move( rhs.contained.error() ) ); + } + + template< typename U, typename G > + nsel_constexpr14 explicit expected( expected<U, G> const & rhs, nsel_REQUIRES( + std::is_constructible<T, const U&>::value + && std::is_constructible<E, const G&>::value + && !std::is_constructible<T, expected<U, G>&>::value + && !std::is_constructible<T, expected<U, G>&&>::value + && !std::is_constructible<T, const expected<U, G>&>::value + && !std::is_constructible<T, const expected<U, G>&&>::value + && !std::is_convertible<expected<U, G>&, T>::value + && !std::is_convertible<expected<U, G>&&, T>::value + && !std::is_convertible<const expected<U, G>&, T>::value + && !std::is_convertible<const expected<U, G>&&, T>::value + && (!std::is_convertible<U const&, T>::value || !std::is_convertible<const G&, E>::value ) /*=> explicit */ ) + ) + : has_value_( rhs.has_value_ ) + { + if ( has_value() ) contained.construct_value( rhs.contained.value() ); + else contained.construct_error( rhs.contained.error() ); + } + + template< typename U, typename G > + nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> const & rhs, nsel_REQUIRES( + std::is_constructible<T, const U&>::value + && std::is_constructible<E, const G&>::value + && !std::is_constructible<T, expected<U, G>&>::value + && !std::is_constructible<T, expected<U, G>&&>::value + && !std::is_constructible<T, const expected<U, G>&>::value + && !std::is_constructible<T, const expected<U, G>&&>::value + && !std::is_convertible<expected<U, G>&, T>::value + && !std::is_convertible<expected<U, G>&&, T>::value + && !std::is_convertible<const expected<U, G>&, T>::value + && !std::is_convertible<const expected<U, G>&&, T>::value + && !(!std::is_convertible<U const&, T>::value || !std::is_convertible<const G&, E>::value ) /*=> explicit */ ) + ) + : has_value_( rhs.has_value_ ) + { + if ( has_value() ) contained.construct_value( rhs.contained.value() ); + else contained.construct_error( rhs.contained.error() ); + } + + template< typename U, typename G > + nsel_constexpr14 explicit expected( expected<U, G> && rhs, nsel_REQUIRES( + std::is_constructible<T, U>::value + && std::is_constructible<E, G>::value + && !std::is_constructible<T, expected<U, G>&>::value + && !std::is_constructible<T, expected<U, G>&&>::value + && !std::is_constructible<T, const expected<U, G>&>::value + && !std::is_constructible<T, const expected<U, G>&&>::value + && !std::is_convertible<expected<U, G>&, T>::value + && !std::is_convertible<expected<U, G>&&, T>::value + && !std::is_convertible<const expected<U, G>&, T>::value + && !std::is_convertible<const expected<U, G>&&, T>::value + && (!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> explicit */ ) + ) + : has_value_( rhs.has_value_ ) + { + if ( has_value() ) contained.construct_value( std::move( rhs.contained.value() ) ); + else contained.construct_error( std::move( rhs.contained.error() ) ); + } + + template< typename U, typename G > + nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> && rhs, nsel_REQUIRES( + std::is_constructible<T, U>::value + && std::is_constructible<E, G>::value + && !std::is_constructible<T, expected<U, G>&>::value + && !std::is_constructible<T, expected<U, G>&&>::value + && !std::is_constructible<T, const expected<U, G>&>::value + && !std::is_constructible<T, const expected<U, G>&&>::value + && !std::is_convertible<expected<U, G>&, T>::value + && !std::is_convertible<expected<U, G>&&, T>::value + && !std::is_convertible<const expected<U, G>&, T>::value + && !std::is_convertible<const expected<U, G>&&, T>::value + && !(!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> non-explicit */ ) + ) + : has_value_( rhs.has_value_ ) + { + if ( has_value() ) contained.construct_value( std::move( rhs.contained.value() ) ); + else contained.construct_error( std::move( rhs.contained.error() ) ); + } + + nsel_constexpr14 expected( value_type const & v +// , nsel_REQUIRES( +// std::is_copy_constructible<T>::value ) + ) + : has_value_( true ) + { + contained.construct_value( v ); + } + + template< typename U = T > + nsel_constexpr14 explicit expected( U && v, nsel_REQUIRES( + std::is_constructible<T,U&&>::value + && !std::is_same<typename std20::remove_cvref<U>::type, in_place_t>::value + && !std::is_same<expected<T,E>, typename std20::remove_cvref<U>::type>::value + && !std::is_same<nonstd::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value + && !std::is_convertible<U&&,T>::value /*=> explicit */ +) + ) noexcept + ( + std::is_nothrow_move_constructible<U>::value && + std::is_nothrow_move_constructible<E>::value + ) + : has_value_( true ) + { + contained.construct_value( std::forward<U>( v ) ); + } + + template< typename U = T > + nsel_constexpr14 expected( U && v, nsel_REQUIRES( + std::is_constructible<T,U&&>::value + && !std::is_same<typename std20::remove_cvref<U>::type, in_place_t>::value + && !std::is_same<expected<T,E>, typename std20::remove_cvref<U>::type>::value + && !std::is_same<nonstd::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value + && std::is_convertible<U&&,T>::value /*=> non-explicit */ +) + ) noexcept + ( + std::is_nothrow_move_constructible<U>::value && + std::is_nothrow_move_constructible<E>::value + ) + : has_value_( true ) + { + contained.construct_value( std::forward<U>( v ) ); + } + + template <typename... Args, nsel_REQUIRES_T( + std::is_constructible<T, Args&&...>::value ) > + + nsel_constexpr14 explicit expected( in_place_t, Args&&... args ) + : has_value_( true ) + { + contained.construct_value( std::forward<Args>( args )... ); + } + + template< typename U, typename... Args, nsel_REQUIRES_T( + std::is_constructible<T, std::initializer_list<U>, Args&&...>::value ) > + + nsel_constexpr14 explicit expected( in_place_t, std::initializer_list<U> il, Args&&... args ) + : has_value_( true ) + { + contained.construct_value( il, std::forward<Args>( args )... ); + } + + template< typename G = E > + nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> const & error, nsel_REQUIRES( + !std::is_convertible<const G&, E>::value /*=> explicit */ ) + ) + : has_value_( false ) + { + contained.construct_error( error.value() ); + } + + template< typename G = E > + nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> const & error, nsel_REQUIRES( + std::is_convertible<const G&, E>::value /*=> non-explicit */ ) + ) + : has_value_( false ) + { + contained.construct_error( error.value() ); + } + + template< typename G = E > + nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> && error, nsel_REQUIRES( + !std::is_convertible<G&&, E>::value /*=> explicit */ ) + ) + : has_value_( false ) + { + contained.construct_error( std::move( error.value() ) ); + } + + template< typename G = E > + nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> && error, nsel_REQUIRES( + std::is_convertible<G&&, E>::value /*=> non-explicit */ ) + ) + : has_value_( false ) + { + contained.construct_error( std::move( error.value() ) ); + } + + template< typename... Args, nsel_REQUIRES_T( + std::is_constructible<E, Args&&...>::value ) + > + nsel_constexpr14 explicit expected( unexpect_t, Args&&... args ) + : has_value_( false ) + { + contained.construct_error( std::forward<Args>( args )... ); + } + + template< typename U, typename... Args, nsel_REQUIRES_T( + std::is_constructible<T, std::initializer_list<U>, Args&&...>::value ) + > + nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args ) + : has_value_( false ) + { + contained.construct_error( il, std::forward<Args>( args )... ); + } + + // x.x.4.2 destructor + + ~expected() + { + if ( has_value() ) contained.destruct_value(); + else contained.destruct_error(); + } + + // x.x.4.3 assignment + +// nsel_REQUIRES( +// std::is_copy_constructible<T>::value && +// std::is_copy_assignable<T>::value && +// std::is_copy_constructible<E>::value && +// std::is_copy_assignable<E>::value ) + + expected operator=( expected const & rhs ) + { + expected( rhs ).swap( *this ); + return *this; + } + +// nsel_REQUIRES( +// std::is_move_constructible<T>::value && +// std::is_move_assignable<T>::value && +// std::is_move_constructible<E>::value && +// std::is_move_assignable<E>::value ) + + expected & operator=( expected && rhs ) noexcept + ( + std::is_nothrow_move_assignable<T>::value && + std::is_nothrow_move_constructible<T>::value&& + std::is_nothrow_move_assignable<E>::value && + std::is_nothrow_move_constructible<E>::value ) + { + expected( std::move( rhs ) ).swap( *this ); + return *this; + } + + template< typename U, nsel_REQUIRES_T( + std::is_constructible<T,U>::value && + std::is_assignable<T&, U>::value ) > + + expected & operator=( U && v ) + { + expected( std::forward<U>( v ) ).swap( *this ); + return *this; + } + +// nsel_REQUIRES( +// std::is_copy_constructible<E>::value && +// std::is_assignable<E&, E>::value ) + + expected & operator=( unexpected_type const & u ) + { + expected( std::move( u ) ).swap( *this ); + return *this; + } + +// nsel_REQUIRES( +// std::is_copy_constructible<E>::value && +// std::is_assignable<E&, E>::value ) + + expected & operator=( unexpected_type && u ) + { + expected( std::move( u ) ).swap( *this ); + return *this; + } + + template< typename... Args, nsel_REQUIRES_T( + std::is_constructible<T, Args&&...>::value ) > + + void emplace( Args &&... args ) + { + expected( in_place, std::forward<Args>(args)... ).swap( *this ); + } + + template< typename U, typename... Args, nsel_REQUIRES_T( + std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value ) > + + void emplace( std::initializer_list<U> il, Args &&... args ) + { + expected( in_place, il, std::forward<Args>(args)... ).swap( *this ); + } + + // x.x.4.4 swap + +// nsel_REQUIRES( +// std::is_move_constructible<T>::value && +// std::is_move_constructible<E>::value ) + + void swap( expected & rhs ) noexcept + ( +#if nsel_CPP17_OR_GREATER + std::is_nothrow_move_constructible<T>::value && std::is_nothrow_swappable<T&>::value && + std::is_nothrow_move_constructible<E>::value && std::is_nothrow_swappable<E&>::value +#else + std::is_nothrow_move_constructible<T>::value && noexcept ( std::swap( std::declval<T&>(), std::declval<T&>() ) ) && + std::is_nothrow_move_constructible<E>::value && noexcept ( std::swap( std::declval<E&>(), std::declval<E&>() ) ) +#endif + ) + { + using std::swap; + + if ( bool(*this) && bool(rhs) ) { swap( contained.value(), rhs.contained.value() ); } + else if ( ! bool(*this) && ! bool(rhs) ) { swap( contained.error(), rhs.contained.error() ); } + else if ( bool(*this) && ! bool(rhs) ) { error_type t( std::move( rhs.error() ) ); + rhs.contained.destruct_error(); + rhs.contained.construct_value( std::move( contained.value() ) ); + contained.destruct_value(); + contained.construct_error( std::move( t ) ); + swap( has_value_, rhs.has_value_ ); } + else if ( ! bool(*this) && bool(rhs) ) { rhs.swap( *this ); } + } + + // x.x.4.5 observers + + constexpr value_type const * operator ->() const + { + return assert( has_value() ), contained.value_ptr(); + } + + value_type * operator ->() + { + return assert( has_value() ), contained.value_ptr(); + } + + constexpr value_type const & operator *() const & + { + return assert( has_value() ), contained.value(); + } + + value_type & operator *() & + { + return assert( has_value() ), contained.value(); + } + + constexpr value_type const && operator *() const && + { + return assert( has_value() ), std::move( contained.value() ); + } + + nsel_constexpr14 value_type && operator *() && + { + return assert( has_value() ), std::move( contained.value() ); + } + + constexpr explicit operator bool() const noexcept + { + return has_value(); + } + + constexpr bool has_value() const noexcept + { + return has_value_; + } + + constexpr value_type const & value() const & + { + return has_value() + ? ( contained.value() ) + : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ); + } + + value_type & value() & + { + return has_value() + ? ( contained.value() ) + : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ); + } + + constexpr value_type const && value() const && + { + return std::move( has_value() + ? ( contained.value() ) + : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) ); + } + + nsel_constexpr14 value_type && value() && + { + return std::move( has_value() + ? ( contained.value() ) + : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) ); + } + + constexpr error_type const & error() const & + { + return assert( ! has_value() ), contained.error(); + } + + error_type & error() & + { + return assert( ! has_value() ), contained.error(); + } + + constexpr error_type const && error() const && + { + return assert( ! has_value() ), std::move( contained.error() ); + } + + error_type && error() && + { + return assert( ! has_value() ), std::move( contained.error() ); + } + + constexpr unexpected_type get_unexpected() const + { + return make_unexpected( contained.error() ); + } + + template< typename Ex > + bool has_exception() const + { + using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type; + return ! has_value() && std::is_base_of< Ex, ContainedEx>::value; + } + + template< typename U, nsel_REQUIRES_T( + std::is_copy_constructible<T>::value && + std::is_convertible<U&&, T>::value ) > + + value_type value_or( U && v ) const & + { + return has_value() + ? contained.value() + : static_cast<T>( std::forward<U>( v ) ); + } + + template< typename U, nsel_REQUIRES_T( + std::is_move_constructible<T>::value && + std::is_convertible<U&&, T>::value ) > + + value_type value_or( U && v ) && + { + return has_value() + ? std::move( contained.value() ) + : static_cast<T>( std::forward<U>( v ) ); + } + + // unwrap() + +// template <class U, class E> +// constexpr expected<U,E> expected<expected<U,E>,E>::unwrap() const&; + +// template <class T, class E> +// constexpr expected<T,E> expected<T,E>::unwrap() const&; + +// template <class U, class E> +// expected<U,E> expected<expected<U,E>, E>::unwrap() &&; + +// template <class T, class E> +// template expected<T,E> expected<T,E>::unwrap() &&; + + // factories + +// template <typename Ex, typename F> +// expected<T,E> catch_exception(F&& f); + +// template <typename F> +// expected<decltype(func(declval<T>())),E> map(F&& func) ; + +// template <typename F> +// 'see below' bind(F&& func); + +// template <typename F> +// expected<T,E> catch_error(F&& f); + +// template <typename F> +// 'see below' then(F&& func); + +private: + bool has_value_; + expected_detail::storage_t<T,E> contained; +}; + +/// class expected, void specialization + +template< typename E > +class expected<void, E> +{ +public: + using value_type = void; + using error_type = E; + using unexpected_type = nonstd::unexpected_type<E>; + + // x.x.4.1 constructors + + constexpr expected() noexcept + : has_value_( true ) + { + } + + nsel_constexpr14 expected( expected const & rhs ) + : has_value_( rhs.has_value_ ) + { + if ( ! has_value() ) contained.construct_error( rhs.contained.error() ); + } + + nsel_REQUIRES_0( + std::is_move_constructible<E>::value ) + + nsel_constexpr14 expected( expected && rhs ) noexcept + ( + true // TBD - see also non-void specialization + ) + : has_value_( rhs.has_value_ ) + { + if ( ! has_value() ) contained.construct_error( std::move( rhs.contained.error() ) ); + } + + constexpr explicit expected( in_place_t ) + : has_value_( true ) + { + } + + template< typename G = E > + nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> const & error, nsel_REQUIRES( + !std::is_convertible<const G&, E>::value /*=> explicit */ ) + ) + : has_value_( false ) + { + contained.construct_error( error.value() ); + } + + template< typename G = E > + nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> const & error, nsel_REQUIRES( + std::is_convertible<const G&, E>::value /*=> non-explicit */ ) + ) + : has_value_( false ) + { + contained.construct_error( error.value() ); + } + + template< typename G = E > + nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> && error, nsel_REQUIRES( + !std::is_convertible<G&&, E>::value /*=> explicit */ ) + ) + : has_value_( false ) + { + contained.construct_error( std::move( error.value() ) ); + } + + template< typename G = E > + nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> && error, nsel_REQUIRES( + std::is_convertible<G&&, E>::value /*=> non-explicit */ ) + ) + : has_value_( false ) + { + contained.construct_error( std::move( error.value() ) ); + } + + template< typename... Args, nsel_REQUIRES_T( + std::is_constructible<E, Args&&...>::value ) + > + nsel_constexpr14 explicit expected( unexpect_t, Args&&... args ) + : has_value_( false ) + { + contained.construct_error( std::forward<Args>( args )... ); + } + + template< typename U, typename... Args, nsel_REQUIRES_T( + std::is_constructible<U, std::initializer_list<U>, Args&&...>::value ) + > + nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args ) + : has_value_( false ) + { + contained.construct_error( il, std::forward<Args>( args )... ); + } + + + // destructor + + ~expected() + { + if ( ! has_value() ) contained.destruct_error(); + } + + // x.x.4.3 assignment + +// nsel_REQUIRES( +// std::is_copy_constructible<E>::value && +// std::is_copy_assignable<E>::value ) + + expected & operator=( expected const & rhs ) + { + expected( rhs ).swap( *this ); + return *this; + } + +// nsel_REQUIRES( +// std::is_move_constructible<E>::value && +// std::is_move_assignable<E>::value ) + + expected & operator=( expected && rhs ) noexcept + ( + std::is_nothrow_move_assignable<E>::value && + std::is_nothrow_move_constructible<E>::value ) + { + expected( std::move( rhs ) ).swap( *this ); + return *this; + } + + void emplace() + {} + + // x.x.4.4 swap + +// nsel_REQUIRES( +// std::is_move_constructible<E>::value ) + + void swap( expected & rhs ) noexcept + ( +#if nsel_CPP17_OR_GREATER + std::is_nothrow_move_constructible<E>::value && std::is_nothrow_swappable<E&>::value +#else + std::is_nothrow_move_constructible<E>::value && noexcept ( std::swap( std::declval<E&>(), std::declval<E&>() ) ) +#endif + ) + { + using std::swap; + + if ( ! bool(*this) && ! bool(rhs) ) { swap( contained.error(), rhs.contained.error() ); } + else if ( bool(*this) && ! bool(rhs) ) { contained.construct_error( std::move( rhs.error() ) ); + swap( has_value_, rhs.has_value_ ); } + else if ( ! bool(*this) && bool(rhs) ) { rhs.swap( *this ); } + } + + // x.x.4.5 observers + + constexpr explicit operator bool() const noexcept + { + return has_value(); + } + + constexpr bool has_value() const noexcept + { + return has_value_; + } + + void value() const + {} + + constexpr error_type const & error() const & + { + return assert( ! has_value() ), contained.error(); + } + + error_type & error() & + { + return assert( ! has_value() ), contained.error(); + } + + constexpr error_type const && error() const && + { + return assert( ! has_value() ), std::move( contained.error() ); + } + + error_type && error() && + { + return assert( ! has_value() ), std::move( contained.error() ); + } + + constexpr unexpected_type get_unexpected() const + { + return make_unexpected( contained.error() ); + } + + template <typename Ex> + bool has_exception() const + { + return ! has_value() && std::is_base_of< Ex, decltype( get_unexpected().value() ) >::value; + } + +// template constexpr 'see below' unwrap() const&; +// +// template 'see below' unwrap() &&; + + // factories + +// template <typename Ex, typename F> +// expected<void,E> catch_exception(F&& f); +// +// template <typename F> +// expected<decltype(func()), E> map(F&& func) ; +// +// template <typename F> +// 'see below' bind(F&& func) ; +// +// template <typename F> +// expected<void,E> catch_error(F&& f); +// +// template <typename F> +// 'see below' then(F&& func); + +private: + bool has_value_; + expected_detail::storage_t<void,E> contained; +}; + +// expected: relational operators + +template <typename T, typename E> +constexpr bool operator==( expected<T,E> const & x, expected<T,E> const & y ) +{ + return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; +} + +template <typename T, typename E> +constexpr bool operator!=( expected<T,E> const & x, expected<T,E> const & y ) +{ + return !(x == y); +} + +template <typename T, typename E> +constexpr bool operator<( expected<T,E> const & x, expected<T,E> const & y ) +{ + return (!y) ? false : (!x) ? true : *x < *y; +} + +template <typename T, typename E> +constexpr bool operator>( expected<T,E> const & x, expected<T,E> const & y ) +{ + return (y < x); +} + +template <typename T, typename E> +constexpr bool operator<=( expected<T,E> const & x, expected<T,E> const & y ) +{ + return !(y < x); +} + +template <typename T, typename E> +constexpr bool operator>=( expected<T,E> const & x, expected<T,E> const & y ) +{ + return !(x < y); +} + +// expected: comparison with unexpected_type + +template <typename T, typename E> +constexpr bool operator==( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return (!x) ? x.get_unexpected() == u : false; +} + +template <typename T, typename E> +constexpr bool operator==( unexpected_type<E> const & u, expected<T,E> const & x ) +{ + return ( x == u ); +} + +template <typename T, typename E> +constexpr bool operator!=( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return ! ( x == u ); +} + +template <typename T, typename E> +constexpr bool operator!=( unexpected_type<E> const & u, expected<T,E> const & x ) +{ + return ! ( x == u ); +} + +template <typename T, typename E> +constexpr bool operator<( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return (!x) ? ( x.get_unexpected() < u ) : false; +} + +#if nsel_P0323R <= 2 + +template <typename T, typename E> +constexpr bool operator<( unexpected_type<E> const & u, expected<T,E> const & x ) +{ + return (!x) ? ( u < x.get_unexpected() ) : true ; +} + +template <typename T, typename E> +constexpr bool operator>( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return ( u < x ); +} + +template <typename T, typename E> +constexpr bool operator>( unexpected_type<E> const & u, expected<T,E> const & x ) +{ + return ( x < u ); +} + +template <typename T, typename E> +constexpr bool operator<=( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return ! ( u < x ); +} + +template <typename T, typename E> +constexpr bool operator<=( unexpected_type<E> const & u, expected<T,E> const & x) +{ + return ! ( x < u ); +} + +template <typename T, typename E> +constexpr bool operator>=( expected<T,E> const & x, unexpected_type<E> const & u ) +{ + return ! ( u > x ); +} + +template <typename T, typename E> +constexpr bool operator>=( unexpected_type<E> const & u, expected<T,E> const & x ) +{ + return ! ( x > u ); +} + +#endif // nsel_P0323R + +// expected: comparison with T + +template <typename T, typename E> +constexpr bool operator==( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? *x == v : false; +} + +template <typename T, typename E> +constexpr bool operator==(T const & v, expected<T,E> const & x ) +{ + return bool(x) ? v == *x : false; +} + +template <typename T, typename E> +constexpr bool operator!=( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? *x != v : true; +} + +template <typename T, typename E> +constexpr bool operator!=( T const & v, expected<T,E> const & x ) +{ + return bool(x) ? v != *x : true; +} + +template <typename T, typename E> +constexpr bool operator<( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? *x < v : true; +} + +template <typename T, typename E> +constexpr bool operator<( T const & v, expected<T,E> const & x ) +{ + return bool(x) ? v < *x : false; +} + +template <typename T, typename E> +constexpr bool operator>( T const & v, expected<T,E> const & x ) +{ + return bool(x) ? *x < v : false; +} + +template <typename T, typename E> +constexpr bool operator>( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? v < *x : false; +} + +template <typename T, typename E> +constexpr bool operator<=( T const & v, expected<T,E> const & x ) +{ + return bool(x) ? ! ( *x < v ) : false; +} + +template <typename T, typename E> +constexpr bool operator<=( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? ! ( v < *x ) : true; +} + +template <typename T, typename E> +constexpr bool operator>=( expected<T,E> const & x, T const & v ) +{ + return bool(x) ? ! ( *x < v ) : false; +} + +template <typename T, typename E> +constexpr bool operator>=( T const & v, expected<T,E> const & x ) +{ + return bool(x) ? ! ( v < *x ) : true; +} + +/// x.x.x Specialized algorithms + +template< typename T, typename E > +void swap( expected<T,E> & x, expected<T,E> & y ) noexcept ( noexcept ( x.swap(y) ) ) +{ + x.swap( y ); +} + +#if nsel_P0323R <= 3 + +template< typename T> +constexpr auto make_expected( T && v ) -> expected< typename std::decay<T>::type > +{ + return expected< typename std::decay<T>::type >( std::forward<T>( v ) ); +} + +// expected<void> specialization: + +auto inline make_expected() -> expected<void> +{ + return expected<void>( in_place ); +} + +template< typename T> +constexpr auto make_expected_from_current_exception() -> expected<T> +{ + return expected<T>( make_unexpected_from_current_exception() ); +} + +template< typename T> +auto make_expected_from_exception( std::exception_ptr v ) -> expected<T> +{ + return expected<T>( unexpected_type<std::exception_ptr>( std::forward<std::exception_ptr>( v ) ) ); +} + +template< typename T, typename E > +constexpr auto make_expected_from_error( E e ) -> expected<T, typename std::decay<E>::type> +{ + return expected<T, typename std::decay<E>::type>( make_unexpected( e ) ); +} + +template< typename F > +/*nsel_constexpr14*/ +auto make_expected_from_call( F f, + nsel_REQUIRES( ! std::is_same<typename std::result_of<F()>::type, void>::value ) +) -> expected< typename std::result_of<F()>::type > +{ + try + { + return make_expected( f() ); + } + catch (...) + { + return make_unexpected_from_current_exception(); + } +} + +template< typename F > +/*nsel_constexpr14*/ +auto make_expected_from_call( F f, + nsel_REQUIRES( std::is_same<typename std::result_of<F()>::type, void>::value ) +) -> expected<void> +{ + try + { + f(); + return make_expected(); + } + catch (...) + { + return make_unexpected_from_current_exception(); + } +} + +#endif // nsel_P0323R + +} // namespace nonstd + +namespace std { + +// expected: hash support + +template< typename T, typename E > +struct hash< nonstd::expected<T,E> > +{ + using result_type = typename hash<T>::result_type; + using argument_type = nonstd::expected<T,E>; + + constexpr result_type operator()(argument_type const & arg) const + { + return arg ? std::hash<T>{}(*arg) : result_type{}; + } +}; + +// TBD - ?? remove? see spec. +template< typename T, typename E > +struct hash< nonstd::expected<T&,E> > +{ + using result_type = typename hash<T>::result_type; + using argument_type = nonstd::expected<T&,E>; + + constexpr result_type operator()(argument_type const & arg) const + { + return arg ? std::hash<T>{}(*arg) : result_type{}; + } +}; + +// TBD - implement +// bool(e), hash<expected<void,E>>()(e) shall evaluate to the hashing true; +// otherwise it evaluates to an unspecified value if E is exception_ptr or +// a combination of hashing false and hash<E>()(e.error()). + +template< typename E > +struct hash< nonstd::expected<void,E> > +{ +}; + +} // namespace std + + +namespace nonstd { + +// void unexpected() is deprecated && removed in C++17 + +#if nsel_CPP17_OR_GREATER && nsel_COMPILER_MSVC_VERSION > 141 +template< typename E > +using unexpected = unexpected_type<E>; +#endif + +} // namespace nonstd + +#undef nsel_REQUIRES +#undef nsel_REQUIRES_0 +#undef nsel_REQUIRES_T + +nsel_RESTORE_WARNINGS() + +#endif // NONSTD_EXPECTED_LITE_HPP diff --git a/vendor/expected/version.txt b/vendor/expected/version.txt new file mode 100644 index 0000000000..52e4107440 --- /dev/null +++ b/vendor/expected/version.txt @@ -0,0 +1 @@ +a2359cf61478d735a308e952b1c9840714f61386 |