diff options
author | Randall Lee <randall.lee@mapbox.com> | 2018-05-22 14:09:36 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-22 14:09:36 -0400 |
commit | d858cb783b499a1cc77b48a0faee137ca5e6a423 (patch) | |
tree | 5c36c353730e3050d44e472545519c0429723266 | |
parent | f93d722458be62d567aa152711a014ef51a90193 (diff) | |
parent | 60505b03174b5ec02ae723beafa7683f6ed54a62 (diff) | |
download | qtlocation-mapboxgl-upstream/rclee-async-setup.tar.gz |
Merge branch 'master' into rclee-async-setupupstream/rclee-async-setup
398 files changed, 7333 insertions, 5427 deletions
diff --git a/.gitignore b/.gitignore index 0f4fa77858..b32baaac09 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ xcuserdata /test/fixtures/api/2.png /test/fixtures/offline_database/offline.db /test/fixtures/offline_database/offline.db-* +/test/fixtures/offline_database/satellite.db +/test/fixtures/offline_database/satellite.db-* /test/fixtures/offline_database/invalid.db /test/fixtures/offline_database/invalid.db-* /test/fixtures/offline_database/migrated.db @@ -38,4 +40,4 @@ test/fixtures/storage/assets.zip # Generated list files from code generation /scripts/generate-cmake-files.list /scripts/generate-shaders.list -/scripts/generate-style-code.list
\ No newline at end of file +/scripts/generate-style-code.list diff --git a/.tx/config b/.tx/config index 1de8f78303..272fe45b16 100644 --- a/.tx/config +++ b/.tx/config @@ -46,6 +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 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 8307191a3f..46aea73356 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,13 +15,9 @@ include(cmake/xcode.cmake) if(WITH_CXX11ABI) set(MASON_CXXABI_SUFFIX -cxx11abi) - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1) - endif() + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1) else() - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) - endif() + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) endif() if(WITH_OSMESA AND WITH_EGL) diff --git a/INSTALL.md b/INSTALL.md index a28348d15c..463d199d10 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,56 +1,15 @@ -# Building & Developing Mapbox GL Native from Source +# Building & Developing from Source -**Just trying to use Mapbox GL Native? You don't need to read this stuff! We +**Just trying to use one of the Mapbox Maps SDKs? You don't need to read this stuff! We provide [easy-to-install, prebuilt versions of the Mapbox Maps SDKs for iOS and Android that you can download instantly and get started with fast](https://www.mapbox.com/install/).** -Still with us? These are the instructions you'll need to build Mapbox GL Native -from source on a variety of platforms and set up a development environment. +If you're certain you need to build from source, rather than using a prebuilt SDK, please +refer to the installation instructions for the platform of interest: -Your journey will start with installing dependencies, then getting the source code, and -then setting up a development environment, which varies depending on your -operating system and what platform you want to develop for. - -## 1: Installing dependencies - -### macOS - - 1. Install [Xcode](https://developer.apple.com/xcode/) - 2. Launch Xcode and install any updates - 3. Install [Homebrew](http://brew.sh) - 4. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org) with `brew install nodejs cmake ccache` - 5. Install [xcpretty](https://github.com/supermarin/xcpretty) with `[sudo] gem install xcpretty` (optional, used for prettifying command line builds) - -### Linux - -Install the following: - - - `clang++` 3.5 or later or `g++` 4.9 or later - - [git](https://git-scm.com/) - - [CMake](https://cmake.org/) 3.1 or later - - [cURL](https://curl.haxx.se) - - [Node.js](https://nodejs.org/) 4.2.1 or later - - [`libcurl`](http://curl.haxx.se/libcurl/) (depends on OpenSSL) - - [ccache](https://ccache.samba.org) (optional, improves recompilation performance) - -## 2: Getting the source - - Clone the git repository: - - git clone https://github.com/mapbox/mapbox-gl-native.git - cd mapbox-gl-native - -## 3: Setting up a development environment & building - -See the relevant SDK documentation for next steps: - -* [Maps SDK for Android](platform/android/) -* [Maps SDK for iOS](platform/ios/) -* [Maps SDK for macOS](platform/macos/) -* [Mapbox Qt SDK](platform/qt/) -* [Mapbox GL Native on Linux](platform/linux/) -* [node-mapbox-gl-native](platform/node/) - -## 4: Keeping up to date - -This repository uses Git submodules, which should be automatically checked out when you first run a `make` command for one of the above platforms. These submodules are not updated automatically and we recommended that you run `git submodule update` after pulling down new commits to this repository. +* [Mapbox Maps SDK for Android](platform/android/README.md) +* [Mapbox Maps SDK for iOS](platform/ios/INSTALL.md) +* [Mapbox Maps SDK for macOS](platform/macos/INSTALL.md) +* [Mapbox Maps SDK for Qt](platform/qt/README.md) +* [Mapbox GL Native on Linux](platform/linux/README.md) +* [node-mapbox-gl-native](platform/node/DEVELOPING.md) @@ -111,8 +111,7 @@ run-benchmark-%: benchmark $(MACOS_OUTPUT_PATH)/$(BUILDTYPE)/mbgl-benchmark --benchmark_filter=$* ${BENCHMARK_ARGS} .PHONY: node-benchmark -node-benchmark: $(MACOS_PROJ_PATH) - set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'node-benchmark' build $(XCPRETTY) +node-benchmark: node .PHONY: run-node-benchmark run-node-benchmark: node-benchmark @@ -136,7 +135,11 @@ offline: $(MACOS_PROJ_PATH) .PHONY: node node: $(MACOS_PROJ_PATH) - set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'mbgl-node' build $(XCPRETTY) + set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'mbgl-node (Active ABI)' build $(XCPRETTY) + +.PHONY: node-all +node-all: $(MACOS_PROJ_PATH) + set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'mbgl-node (All ABIs)' build $(XCPRETTY) .PHONY: macos-test macos-test: $(MACOS_PROJ_PATH) @@ -362,7 +365,11 @@ run-glfw-app: glfw-app .PHONY: node node: $(LINUX_BUILD) - $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-node + $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-node.active + +.PHONY: node-all +node-all: $(LINUX_BUILD) + $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-node.all .PHONY: compdb compdb: $(LINUX_BUILD) @@ -473,12 +480,10 @@ test-node-recycle-map: node #### Android targets ########################################################### -MBGL_ANDROID_ABIS = arm-v5;armeabi -MBGL_ANDROID_ABIS += arm-v7;armeabi-v7a +MBGL_ANDROID_ABIS = arm-v7;armeabi-v7a MBGL_ANDROID_ABIS += arm-v8;arm64-v8a MBGL_ANDROID_ABIS += x86;x86 MBGL_ANDROID_ABIS += x86-64;x86_64 -MBGL_ANDROID_ABIS += mips;mips MBGL_ANDROID_LOCAL_WORK_DIR = /data/local/tmp/core-tests MBGL_ANDROID_LIBDIR = lib$(if $(filter arm-v8 x86-64,$1),64) @@ -619,7 +624,7 @@ run-android-unit-test-%: platform/android/gradle/configuration.gradle # Builds a release package of the Android SDK .PHONY: apackage apackage: platform/android/gradle/configuration.gradle - make android-lib-arm-v5 && make android-lib-arm-v7 && make android-lib-arm-v8 && make android-lib-x86 && make android-lib-x86-64 && make android-lib-mips + make android-lib-arm-v7 && make android-lib-arm-v8 && make android-lib-x86 && make android-lib-x86-64 cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all assemble$(BUILDTYPE) # Build test app instrumentation tests apk and test app apk for all abi's @@ -687,6 +692,11 @@ endif android-configuration: platform/android/gradle/configuration.gradle cat platform/android/gradle/configuration.gradle +# Creates a dependency graph using Graphviz +.PHONY: android-graph +android-graph: + cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:generateDependencyGraphMapboxLibraries + #### Miscellaneous targets ##################################################### .PHONY: style-code @@ -701,6 +711,7 @@ codestyle: .PHONY: clean clean: -rm -rf ./build \ + ./lib/*.node \ ./platform/android/gradle/configuration.gradle \ ./platform/android/MapboxGLAndroidSDK/build \ ./platform/android/MapboxGLAndroidSDK/.externalNativeBuild \ @@ -13,15 +13,15 @@ This repository hosts the cross-platform Mapbox GL Native library, plus convenie | [Mapbox Maps SDK for iOS](platform/ios/) | Objective-C or Swift | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) | | [Mapbox Maps SDK for macOS](platform/macos/) | Objective-C, Swift, or AppleScript | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) | | [node-mapbox-gl-native](platform/node/) | Node.js | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) | -| [Mapbox Qt SDK](platform/qt) | C++03 | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) | +| [Mapbox Maps SDK for Qt](platform/qt) | C++03 | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) | Additional Mapbox GL Native–based libraries for **hybrid applications** are developed outside of this repository: | Toolkit | Android | iOS | Developer | | ---------------------------------------- | --------|-----|------------ | -| [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | Mapbox | +| [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/@mapbox/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | Mapbox | | [Apache Cordova](http://plugins.telerik.com/cordova/plugin/mapbox/) ([npm](https://www.npmjs.com/package/cordova-plugin-mapbox)) | :white_check_mark: | :white_check_mark: | Telerik | -| [NativeScript](http://plugins.telerik.com/nativescript/plugin/mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik | +| [NativeScript](https://market.nativescript.org/plugins/nativescript-mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik | | [Xamarin](https://components.xamarin.com/view/mapboxsdk/) | :white_check_mark: | :white_check_mark: | Xamarin | If your platform or hybrid application framework isn’t listed here, consider embedding [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) using the standard Web capabilities on your platform. diff --git a/appveyor.yml b/appveyor.yml index d31160261f..eb53ad58bc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,7 +36,7 @@ before_build: build_script: - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 - - cmake -G "Ninja" -DMBGL_PLATFORM=qt -DWITH_QT_DECODERS=ON -DWITH_QT_I18N=ON -DWITH_NODEJS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=C:\Qt\5.7\msvc2015_64\lib\cmake -DCMAKE_MAKE_PROGRAM="%APPVEYOR_BUILD_FOLDER%\platform\qt\ninja.exe" .. + - cmake -G "Ninja" -DMBGL_PLATFORM=qt -DWITH_QT_DECODERS=ON -DWITH_QT_I18N=ON -DWITH_NODEJS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=C:\Qt\5.6.3\msvc2015_64\lib\cmake -DCMAKE_MAKE_PROGRAM="%APPVEYOR_BUILD_FOLDER%\platform\qt\ninja.exe" .. - cmake --build . -- -j %NUMBER_OF_PROCESSORS% after_build: diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp index e4cf635256..4e39237138 100644 --- a/benchmark/parse/filter.benchmark.cpp +++ b/benchmark/parse/filter.benchmark.cpp @@ -1,7 +1,6 @@ #include <benchmark/benchmark.h> #include <mbgl/style/filter.hpp> -#include <mbgl/style/filter_evaluator.hpp> #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/filter.hpp> diff --git a/benchmark/util/tilecover.benchmark.cpp b/benchmark/util/tilecover.benchmark.cpp new file mode 100644 index 0000000000..186de6f216 --- /dev/null +++ b/benchmark/util/tilecover.benchmark.cpp @@ -0,0 +1,96 @@ +#include <benchmark/benchmark.h> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/geometry.hpp> +#include <mbgl/util/tile_coordinate.hpp> +#include <mbgl/util/tile_cover.hpp> +#include <mbgl/map/transform.hpp> + +using namespace mbgl; + +static const LatLngBounds sanFrancisco = + LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); + +static void TileCountBounds(benchmark::State& state) { + std::size_t length = 0; + while (state.KeepRunning()) { + auto count = util::tileCount(sanFrancisco, 10); + length += count; + } +} + +static void TileCoverPitchedViewport(benchmark::State& state) { + Transform transform; + transform.resize({ 512, 512 }); + // slightly offset center so that tile order is better defined + transform.setLatLng({ 0.1, -0.1 }); + transform.setZoom(8); + transform.setAngle(5.0); + transform.setPitch(40.0 * M_PI / 180.0); + + std::size_t length = 0; + while (state.KeepRunning()) { + auto tiles = util::tileCover(transform.getState(), 8); + length += tiles.size(); + } +} + +static void TileCoverBounds(benchmark::State& state) { + std::size_t length = 0; + while (state.KeepRunning()) { + auto tiles = util::tileCover(sanFrancisco, 8); + length += tiles.size(); + } +} + +static const auto geomPolygon = Polygon<double>{ + { + {-122.5143814086914,37.779127216982424}, + {-122.50811576843262,37.72721239056709}, + {-122.50313758850099,37.70820178063929}, + {-122.3938751220703,37.707454835665274}, + {-122.37567901611328,37.70663997801684}, + {-122.36297607421874,37.71343018466285}, + {-122.354736328125,37.727280276860036}, + {-122.36469268798828,37.73868429065797}, + {-122.38014221191408,37.75442980295571}, + {-122.38391876220702,37.78753873820529}, + {-122.35919952392578,37.8065289741725}, + {-122.35679626464844,37.820632846207864}, + {-122.3712158203125,37.835276322922695}, + {-122.3818588256836,37.82958198283902}, + {-122.37190246582031,37.80788523279169}, + {-122.38735198974608,37.791337175930686}, + {-122.40966796874999,37.812767557570204}, + {-122.46425628662108,37.807071480609274}, + {-122.46803283691405,37.810326435534755}, + {-122.47901916503906,37.81168262440736}, + {-122.48966217041016,37.78916666399649}, + {-122.50579833984375,37.78781006166096}, + {-122.5143814086914,37.779127216982424} + } +}; + +static void TileCoverPolygon(benchmark::State& state) { + std::size_t length = 0; + + while (state.KeepRunning()) { + auto tiles = util::tileCover(geomPolygon, 8); + length += tiles.size(); + } +} + +static void TileCountPolygon(benchmark::State& state) { + std::size_t length = 0; + + while (state.KeepRunning()) { + auto tiles = util::tileCount(geomPolygon, 16); + length += tiles; + } +} + +BENCHMARK(TileCountBounds); +BENCHMARK(TileCountPolygon); +BENCHMARK(TileCoverPitchedViewport); +BENCHMARK(TileCoverBounds); +BENCHMARK(TileCoverPolygon); + diff --git a/circle.yml b/circle.yml index 5f06791cd6..53cc6e9c7b 100644 --- a/circle.yml +++ b/circle.yml @@ -305,7 +305,7 @@ jobs: # ------------------------------------------------------------------------------ android-debug-arm-v7: docker: - - image: mbgl/7d2403f42e:android-ndk-r16b + - image: mbgl/feb0443038:android-ndk-r17 resource_class: large working_directory: /src environment: @@ -358,7 +358,7 @@ jobs: gcloud firebase test android run --type instrumentation \ --app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug/MapboxGLAndroidSDKTestApp-debug.apk \ --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 + --device-ids sailfish --os-version-ids 26 --locales en --orientations portrait --timeout 20m - store_artifacts: path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug destination: . @@ -378,7 +378,7 @@ jobs: # ------------------------------------------------------------------------------ android-release-all: docker: - - image: mbgl/7d2403f42e:android-ndk-r16b + - image: mbgl/feb0443038:android-ndk-r17 resource_class: large working_directory: /src environment: @@ -422,7 +422,7 @@ jobs: - deploy: name: Publish to Maven command: | - if [ "${CIRCLE_BRANCH}" == "release-boba" ]; then make run-android-upload-archives ; fi + if [ "${CIRCLE_BRANCH}" == "master" ]; then make run-android-upload-archives ; fi # ------------------------------------------------------------------------------ @@ -866,7 +866,7 @@ jobs: # ------------------------------------------------------------------------------ macos-debug-qt5: macos: - xcode: "9.2.0" + xcode: "9.3.0" environment: BUILDTYPE: Debug HOMEBREW_NO_AUTO_UPDATE: 1 diff --git a/cmake/NodeJS.cmake b/cmake/NodeJS.cmake deleted file mode 100644 index 8e0ec56982..0000000000 --- a/cmake/NodeJS.cmake +++ /dev/null @@ -1,600 +0,0 @@ -# NOTE: We're using a patched version of the original https://github.com/cjntaylor/node-cmake - -# Our version is in https://github.com/mapbox/node-cmake/blob/mapbox-gl-native/NodeJS.cmake and -# contains these patches: -# - https://github.com/cjntaylor/node-cmake/pull/20 -# - https://github.com/cjntaylor/node-cmake/pull/22 -# - https://github.com/cjntaylor/node-cmake/pull/23 - -# Defaults for standard Node.js builds -set(NODEJS_DEFAULT_URL https://nodejs.org/download/release) -set(NODEJS_DEFAULT_VERSION installed) -set(NODEJS_VERSION_FALLBACK latest) -set(NODEJS_DEFAULT_NAME node) -set(NODEJS_DEFAULT_CHECKSUM SHASUMS256.txt) -set(NODEJS_DEFAULT_CHECKTYPE SHA256) - -include(CMakeParseArguments) - -# Find a path by walking upward from a base directory until the path is -# found. Sets the variable ${PATH} to False if the path can't -# be determined -function(find_path_parent NAME BASE PATH) - set(ROOT ${BASE}) - set(${PATH} ${ROOT}/${NAME} PARENT_SCOPE) - set(DRIVE "^[A-Za-z]?:?/$") - while(NOT ROOT MATCHES ${DRIVE} AND NOT EXISTS ${ROOT}/${NAME}) - get_filename_component(ROOT ${ROOT} DIRECTORY) - set(${PATH} ${ROOT}/${NAME} PARENT_SCOPE) - endwhile() - if(ROOT MATCHES ${DRIVE}) - set(${PATH} False PARENT_SCOPE) - endif() -endfunction() - -# Shortcut for finding standard node module locations -macro(find_nodejs_module NAME BASE PATH) - find_path_parent(node_modules/${NAME} ${BASE} ${PATH}) -endmacro() - -# Download with a bit of nice output (without spewing progress) -function(download_file DESCRIPTION URL FILE) - message(STATUS "Downloading: ${URL}") - file(DOWNLOAD - ${URL} - ${FILE}.tmp - ${ARGN} - STATUS RESULT - ) - list(GET RESULT 0 STATUS) - if(STATUS) - list(GET result 1 MESSAGE) - message(FATAL_ERROR "Unable to download ${DESCRIPTION} from ${URL}: ${MESSAGE}") - else() - file(RENAME ${FILE}.tmp ${FILE}) - endif() -endfunction() - -# Embedded win_delay_load_hook file so that this file can be copied -# into projects directly (recommended practice) -function(nodejs_generate_delayload_hook OUTPUT) - file(WRITE ${OUTPUT} "") - file(APPEND ${OUTPUT} "/*\n") - file(APPEND ${OUTPUT} " * When this file is linked to a DLL, it sets up a delay-load hook that\n") - file(APPEND ${OUTPUT} " * intervenes when the DLL is trying to load 'node.exe' or 'iojs.exe'\n") - file(APPEND ${OUTPUT} " * dynamically. Instead of trying to locate the .exe file it'll just return\n") - file(APPEND ${OUTPUT} " * a handle to the process image.\n") - file(APPEND ${OUTPUT} " *\n") - file(APPEND ${OUTPUT} " * This allows compiled addons to work when node.exe or iojs.exe is renamed.\n") - file(APPEND ${OUTPUT} " */\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#ifdef _MSC_VER\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#ifndef DELAYIMP_INSECURE_WRITABLE_HOOKS\n") - file(APPEND ${OUTPUT} "#define DELAYIMP_INSECURE_WRITABLE_HOOKS\n") - file(APPEND ${OUTPUT} "#endif\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#ifndef WIN32_LEAN_AND_MEAN\n") - file(APPEND ${OUTPUT} "#define WIN32_LEAN_AND_MEAN\n") - file(APPEND ${OUTPUT} "#endif\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#include <windows.h>\n") - file(APPEND ${OUTPUT} "#include <Shlwapi.h>\n") - file(APPEND ${OUTPUT} "#include <delayimp.h>\n") - file(APPEND ${OUTPUT} "#include <string.h>\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "static FARPROC WINAPI load_exe_hook(unsigned int event, DelayLoadInfo* info) {\n") - file(APPEND ${OUTPUT} " if (event != dliNotePreLoadLibrary) return NULL;\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " if (_stricmp(info->szDll, \"iojs.exe\") != 0 &&\n") - file(APPEND ${OUTPUT} " _stricmp(info->szDll, \"node.exe\") != 0 &&\n") - file(APPEND ${OUTPUT} " _stricmp(info->szDll, \"node.dll\") != 0)\n") - file(APPEND ${OUTPUT} " return NULL;\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // Get a handle to the current process executable.\n") - file(APPEND ${OUTPUT} " HMODULE processModule = GetModuleHandle(NULL);\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // Get the path to the executable.\n") - file(APPEND ${OUTPUT} " TCHAR processPath[_MAX_PATH];\n") - file(APPEND ${OUTPUT} " GetModuleFileName(processModule, processPath, _MAX_PATH);\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // Get the name of the current executable.\n") - file(APPEND ${OUTPUT} " LPSTR processName = PathFindFileName(processPath);\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // If the current process is node or iojs, then just return the proccess \n") - file(APPEND ${OUTPUT} " // module.\n") - file(APPEND ${OUTPUT} " if (_stricmp(processName, \"node.exe\") == 0 ||\n") - file(APPEND ${OUTPUT} " _stricmp(processName, \"iojs.exe\") == 0) {\n") - file(APPEND ${OUTPUT} " return (FARPROC) processModule;\n") - file(APPEND ${OUTPUT} " }\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // If it is another process, attempt to load 'node.dll' from the same \n") - file(APPEND ${OUTPUT} " // directory.\n") - file(APPEND ${OUTPUT} " PathRemoveFileSpec(processPath);\n") - file(APPEND ${OUTPUT} " PathAppend(processPath, \"node.dll\");\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " HMODULE nodeDllModule = GetModuleHandle(processPath);\n") - file(APPEND ${OUTPUT} " if(nodeDllModule != NULL) {\n") - file(APPEND ${OUTPUT} " // This application has a node.dll in the same directory as the executable,\n") - file(APPEND ${OUTPUT} " // use that.\n") - file(APPEND ${OUTPUT} " return (FARPROC) nodeDllModule;\n") - file(APPEND ${OUTPUT} " }\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // Fallback to the current executable, which must statically link to \n") - file(APPEND ${OUTPUT} " // node.lib\n") - file(APPEND ${OUTPUT} " return (FARPROC) processModule;\n") - file(APPEND ${OUTPUT} "}\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "PfnDliHook __pfnDliNotifyHook2 = load_exe_hook;\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#endif\n") -endfunction() - -# Sets up a project to build Node.js native modules -# - Downloads required dependencies and unpacks them to the build directory. -# Internet access is required the first invocation but not after ( -# provided the download is successful) -# - Sets up several variables for building against the downloaded -# dependencies -# - Guarded to prevent multiple executions, so a single project hierarchy -# will only call this once -function(nodejs_init) - # Prevents this function from executing more than once - if(NODEJS_INIT) - return() - endif() - - # Regex patterns used by the init function for component extraction - set(HEADERS_MATCH "^([A-Fa-f0-9]+)[ \t]+([^-]+)-(headers|v?[0-9.]+)-(headers|v?[0-9.]+)([.]tar[.]gz)$") - set(LIB32_MATCH "(^[0-9A-Fa-f]+)[\t ]+(win-x86)?(/)?([^/]*)(.lib)$") - set(LIB64_MATCH "(^[0-9A-Fa-f]+)[\t ]+(win-)?(x64/)(.*)(.lib)$") - - # Parse function arguments - cmake_parse_arguments(nodejs_init - "" "URL;NAME;VERSION;CHECKSUM;CHECKTYPE" "" ${ARGN} - ) - - # Allow the download URL to be overridden by command line argument - # NODEJS_URL - if(NODEJS_URL) - set(URL ${NODEJS_URL}) - else() - # Use the argument if specified, falling back to the default - set(URL ${NODEJS_DEFAULT_URL}) - if(nodejs_init_URL) - set(URL ${nodejs_init_URL}) - endif() - endif() - - # Allow name to be overridden by command line argument NODEJS_NAME - if(NODEJS_NAME) - set(NAME ${NODEJS_NAME}) - else() - # Use the argument if specified, falling back to the default - set(NAME ${NODEJS_DEFAULT_NAME}) - if(nodejs_init_NAME) - set(NAME ${nodejs_init_NAME}) - endif() - endif() - - # Allow the checksum file to be overridden by command line argument - # NODEJS_CHECKSUM - if(NODEJS_CHECKSUM) - set(CHECKSUM ${NODEJS_CHECKSUM}) - else() - # Use the argument if specified, falling back to the default - set(CHECKSUM ${NODEJS_DEFAULT_CHECKSUM}) - if(nodejs_init_CHECKSUM) - set(CHECKSUM ${nodejs_init_CHECKSUM}) - endif() - endif() - - # Allow the checksum type to be overriden by the command line argument - # NODEJS_CHECKTYPE - if(NODEJS_CHECKTYPE) - set(CHECKTYPE ${NODEJS_CHECKTYPE}) - else() - # Use the argument if specified, falling back to the default - set(CHECKTYPE ${NODEJS_DEFAULT_CHECKTYPE}) - if(nodejs_init_CHECKTYPE) - set(CHECKTYPE ${nodejs_init_CHECKTYPE}) - endif() - endif() - - # Allow the version to be overridden by the command line argument - # NODEJS_VERSION - if(NODEJS_VERSION) - set(VERSION ${NODEJS_VERSION}) - else() - # Use the argument if specified, falling back to the default - set(VERSION ${NODEJS_DEFAULT_VERSION}) - if(nodejs_init_VERSION) - set(VERSION ${nodejs_init_VERSION}) - endif() - endif() - - # "installed" is a special version that tries to use the currently - # installed version (determined by running node) - set(NODEJS_INSTALLED False CACHE BOOL "Node.js install status" FORCE) - if(VERSION STREQUAL "installed") - if(NOT NAME STREQUAL ${NODEJS_DEFAULT_NAME}) - message(FATAL_ERROR - "'Installed' version identifier can only be used with" - "the core Node.js library" - ) - endif() - # Fall back to the "latest" version if node isn't installed - set(VERSION ${NODEJS_VERSION_FALLBACK}) - find_program(NODEJS_BINARY NAMES nodejs node) - if(NODEJS_BINARY) - execute_process( - COMMAND ${NODEJS_BINARY} --version - RESULT_VARIABLE INSTALLED_VERSION_RESULT - OUTPUT_VARIABLE INSTALLED_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(INSTALLED_VERSION_RESULT STREQUAL "0") - set(NODEJS_INSTALLED True CACHE BOOL - "Node.js install status" FORCE - ) - set(VERSION ${INSTALLED_VERSION}) - endif() - endif() - endif() - - # Create a temporary download directory - set(TEMP ${CMAKE_CURRENT_BINARY_DIR}/temp) - file(MAKE_DIRECTORY ${TEMP}) - - # Unless the target is special version "latest", the parameters - # necessary to construct the root path are known - if(NOT VERSION STREQUAL "latest") - set(ROOT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}/${VERSION}) - # Extract checksums from the existing checksum file - set(CHECKSUM_TARGET ${ROOT}/CHECKSUM) - endif() - - # If we're trying to determine the version or we haven't saved the - # checksum file for this version, download it from the specified server - if(VERSION STREQUAL "latest" OR - (DEFINED ROOT AND NOT EXISTS ${ROOT}/CHECKSUM)) - if(DEFINED ROOT) - # Clear away the old checksum in case the new one is different - # and/or it fails to download - file(REMOVE ${ROOT}/CHECKSUM) - endif() - file(REMOVE ${TEMP}/CHECKSUM) - download_file( - "checksum file" - ${URL}/${VERSION}/${CHECKSUM} - ${TEMP}/CHECKSUM - INACTIVITY_TIMEOUT 10 - ) - # Extract checksums from the temporary file - set(CHECKSUM_TARGET ${TEMP}/CHECKSUM) - endif() - - # Extract the version, name, header archive and archive checksum - # from the file. This first extract is what defines / specifies the - # actual version number and name. - file(STRINGS - ${CHECKSUM_TARGET} HEADERS_CHECKSUM - REGEX ${HEADERS_MATCH} - LIMIT_COUNT 1 - ) - if(NOT HEADERS_CHECKSUM) - file(REMOVE ${TEMP}/CHECKSUM) - if(DEFINED ROOT) - file(REMOVE ${ROOT}/CHECKSUM) - endif() - message(FATAL_ERROR "Unable to extract header archive checksum") - endif() - string(REGEX MATCH ${HEADERS_MATCH} HEADERS_CHECKSUM ${HEADERS_CHECKSUM}) - set(HEADERS_CHECKSUM ${CMAKE_MATCH_1}) - set(NAME ${CMAKE_MATCH_2}) - if(CMAKE_MATCH_3 STREQUAL "headers") - set(VERSION ${CMAKE_MATCH_4}) - else() - set(VERSION ${CMAKE_MATCH_3}) - endif() - set(HEADERS_ARCHIVE - ${CMAKE_MATCH_2}-${CMAKE_MATCH_3}-${CMAKE_MATCH_4}${CMAKE_MATCH_5} - ) - # Make sure that the root directory exists, and that the checksum - # file has been moved over from temp - if(DEFINED ROOT) - set(OLD_ROOT ${ROOT}) - endif() - set(ROOT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}/${VERSION}) - if(DEFINED OLD_ROOT AND NOT ROOT STREQUAL "${OLD_ROOT}") - file(REMOVE ${TEMP}/CHECKSUM) - file(REMOVE ${ROOT}/CHECKSUM) - message(FATAL_ERROR "Version/Name mismatch") - endif() - file(MAKE_DIRECTORY ${ROOT}) - if(EXISTS ${TEMP}/CHECKSUM) - file(REMOVE ${ROOT}/CHECKSUM) - file(RENAME ${TEMP}/CHECKSUM ${ROOT}/CHECKSUM) - endif() - - # Now that its fully resolved, report the name and version of Node.js being - # used - message(STATUS "NodeJS: Using ${NAME}, version ${VERSION}") - - # Download the headers for the version being used - # Theoretically, these could be found by searching the installed - # system, but in practice, this can be error prone. They're provided - # on the download servers, so just use the ones there. - if(NOT EXISTS ${ROOT}/include) - file(REMOVE ${TEMP}/${HEADERS_ARCHIVE}) - download_file( - "Node.js headers" - ${URL}/${VERSION}/${HEADERS_ARCHIVE} - ${TEMP}/${HEADERS_ARCHIVE} - INACTIVITY_TIMEOUT 10 - EXPECTED_HASH ${CHECKTYPE}=${HEADERS_CHECKSUM} - ) - execute_process( - COMMAND ${CMAKE_COMMAND} -E tar xfz ${TEMP}/${HEADERS_ARCHIVE} - WORKING_DIRECTORY ${TEMP} - ) - - # This adapts the header extraction to support a number of different - # header archive contents in addition to the one used by the - # default Node.js library - unset(NODEJS_HEADERS_PATH CACHE) - find_path(NODEJS_HEADERS_PATH - NAMES src include - PATHS - ${TEMP}/${NAME}-${VERSION}-headers - ${TEMP}/${NAME}-${VERSION} - ${TEMP}/${NODEJS_DEFAULT_NAME}-${VERSION}-headers - ${TEMP}/${NODEJS_DEFAULT_NAME}-${VERSION} - ${TEMP}/${NODEJS_DEFAULT_NAME} - ${TEMP} - NO_DEFAULT_PATH - ) - if(NOT NODEJS_HEADERS_PATH) - message(FATAL_ERROR "Unable to find extracted headers folder") - endif() - - # Move the headers into a standard location with a standard layout - file(REMOVE ${TEMP}/${HEADERS_ARCHIVE}) - file(REMOVE_RECURSE ${ROOT}/include) - if(EXISTS ${NODEJS_HEADERS_PATH}/include/node) - file(RENAME ${NODEJS_HEADERS_PATH}/include/node ${ROOT}/include) - elseif(EXISTS ${NODEJS_HEADERS_PATH}/src) - file(MAKE_DIRECTORY ${ROOT}/include) - if(NOT EXISTS ${NODEJS_HEADERS_PATH}/src) - file(REMOVE_RECURSE ${ROOT}/include) - message(FATAL_ERROR "Unable to find core headers") - endif() - file(COPY ${NODEJS_HEADERS_PATH}/src/ - DESTINATION ${ROOT}/include - ) - if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/uv/include) - file(REMOVE_RECURSE ${ROOT}/include) - message(FATAL_ERROR "Unable to find libuv headers") - endif() - file(COPY ${NODEJS_HEADERS_PATH}/deps/uv/include/ - DESTINATION ${ROOT}/include - ) - if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/v8/include) - file(REMOVE_RECURSE ${ROOT}/include) - message(FATAL_ERROR "Unable to find v8 headers") - endif() - file(COPY ${NODEJS_HEADERS_PATH}/deps/v8/include/ - DESTINATION ${ROOT}/include - ) - if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/zlib) - file(REMOVE_RECURSE ${ROOT}/include) - message(FATAL_ERROR "Unable to find zlib headers") - endif() - file(COPY ${NODEJS_HEADERS_PATH}/deps/zlib/ - DESTINATION ${ROOT}/include - ) - endif() - file(REMOVE_RECURSE ${NODEJS_HEADERS_PATH}) - unset(NODEJS_HEADERS_PATH CACHE) - endif() - - # Only download the libraries on windows, since its the only place - # its necessary. Note, this requires rerunning CMake if moving - # a module from one platform to another (should happen automatically - # with most generators) - if(WIN32) - # Download the win32 library for linking - file(STRINGS - ${ROOT}/CHECKSUM LIB32_CHECKSUM - LIMIT_COUNT 1 - REGEX ${LIB32_MATCH} - ) - if(NOT LIB32_CHECKSUM) - message(FATAL_ERROR "Unable to extract x86 library checksum") - endif() - string(REGEX MATCH ${LIB32_MATCH} LIB32_CHECKSUM ${LIB32_CHECKSUM}) - set(LIB32_CHECKSUM ${CMAKE_MATCH_1}) - set(LIB32_PATH win-x86) - set(LIB32_NAME ${CMAKE_MATCH_4}${CMAKE_MATCH_5}) - set(LIB32_TARGET ${CMAKE_MATCH_2}${CMAKE_MATCH_3}${LIB32_NAME}) - if(NOT EXISTS ${ROOT}/${LIB32_PATH}) - file(REMOVE_RECURSE ${TEMP}/${LIB32_PATH}) - download_file( - "Node.js windows library (32-bit)" - ${URL}/${VERSION}/${LIB32_TARGET} - ${TEMP}/${LIB32_PATH}/${LIB32_NAME} - INACTIVITY_TIMEOUT 10 - EXPECTED_HASH ${CHECKTYPE}=${LIB32_CHECKSUM} - ) - file(REMOVE_RECURSE ${ROOT}/${LIB32_PATH}) - file(MAKE_DIRECTORY ${ROOT}/${LIB32_PATH}) - file(RENAME - ${TEMP}/${LIB32_PATH}/${LIB32_NAME} - ${ROOT}/${LIB32_PATH}/${LIB32_NAME} - ) - file(REMOVE_RECURSE ${TEMP}/${LIB32_PATH}) - endif() - - # Download the win64 library for linking - file(STRINGS - ${ROOT}/CHECKSUM LIB64_CHECKSUM - LIMIT_COUNT 1 - REGEX ${LIB64_MATCH} - ) - if(NOT LIB64_CHECKSUM) - message(FATAL_ERROR "Unable to extract x64 library checksum") - endif() - string(REGEX MATCH ${LIB64_MATCH} LIB64_CHECKSUM ${LIB64_CHECKSUM}) - set(LIB64_CHECKSUM ${CMAKE_MATCH_1}) - set(LIB64_PATH win-x64) - set(LIB64_NAME ${CMAKE_MATCH_4}${CMAKE_MATCH_5}) - set(LIB64_TARGET ${CMAKE_MATCH_2}${CMAKE_MATCH_3}${LIB64_NAME}) - if(NOT EXISTS ${ROOT}/${LIB64_PATH}) - file(REMOVE_RECURSE ${TEMP}/${LIB64_PATH}) - download_file( - "Node.js windows library (64-bit)" - ${URL}/${VERSION}/${LIB64_TARGET} - ${TEMP}/${LIB64_PATH}/${LIB64_NAME} - INACTIVITY_TIMEOUT 10 - EXPECTED_HASH ${CHECKTYPE}=${LIB64_CHECKSUM} - ) - file(REMOVE_RECURSE ${ROOT}/${LIB64_PATH}) - file(MAKE_DIRECTORY ${ROOT}/${LIB64_PATH}) - file(RENAME - ${TEMP}/${LIB64_PATH}/${LIB64_NAME} - ${ROOT}/${LIB64_PATH}/${LIB64_NAME} - ) - file(REMOVE_RECURSE ${TEMP}/${LIB64_PATH}) - endif() - endif() - - # The downloaded headers should always be set for inclusion - list(APPEND INCLUDE_DIRS ${ROOT}/include) - - # Look for the NAN module, and add it to the includes - find_nodejs_module( - nan - ${CMAKE_CURRENT_SOURCE_DIR} - NODEJS_NAN_DIR - ) - if(NODEJS_NAN_DIR) - list(APPEND INCLUDE_DIRS ${NODEJS_NAN_DIR}) - endif() - - # Under windows, we need a bunch of libraries (due to the way - # dynamic linking works) - if(WIN32) - # Generate and use a delay load hook to allow the node binary - # name to be changed while still loading native modules - set(DELAY_LOAD_HOOK ${CMAKE_CURRENT_BINARY_DIR}/win_delay_load_hook.c) - nodejs_generate_delayload_hook(${DELAY_LOAD_HOOK}) - set(SOURCES ${DELAY_LOAD_HOOK}) - - # Necessary flags to get delayload working correctly - list(APPEND LINK_FLAGS - "-IGNORE:4199" - "-DELAYLOAD:iojs.exe" - "-DELAYLOAD:node.exe" - "-DELAYLOAD:node.dll" - ) - - # Core system libraries used by node - list(APPEND LIBRARIES - kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib - odbc32.lib Shlwapi.lib DelayImp.lib - ) - - # Also link to the node stub itself (downloaded above) - if(CMAKE_CL_64) - list(APPEND LIBRARIES ${ROOT}/${LIB64_PATH}/${LIB64_NAME}) - else() - list(APPEND LIBRARIES ${ROOT}/${LIB32_PATH}/${LIB32_NAME}) - endif() - else() - # Non-windows platforms should use these flags - list(APPEND DEFINITIONS _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64) - endif() - - # Special handling for OSX / clang to allow undefined symbols - # Define is required by node on OSX - if(APPLE) - list(APPEND LINK_FLAGS "-undefined dynamic_lookup") - list(APPEND DEFINITIONS _DARWIN_USE_64_BIT_INODE=1) - endif() - - # Export all settings for use as arguments in the rest of the build - set(NODEJS_VERSION ${VERSION} PARENT_SCOPE) - set(NODEJS_SOURCES ${SOURCES} PARENT_SCOPE) - set(NODEJS_INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE) - set(NODEJS_LIBRARIES ${LIBRARIES} PARENT_SCOPE) - set(NODEJS_LINK_FLAGS ${LINK_FLAGS} PARENT_SCOPE) - set(NODEJS_DEFINITIONS ${DEFINITIONS} PARENT_SCOPE) - - # Prevents this function from executing more than once - set(NODEJS_INIT TRUE PARENT_SCOPE) -endfunction() - -# Helper function for defining a node module -# After nodejs_init, all of the settings and dependencies necessary to do -# this yourself are defined, but this helps make sure everything is configured -# correctly. Feel free to use it as a model to do this by hand (or to -# tweak this configuration if you need something custom). -function(add_nodejs_module NAME) - # Make sure node is initialized (variables set) before defining the module - if(NOT NODEJS_INIT) - message(FATAL_ERROR - "Node.js has not been initialized. " - "Call nodejs_init before adding any modules" - ) - endif() - # In order to match node-gyp, we need to build into type specific folders - # ncmake takes care of this, but be sure to set CMAKE_BUILD_TYPE yourself - # if invoking CMake directly - if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) - message(FATAL_ERROR - "Configuration type must be specified. " - "Set CMAKE_BUILD_TYPE or use a different generator" - ) - endif() - - # A node module is a shared library - add_library(${NAME} SHARED ${NODEJS_SOURCES} ${ARGN}) - # Add compiler defines for the module - # Two helpful ones: - # MODULE_NAME must match the name of the build library, define that here - target_compile_definitions(${NAME} - PRIVATE MODULE_NAME=${NAME} - PUBLIC ${NODEJS_DEFINITIONS} - ) - # This properly defines includes for the module - target_include_directories(${NAME} PUBLIC ${NODEJS_INCLUDE_DIRS}) - - # Add link flags to the module - target_link_libraries(${NAME} ${NODEJS_LIBRARIES}) - - # Set required properties for the module to build properly - # Correct naming, symbol visiblity and C++ standard - set_target_properties(${NAME} PROPERTIES - OUTPUT_NAME ${NAME} - PREFIX "" - SUFFIX ".node" - MACOSX_RPATH ON - C_VISIBILITY_PRESET hidden - CXX_VISIBILITY_PRESET hidden - POSITION_INDEPENDENT_CODE TRUE - CMAKE_CXX_STANDARD_REQUIRED TRUE - CXX_STANDARD 11 - LINK_FLAGS "${NODEJS_LINK_FLAGS}" - ) - - # Make sure we're buiilding in a build specific output directory - # Only necessary on single-target generators (Make, Ninja) - # Multi-target generators do this automatically - # This (luckily) mirrors node-gyp conventions - if(NOT CMAKE_CONFIGURATION_TYPES) - set_property(TARGET ${NAME} PROPERTY - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BUILD_TYPE} - ) - endif() -endfunction() diff --git a/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake index fdafcf30f9..21547eb9c4 100644 --- a/cmake/benchmark-files.cmake +++ b/cmake/benchmark-files.cmake @@ -23,5 +23,6 @@ set(MBGL_BENCHMARK_FILES # util benchmark/util/dtoa.benchmark.cpp + benchmark/util/tilecover.benchmark.cpp ) diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 0853097630..7e48b336b4 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -360,7 +360,6 @@ set(MBGL_CORE_FILES include/mbgl/style/conversion.hpp include/mbgl/style/data_driven_property_value.hpp include/mbgl/style/filter.hpp - include/mbgl/style/filter_evaluator.hpp include/mbgl/style/heatmap_color_property_value.hpp include/mbgl/style/image.hpp include/mbgl/style/layer.hpp @@ -377,7 +376,6 @@ set(MBGL_CORE_FILES src/mbgl/style/custom_tile_loader.cpp src/mbgl/style/custom_tile_loader.hpp src/mbgl/style/filter.cpp - src/mbgl/style/filter_evaluator.cpp src/mbgl/style/image.cpp src/mbgl/style/image_impl.cpp src/mbgl/style/image_impl.hpp @@ -748,11 +746,11 @@ set(MBGL_CORE_FILES src/mbgl/util/stopwatch.hpp src/mbgl/util/string.cpp src/mbgl/util/thread_local.hpp - src/mbgl/util/throttler.cpp - src/mbgl/util/throttler.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 diff --git a/cmake/loop-uv.cmake b/cmake/loop-uv.cmake index f4e7ced00e..e1d3166b63 100644 --- a/cmake/loop-uv.cmake +++ b/cmake/loop-uv.cmake @@ -1,18 +1,14 @@ -add_library(mbgl-loop-uv STATIC - platform/default/async_task.cpp - platform/default/run_loop.cpp - platform/default/timer.cpp -) +add_library(mbgl-loop-uv INTERFACE) -target_include_directories(mbgl-loop-uv - PRIVATE include - PRIVATE src +target_sources(mbgl-loop-uv INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/async_task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/run_loop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/timer.cpp ) -target_link_libraries(mbgl-loop-uv - PRIVATE mbgl-core +target_include_directories(mbgl-loop-uv INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src ) create_source_groups(mbgl-loop-uv) - -xcode_create_scheme(TARGET mbgl-loop-uv)
\ No newline at end of file diff --git a/cmake/mason.cmake b/cmake/mason.cmake index bc31feeb5f..6116067080 100644 --- a/cmake/mason.cmake +++ b/cmake/mason.cmake @@ -23,9 +23,7 @@ function(mason_detect_platform) if(NOT MASON_PLATFORM_VERSION) # 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") - set(MASON_PLATFORM_VERSION "arm-v5-9" PARENT_SCOPE) - elseif (ANDROID_ABI STREQUAL "armeabi-v7a") + if (ANDROID_ABI STREQUAL "armeabi-v7a") set(MASON_PLATFORM_VERSION "arm-v7-9" PARENT_SCOPE) elseif (ANDROID_ABI STREQUAL "arm64-v8a") set(MASON_PLATFORM_VERSION "arm-v8-21" PARENT_SCOPE) @@ -33,10 +31,6 @@ function(mason_detect_platform) set(MASON_PLATFORM_VERSION "x86-9" PARENT_SCOPE) elseif (ANDROID_ABI STREQUAL "x86_64") set(MASON_PLATFORM_VERSION "x86-64-21" PARENT_SCOPE) - elseif (ANDROID_ABI STREQUAL "mips") - set(MASON_PLATFORM_VERSION "mips-9" PARENT_SCOPE) - elseif (ANDROID_ABI STREQUAL "mips64") - set(MASON_PLATFORM_VERSION "mips-64-9" PARENT_SCOPE) else() message(FATAL_ERROR "Unknown ANDROID_ABI '${ANDROID_ABI}'.") endif() diff --git a/cmake/mbgl.cmake b/cmake/mbgl.cmake index f087c32511..88e24e3536 100644 --- a/cmake/mbgl.cmake +++ b/cmake/mbgl.cmake @@ -17,12 +17,32 @@ if(WITH_NODEJS) message(FATAL_ERROR "Could not find npm") endif() + execute_process( + COMMAND "${NodeJS_EXECUTABLE}" -e "process.stdout.write(process.versions.node)" + RESULT_VARIABLE _STATUS_CODE + OUTPUT_VARIABLE NodeJS_VERSION + ERROR_VARIABLE _STATUS_MESSAGE + ) + if(NOT _STATUS_CODE EQUAL 0) + message(FATAL_ERROR "Could not detect Node.js version: ${_STATUS_MESSAGE}") + endif() + + execute_process( + COMMAND "${NodeJS_EXECUTABLE}" -e "process.stdout.write(process.versions.modules)" + RESULT_VARIABLE _STATUS_CODE + OUTPUT_VARIABLE NodeJS_ABI + ERROR_VARIABLE _STATUS_MESSAGE + ) + if(NOT _STATUS_CODE EQUAL 0) + message(FATAL_ERROR "Could not detect Node.js ABI version: ${_STATUS_MESSAGE}") + endif() + function(_npm_install DIRECTORY NAME ADDITIONAL_DEPS) SET(NPM_INSTALL_FAILED FALSE) 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 --ignore-scripts WORKING_DIRECTORY "${DIRECTORY}" RESULT_VARIABLE NPM_INSTALL_FAILED) if(NOT NPM_INSTALL_FAILED) @@ -32,7 +52,7 @@ if(WITH_NODEJS) add_custom_command( OUTPUT "${DIRECTORY}/node_modules/.${NAME}.stamp" - COMMAND ${NodeJS_EXECUTABLE} ${npm_EXECUTABLE} install --ignore-scripts + COMMAND "${NodeJS_EXECUTABLE}" "${npm_EXECUTABLE}" install --ignore-scripts COMMAND ${CMAKE_COMMAND} -E touch "${DIRECTORY}/node_modules/.${NAME}.stamp" WORKING_DIRECTORY "${DIRECTORY}" DEPENDS ${ADDITIONAL_DEPS} "${DIRECTORY}/package.json" @@ -72,7 +92,12 @@ endif() # Generate source groups so the files are properly sorted in IDEs like Xcode. function(create_source_groups target) - get_target_property(sources ${target} SOURCES) + get_target_property(type ${target} TYPE) + if(type AND type STREQUAL "INTERFACE_LIBRARY") + get_target_property(sources ${target} INTERFACE_SOURCES) + else() + get_target_property(sources ${target} SOURCES) + endif() foreach(file ${sources}) get_filename_component(file "${file}" ABSOLUTE) string(REGEX REPLACE "^${CMAKE_SOURCE_DIR}/" "" group "${file}") diff --git a/cmake/node.cmake b/cmake/node.cmake index 0c2f556b7d..b6f7bb9dc0 100644 --- a/cmake/node.cmake +++ b/cmake/node.cmake @@ -1,75 +1,96 @@ # Load Node.js -include(cmake/NodeJS.cmake) -nodejs_init() +include(node_modules/@mapbox/cmake-node-module/module.cmake) -add_nodejs_module(mbgl-node - platform/node/src/node_mapbox_gl_native.cpp -) +add_library(mbgl-loop-node INTERFACE) -# NodeJS.cmake forces C++11. -# https://github.com/cjntaylor/node-cmake/issues/18 -set_target_properties("mbgl-node" PROPERTIES CXX_STANDARD 14) - -target_sources(mbgl-node - PRIVATE platform/node/src/node_logging.hpp - PRIVATE platform/node/src/node_logging.cpp - PRIVATE platform/node/src/node_conversion.hpp - PRIVATE platform/node/src/node_map.hpp - PRIVATE platform/node/src/node_map.cpp - PRIVATE platform/node/src/node_request.hpp - PRIVATE platform/node/src/node_request.cpp - PRIVATE platform/node/src/node_feature.hpp - PRIVATE platform/node/src/node_feature.cpp - PRIVATE platform/node/src/node_thread_pool.hpp - PRIVATE platform/node/src/node_thread_pool.cpp - PRIVATE platform/node/src/node_expression.hpp - PRIVATE platform/node/src/node_expression.cpp - PRIVATE platform/node/src/util/async_queue.hpp +target_sources(mbgl-loop-node INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/async_task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/run_loop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/timer.cpp ) -target_include_directories(mbgl-node - PRIVATE platform/default +target_include_directories(mbgl-loop-node INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src ) -# Use node-provided uv.h. This is not part of loop-uv.cmake because loop-uv.cmake is also -# used by linux/config.cmake, where we need to use headers provided by mason's libuv. -target_include_directories(mbgl-loop-uv PUBLIC ${NODEJS_INCLUDE_DIRS}) +create_source_groups(mbgl-loop-node) + -target_link_libraries(mbgl-node - PRIVATE mbgl-core - PRIVATE mbgl-loop-uv +add_node_module(mbgl-node + INSTALL_DIR "lib" + NAN_VERSION "2.10.0" + EXCLUDE_NODE_ABIS 47 51 59 # Don't build old beta ABIs 5.x, 7.x, and 9.x ) -target_add_mason_package(mbgl-node PRIVATE geojson) +target_sources(mbgl-node INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_mapbox_gl_native.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_mapbox_gl_native.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_logging.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_logging.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_conversion.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_map.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_map.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_request.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_request.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_feature.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_feature.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_thread_pool.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_thread_pool.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_expression.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_expression.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/util/async_queue.hpp +) + +target_include_directories(mbgl-node INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default +) -add_custom_command( - TARGET mbgl-node - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:mbgl-node> ${CMAKE_SOURCE_DIR}/lib/mapbox_gl_native.node +target_link_libraries(mbgl-node INTERFACE + mbgl-core + mbgl-loop-node ) +target_add_mason_package(mbgl-node INTERFACE geojson) + +add_custom_target(mbgl-node.active DEPENDS mbgl-node.abi-${NodeJS_ABI}) + mbgl_platform_node() create_source_groups(mbgl-node) -initialize_xcode_cxx_build_settings(mbgl-node) +foreach(ABI IN LISTS mbgl-node::abis) + initialize_xcode_cxx_build_settings(mbgl-node.abi-${ABI}) + xcode_create_scheme( + TARGET mbgl-node.abi-${ABI} + NAME "mbgl-node (ABI ${ABI})" + ) +endforeach() + +xcode_create_scheme( + TARGET mbgl-node.active + TYPE library + NAME "mbgl-node (Active ABI)" +) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.all + TYPE library + NAME "mbgl-node (All ABIs)" ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node tests" + NAME "node tests (Active ABI)" ARGS "node_modules/.bin/tape platform/node/test/js/**/*.test.js" ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node render tests" + NAME "node render tests (Active ABI)" ARGS "platform/node/test/render.test.js" OPTIONAL_ARGS @@ -78,9 +99,9 @@ xcode_create_scheme( ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node query tests" + NAME "node query tests (Active ABI)" ARGS "platform/node/test/query.test.js" OPTIONAL_ARGS @@ -89,9 +110,9 @@ xcode_create_scheme( ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node expression tests" + NAME "node expression tests (Active ABI)" ARGS "platform/node/test/expression.test.js" OPTIONAL_ARGS @@ -100,9 +121,9 @@ xcode_create_scheme( ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node-benchmark" + NAME "node-benchmark (Active ABI)" ARGS "platform/node/test/benchmark.js" OPTIONAL_ARGS diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp index 6baaae862f..431afb4d13 100644 --- a/include/mbgl/style/expression/compound_expression.hpp +++ b/include/mbgl/style/expression/compound_expression.hpp @@ -36,7 +36,7 @@ template <typename T> struct Varargs : std::vector<T> { using std::vector<T>::vector; }; namespace detail { -// Base class for the Signature<Fn> structs that are used to determine the +// Base class for the Signature<Fn> structs that are used to determine // each CompoundExpression definition's type::Type data from the type of its // "evaluate" function. struct SignatureBase { @@ -137,10 +137,21 @@ ParseResult parseCompoundExpression(const std::string name, const mbgl::style::c ParseResult createCompoundExpression(const CompoundExpressionRegistry::Definition& definition, std::vector<std::unique_ptr<Expression>> args, ParsingContext& ctx); - + ParseResult createCompoundExpression(const std::string& name, std::vector<std::unique_ptr<Expression>> args, ParsingContext& ctx); +// Convenience method for use expressions that have 0, 1, or 2 args. +ParseResult createCompoundExpression(const std::string& name, ParsingContext& ctx); + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr<Expression> arg1, + ParsingContext& ctx); + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr<Expression> arg1, + std::unique_ptr<Expression> arg2, + ParsingContext& ctx); } // namespace expression } // namespace style diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp index a00c468efc..d3b3a20cce 100644 --- a/include/mbgl/style/expression/literal.hpp +++ b/include/mbgl/style/expression/literal.hpp @@ -51,6 +51,9 @@ private: Value value; }; +std::unique_ptr<Literal> createLiteral(const char* value); +std::unique_ptr<Literal> createLiteral(Value value); + } // namespace expression } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index ccf8dce188..ce4015bb69 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -11,271 +11,31 @@ namespace mbgl { namespace style { - -class Filter; - -class NullFilter { -public: - friend bool operator==(const NullFilter&, const NullFilter&) { - return true; - } -}; - -class EqualsFilter { -public: - std::string key; - Value value; - - friend bool operator==(const EqualsFilter& lhs, const EqualsFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class NotEqualsFilter { -public: - std::string key; - Value value; - - friend bool operator==(const NotEqualsFilter& lhs, const NotEqualsFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class LessThanFilter { -public: - std::string key; - Value value; - - friend bool operator==(const LessThanFilter& lhs, const LessThanFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class LessThanEqualsFilter { -public: - std::string key; - Value value; - - friend bool operator==(const LessThanEqualsFilter& lhs, const LessThanEqualsFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class GreaterThanFilter { -public: - std::string key; - Value value; - - friend bool operator==(const GreaterThanFilter& lhs, const GreaterThanFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class GreaterThanEqualsFilter { -public: - std::string key; - Value value; - - friend bool operator==(const GreaterThanEqualsFilter& lhs, const GreaterThanEqualsFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class InFilter { -public: - std::string key; - std::vector<Value> values; - - friend bool operator==(const InFilter& lhs, const InFilter& rhs) { - return std::tie(lhs.key, lhs.values) == std::tie(rhs.key, rhs.values); - } -}; - -class NotInFilter { -public: - std::string key; - std::vector<Value> values; - - friend bool operator==(const NotInFilter& lhs, const NotInFilter& rhs) { - return std::tie(lhs.key, lhs.values) == std::tie(rhs.key, rhs.values); - } -}; - -class AnyFilter { -public: - std::vector<Filter> filters; - - friend bool operator==(const AnyFilter& lhs, const AnyFilter& rhs) { - return lhs.filters == rhs.filters; - } -}; - -class AllFilter { -public: - std::vector<Filter> filters; - - friend bool operator==(const AllFilter& lhs, const AllFilter& rhs) { - return lhs.filters == rhs.filters; - } -}; - -class NoneFilter { -public: - std::vector<Filter> filters; - - friend bool operator==(const NoneFilter& lhs, const NoneFilter& rhs) { - return lhs.filters == rhs.filters; - } -}; - -class HasFilter { -public: - std::string key; - - friend bool operator==(const HasFilter& lhs, const HasFilter& rhs) { - return lhs.key == rhs.key; - } -}; - -class NotHasFilter { -public: - std::string key; - - friend bool operator==(const NotHasFilter& lhs, const NotHasFilter& rhs) { - return lhs.key == rhs.key; - } -}; - - -class TypeEqualsFilter { -public: - FeatureType value; - - friend bool operator==(const TypeEqualsFilter& lhs, const TypeEqualsFilter& rhs) { - return lhs.value == rhs.value; - } -}; - -class TypeNotEqualsFilter { -public: - FeatureType value; - - friend bool operator==(const TypeNotEqualsFilter& lhs, const TypeNotEqualsFilter& rhs) { - return lhs.value == rhs.value; - } -}; - -class TypeInFilter { -public: - std::vector<FeatureType> values; - - friend bool operator==(const TypeInFilter& lhs, const TypeInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - -class TypeNotInFilter { -public: - std::vector<FeatureType> values; - - friend bool operator==(const TypeNotInFilter& lhs, const TypeNotInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - - -class IdentifierEqualsFilter { -public: - FeatureIdentifier value; - - friend bool operator==(const IdentifierEqualsFilter& lhs, const IdentifierEqualsFilter& rhs) { - return lhs.value == rhs.value; - } -}; - -class IdentifierNotEqualsFilter { -public: - FeatureIdentifier value; - - friend bool operator==(const IdentifierNotEqualsFilter& lhs, const IdentifierNotEqualsFilter& rhs) { - return lhs.value == rhs.value; - } -}; - -class IdentifierInFilter { -public: - std::vector<FeatureIdentifier> values; - - friend bool operator==(const IdentifierInFilter& lhs, const IdentifierInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - -class IdentifierNotInFilter { -public: - std::vector<FeatureIdentifier> values; - - friend bool operator==(const IdentifierNotInFilter& lhs, const IdentifierNotInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - -class HasIdentifierFilter { + +class Filter { public: - friend bool operator==(const HasIdentifierFilter&, const HasIdentifierFilter&) { - return true; + optional<std::shared_ptr<const expression::Expression>> expression; + + Filter() : expression() {} + + Filter(expression::ParseResult _expression) : expression(std::move(*_expression)) { + assert(!expression || *expression != nullptr); } -}; + + bool operator()(const expression::EvaluationContext& context) const; -class NotHasIdentifierFilter { -public: - friend bool operator==(const NotHasIdentifierFilter&, const NotHasIdentifierFilter&) { - return true; + friend bool operator==(const Filter& lhs, const Filter& rhs) { + if (!lhs.expression || !rhs.expression) { + return lhs.expression == rhs.expression; + } else { + return *(lhs.expression) == *(rhs.expression); + } } -}; -class ExpressionFilter { -public: - std::shared_ptr<const expression::Expression> expression; - - friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { - return *(lhs.expression) == *(rhs.expression); + friend bool operator!=(const Filter& lhs, const Filter& rhs) { + return !(lhs == rhs); } }; - -using FilterBase = variant< - class NullFilter, - class EqualsFilter, - class NotEqualsFilter, - class LessThanFilter, - class LessThanEqualsFilter, - class GreaterThanFilter, - class GreaterThanEqualsFilter, - class InFilter, - class NotInFilter, - class AnyFilter, - class AllFilter, - class NoneFilter, - class HasFilter, - class NotHasFilter, - class TypeEqualsFilter, - class TypeNotEqualsFilter, - class TypeInFilter, - class TypeNotInFilter, - class IdentifierEqualsFilter, - class IdentifierNotEqualsFilter, - class IdentifierInFilter, - class IdentifierNotInFilter, - class HasIdentifierFilter, - class NotHasIdentifierFilter, - class ExpressionFilter>; - -class Filter : public FilterBase { -public: - using FilterBase::FilterBase; - bool operator()(const expression::EvaluationContext& context) const; -}; - } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp deleted file mode 100644 index a4a4098b3f..0000000000 --- a/include/mbgl/style/filter_evaluator.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include <mbgl/style/filter.hpp> -#include <mbgl/util/geometry.hpp> - -#include <type_traits> - -namespace mbgl { -namespace style { - -/* - A visitor that evaluates a `Filter` for a given feature. - - Use via `Filter::operator()`. For example: - - if (filter(feature)) { - // matches the filter - } else { - // does not match - } -*/ -class FilterEvaluator { -public: - const expression::EvaluationContext context; - - bool operator()(const NullFilter&) const; - bool operator()(const EqualsFilter& filter) const; - bool operator()(const NotEqualsFilter& filter) const; - bool operator()(const LessThanFilter& filter) const; - bool operator()(const LessThanEqualsFilter& filter) const; - bool operator()(const GreaterThanFilter& filter) const; - bool operator()(const GreaterThanEqualsFilter& filter) const; - bool operator()(const InFilter& filter) const; - bool operator()(const NotInFilter& filter) const; - bool operator()(const AnyFilter& filter) const; - bool operator()(const AllFilter& filter) const; - bool operator()(const NoneFilter& filter) const; - bool operator()(const HasFilter& filter) const; - bool operator()(const NotHasFilter& filter) const; - bool operator()(const TypeEqualsFilter& filter) const; - bool operator()(const TypeNotEqualsFilter& filter) const; - bool operator()(const TypeInFilter& filter) const; - bool operator()(const TypeNotInFilter& filter) const; - bool operator()(const IdentifierEqualsFilter& filter) const; - bool operator()(const IdentifierNotEqualsFilter& filter) const; - bool operator()(const IdentifierInFilter& filter) const; - bool operator()(const IdentifierNotInFilter& filter) const; - bool operator()(const HasIdentifierFilter&) const; - bool operator()(const NotHasIdentifierFilter&) const; - bool operator()(const ExpressionFilter&) const; - -}; - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index b4a34521a4..65a84d8dc2 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -78,8 +78,9 @@ public: return project_(latLng, worldSize(scale)); } - static Point<double> project(const LatLng& latLng, uint8_t zoom) { - return project_(latLng, std::pow(2.0, zoom)); + //Returns point on tile + static Point<double> project(const LatLng& latLng, int32_t zoom) { + return project_(latLng, 1 << zoom); } static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) { @@ -90,16 +91,7 @@ public: wrapMode }; } - - // Project lat, lon to point in a zoom-dependent world size - static Point<double> project(const LatLng& point, uint8_t zoom, uint16_t tileSize) { - const double t2z = tileSize * std::pow(2, zoom); - Point<double> pt = project_(point, t2z); - // Flip y coordinate - auto x = ::round(std::min(pt.x, t2z)); - auto y = ::round(std::min(t2z - pt.y, t2z)); - return { x, y }; - } + private: static Point<double> project_(const LatLng& latLng, double worldSize) { return Point<double> { diff --git a/mapbox-gl-js b/mapbox-gl-js -Subproject 6b96d69ab54b149db1f6ef06daed37379ac0744 +Subproject a5a8dfe5b90737dbb295eaca7f41c667ae4060a diff --git a/misc/ca-bundle.crt b/misc/ca-bundle.crt index 29691b9733..2ec7884afd 100644 --- a/misc/ca-bundle.crt +++ b/misc/ca-bundle.crt @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Wed Jan 17 00:48:54 2018 GMT +## Certificate data from Mozilla as of: Mon Apr 16 12:40:48 2018 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.27. -## SHA256: a3ac15b98179dd2f3c5de076d10b1d53048754372f7207c2f327510cdd78fbd8 +## SHA256: 704f02707ec6b4c4a7597a8c6039b020def11e64f3ef0605a9c3543d48038a57 ## @@ -446,60 +446,6 @@ EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH llpwrN9M -----END CERTIFICATE----- -Camerfirma Chambers of Commerce Root -==================================== ------BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe -QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i -ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx -NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp -cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn -MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC -AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU -xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH -NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW -DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV -d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud -EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v -cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P -AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh -bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD -VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz -aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi -fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD -L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN -UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n -ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 -erfutGWaIZDgqtCYvDi1czyL+Nw= ------END CERTIFICATE----- - -Camerfirma Global Chambersign Root -================================== ------BEGIN CERTIFICATE----- -MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe -QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i -ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx -NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt -YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg -MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw -ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J -1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O -by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl -6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c -8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ -BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j -aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B -Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj -aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y -ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh -bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA -PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y -gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ -PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 -IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes -t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== ------END CERTIFICATE----- - XRamp Global CA Root ==================== -----BEGIN CERTIFICATE----- @@ -710,30 +656,6 @@ RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ -----END CERTIFICATE----- -DST ACES CA X6 -============== ------BEGIN CERTIFICATE----- -MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG -EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT -MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha -MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE -CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI -DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa -pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow -GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy -MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu -Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy -dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU -CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 -5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t -Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq -nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs -vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 -oKfN5XozNmr6mis= ------END CERTIFICATE----- - SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- @@ -976,27 +898,6 @@ FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= -----END CERTIFICATE----- -Security Communication EV RootCA1 -================================= ------BEGIN CERTIFICATE----- -MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc -U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh -dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE -BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl -Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO -/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX -WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z -ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 -bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK -9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG -SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm -iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG -Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW -mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW -T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 ------END CERTIFICATE----- - OISTE WISeKey Global Root GA CA =============================== -----BEGIN CERTIFICATE----- @@ -2027,36 +1928,6 @@ NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv w9y4AyHqnxbxLFS1 -----END CERTIFICATE----- -CA Disig Root R1 -================ ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw -EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp -ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx -EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp -c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy -3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 -u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 -m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk -CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa -YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 -vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL -LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX -ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is -XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ -04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR -xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B -LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM -CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb -VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 -YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS -ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix -lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N -UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ -a7+h89n07eLw4+1knj0vllJPgFOL ------END CERTIFICATE----- - CA Disig Root R2 ================ -----BEGIN CERTIFICATE----- diff --git a/package.json b/package.json index 977cd2b09c..39f64a3967 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "license": "BSD-2-Clause", "dependencies": { - "nan": "~2.8", + "@mapbox/cmake-node-module": "^1.0.0", "node-pre-gyp": "^0.6.37", "npm-run-all": "^4.0.2" }, diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 246b5828c4..3fbfc32a76 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,38 @@ 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 +- Unknown tokens in URLs are now preserved, rather than replaced with an empty string [#11787](https://github.com/mapbox/mapbox-gl-native/issues/11787) + +## 6.1.2 - May 18, 2018 + - Update telemetry to 3.1.1 [#11942](https://github.com/mapbox/mapbox-gl-native/pull/11942) + +## 6.2.0-alpha.1 - May 17, 2018 + - `"to-string"` expression operator converts `null` to empty string rather than to `"null"` [#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904) + - Expose MapView#setOfflineRegion [#1922](https://github.com/mapbox/mapbox-gl-native/pull/11922) + - Add nullability annotations to public API for kotlin language integration [#11925](https://github.com/mapbox/mapbox-gl-native/pull/11925) + - Expose MapView created callbacks on MapFragment and SupportMapFragment [#11934](https://github.com/mapbox/mapbox-gl-native/pull/11934) + - Update mapbox-java to 3.1.0 [#11916](https://github.com/mapbox/mapbox-gl-native/pull/11916) + +## 6.1.1 - May 7, 2018 + - Update telemetry to 3.1.0 [#11855](https://github.com/mapbox/mapbox-gl-native/pull/11855) + +## 6.1.0 - May 4, 2018 + - Unwrap LatLngBounds during JNI conversion [#11807](https://github.com/mapbox/mapbox-gl-native/pull/11807) + - Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800) + - Null-check source before removing [#11789](https://github.com/mapbox/mapbox-gl-native/pull/11789) + - Flutter support: promote pixel-ratio to public API NativeMapView [#11772](https://github.com/mapbox/mapbox-gl-native/pull/11772) + - Unwrap LatLngBounds for the shortest path when requesting camera [#11759](https://github.com/mapbox/mapbox-gl-native/pull/11759) + - Flutter support: integrate view callback abstraction [#11706](https://github.com/mapbox/mapbox-gl-native/pull/11706) + - Match expression doc tweaks [#11691](https://github.com/mapbox/mapbox-gl-native/pull/11691) + - Improve stop javadoc to include interpolate [#11677](https://github.com/mapbox/mapbox-gl-native/pull/11677) + - Reduce per-frame render CPU time [#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811) + - Add Korean localization [#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792) + - Add Danish localization; update Hungarian, Russian, Swedish translations [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11136) + +## 5.5.3 - May 4, 2018 + - Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800) + ## 6.0.1 - April 17, 2018 - Bump telemetry version to 3.0.2 [#11710](https://github.com/mapbox/mapbox-gl-native/pull/11710) @@ -78,9 +110,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Update javadoc configuration for Gradle 4.4 [#11384](https://github.com/mapbox/mapbox-gl-native/pull/11384) - Rework zoomIn and zoomOut to use ValueAnimators [#11382](https://github.com/mapbox/mapbox-gl-native/pull/11382) - Delete LocalRef when converting Image.java [#11350](https://github.com/mapbox/mapbox-gl-native/pull/11350) - - Use float for pixelratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367) + - Use float for pixelratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367) - Validate width/height when creating a snapshot [#11364](https://github.com/mapbox/mapbox-gl-native/pull/11364) - + ## 6.0.0-beta.3 - March 2, 2018 - Added missing local reference deletes [#11243](https://github.com/mapbox/mapbox-gl-native/pull/11243), [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272) - Remove obsolete camera api [#11201](https://github.com/mapbox/mapbox-gl-native/pull/11201) @@ -103,7 +135,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Add missing DeleteLocalRefs [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272) - Continue loading style even if we mutate it [#11294](https://github.com/mapbox/mapbox-gl-native/pull/11294) - Update telemetry version for OkHttp [#11338](https://github.com/mapbox/mapbox-gl-native/pull/11338) - + ## 6.0.0-beta.2 - February 13, 2018 - Deprecate LocationEngine [#11185](https://github.com/mapbox/mapbox-gl-native/pull/11185) - Remove LOST from SDK [11186](https://github.com/mapbox/mapbox-gl-native/pull/11186) @@ -113,7 +145,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - AddImage performance improvement [#11111](https://github.com/mapbox/mapbox-gl-native/pull/11111) - Migrate MAS to 3.0.0, refactor GeoJson integration [#11149](https://github.com/mapbox/mapbox-gl-native/pull/11149) - Remove @jar and @aar dependency suffixes [#11161](https://github.com/mapbox/mapbox-gl-native/pull/11161) - + ## 5.4.1 - February 9, 2018 - Don't recreate TextureView surface as part of view resizing, solves OOM crashes [#11148](https://github.com/mapbox/mapbox-gl-native/pull/11148) - Don't invoke OnLowMemory before map is ready, solves startup crash on low memory devices [#11109](https://github.com/mapbox/mapbox-gl-native/pull/11109) @@ -148,7 +180,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Custom library loader [#10733](https://github.com/mapbox/mapbox-gl-native/pull/10733) - Inconsistent parameters for LatLngBounds.union [#10728](https://github.com/mapbox/mapbox-gl-native/pull/10728) - Gradle 4.1 / AS 3.0 [#10549](https://github.com/mapbox/mapbox-gl-native/pull/10549) - + ## 5.3.2 - January 22, 2018 - Validate surface creation before destroying [#10890](https://github.com/mapbox/mapbox-gl-native/pull/10890) - Add filesource activation ot OfflineRegion [#10904](https://github.com/mapbox/mapbox-gl-native/pull/10904) @@ -158,7 +190,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Allow changing the used OkHttpClient [#10948](https://github.com/mapbox/mapbox-gl-native/pull/10948) - Validate zoom level before creating telemetry event [#10959](https://github.com/mapbox/mapbox-gl-native/pull/10959) - Handle null call instances in HttpRequest [#10987](https://github.com/mapbox/mapbox-gl-native/pull/10987) - + ## 5.3.1 - January 10, 2018 - Blacklist binary program loading for Vivante GC4000 GPUs [#10862](https://github.com/mapbox/mapbox-gl-native/pull/10862) - Support Genymotion [#10841](https://github.com/mapbox/mapbox-gl-native/pull/10841) @@ -169,11 +201,11 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Allow configuring Http url logging when a request fails [#10830](https://github.com/mapbox/mapbox-gl-native/pull/10830) - Don't send double tap event multiple times for telemetry [#10854](https://github.com/mapbox/mapbox-gl-native/pull/10854) - Fix code generation [#10856](https://github.com/mapbox/mapbox-gl-native/pull/10856) - - Use the correct cancelable callback after posting cancel [#10871](https://github.com/mapbox/mapbox-gl-native/pull/10871) - + - Use the correct cancelable callback after posting cancel [#10871](https://github.com/mapbox/mapbox-gl-native/pull/10871) + ## 5.3.0 - December 20, 2017 - Add support for TinySDF [#10706](https://github.com/mapbox/mapbox-gl-native/pull/10706) - - Save restore MyLocationViewSettings [#10746](https://github.com/mapbox/mapbox-gl-native/pull/10746) + - Save restore MyLocationViewSettings [#10746](https://github.com/mapbox/mapbox-gl-native/pull/10746) - Post animation callback invocation [#10664](https://github.com/mapbox/mapbox-gl-native/pull/10664) - Allow configuring Http logging [#10681](https://github.com/mapbox/mapbox-gl-native/pull/10681) - Fix reverse scale gesture [#10688](https://github.com/mapbox/mapbox-gl-native/pull/10688) diff --git a/platform/android/MapboxGLAndroidSDK/.gitignore b/platform/android/MapboxGLAndroidSDK/.gitignore new file mode 100644 index 0000000000..c24bd2563a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/.gitignore @@ -0,0 +1 @@ +dependency-graph-mapbox-libraries.png diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index f82968051d..21ed25e2c2 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -1,9 +1,13 @@ apply plugin: 'com.android.library' dependencies { - api dependenciesList.mapboxAndroidTelemetry + api (dependenciesList.mapboxAndroidTelemetry) { + exclude group: 'com.android.support', module: 'appcompat-v7' + } api dependenciesList.mapboxJavaGeoJSON - api dependenciesList.mapboxAndroidGestures + api (dependenciesList.mapboxAndroidGestures) { + exclude group: 'com.android.support', module: 'appcompat-v7' + } implementation dependenciesList.supportAnnotations implementation dependenciesList.supportFragmentV4 implementation dependenciesList.timber @@ -87,7 +91,7 @@ android { if (abi != 'all') { abiFilters abi.split(' ') } else { - abiFilters "armeabi", "armeabi-v7a", "mips", "x86", "arm64-v8a", "x86_64" + abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64" } } } @@ -111,7 +115,7 @@ android { } lintOptions { - disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission' + disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission', 'WrongThreadInterprocedural' checkAllWarnings true warningsAsErrors false } @@ -145,3 +149,4 @@ apply from: "${rootDir}/gradle/gradle-javadoc.gradle" apply from: "${rootDir}/gradle/gradle-publish.gradle" apply from: "${rootDir}/gradle/gradle-checkstyle.gradle" apply from: "${rootDir}/gradle/gradle-tests-staticblockremover.gradle" +apply from: "${rootDir}/gradle/gradle-dependencies-graph.gradle" diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index bdfc444454..a49ef47674 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=6.1.0-SNAPSHOT +VERSION_NAME=6.2.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/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index 55494b72d8..c639e49013 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -28,7 +28,7 @@ public class LatLngBounds implements Parcelable { /** * Construct a new LatLngBounds based on its corners, given in NESW * order. - * + * <p> * If eastern longitude is smaller than the western one, bounds will include antimeridian. * For example, if the NE point is (10, -170) and the SW point is (-10, 170), then bounds will span over 20 degrees * and cross the antimeridian. @@ -75,7 +75,6 @@ public class LatLngBounds implements Parcelable { if (longCenter >= GeometryConstants.MAX_LONGITUDE) { longCenter = this.longitudeEast - halfSpan; } - return new LatLng(latCenter, longCenter); } return new LatLng(latCenter, longCenter); @@ -188,7 +187,6 @@ public class LatLngBounds implements Parcelable { return GeometryConstants.LONGITUDE_SPAN - longSpan; } - static double getLongitudeSpan(final double longEast, final double longWest) { double longSpan = Math.abs(longEast - longWest); if (longEast >= longWest) { @@ -240,6 +238,7 @@ public class LatLngBounds implements Parcelable { westLon = temp; } } else { + lonSpan = GeometryConstants.LONGITUDE_SPAN - lonSpan; if (westLon < eastLon) { double temp = eastLon; eastLon = westLon; @@ -278,12 +277,11 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from doubles representing a LatLng pair. - * + * <p> * This values of latNorth and latSouth should be in the range of [-90, 90], * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, * otherwise IllegalArgumentException will be thrown. * latNorth should be greater or equal latSouth, otherwise IllegalArgumentException will be thrown. - * * <p> * This method doesn't recalculate most east or most west boundaries. * Note that lonEast and lonWest will be wrapped to be in the range of [-180, 180], @@ -296,6 +294,20 @@ public class LatLngBounds implements Parcelable { @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latSouth, double lonWest) { + checkParams(latNorth, lonEast, latSouth, lonWest); + + lonEast = LatLng.wrap(lonEast, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + lonWest = LatLng.wrap(lonWest, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + + return new LatLngBounds(latNorth, lonEast, latSouth, lonWest); + } + + private static void checkParams( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latNorth, + double lonEast, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latSouth, + double lonWest) { + if (Double.isNaN(latNorth) || Double.isNaN(latSouth)) { throw new IllegalArgumentException("latitude must not be NaN"); } @@ -316,11 +328,6 @@ public class LatLngBounds implements Parcelable { if (latNorth < latSouth) { throw new IllegalArgumentException("LatSouth cannot be less than latNorth"); } - - lonEast = LatLng.wrap(lonEast, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); - lonWest = LatLng.wrap(lonWest, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); - - return new LatLngBounds(latNorth, lonEast, latSouth, lonWest); } private static double lat_(int z, int y) { @@ -334,14 +341,14 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from a Tile identifier. - * + * <p> * Returned bounds will have latitude in the range of Mercator projection. - * @see GeometryConstants#MIN_MERCATOR_LATITUDE - * @see GeometryConstants#MAX_MERCATOR_LATITUDE * * @param z Tile zoom level. * @param x Tile X coordinate. * @param y Tile Y coordinate. + * @see GeometryConstants#MIN_MERCATOR_LATITUDE + * @see GeometryConstants#MAX_MERCATOR_LATITUDE */ public static LatLngBounds from(int z, int x, int y) { return new LatLngBounds(lat_(z, y), lon_(z, x + 1), lat_(z, y + 1), lon_(z, x)); @@ -397,7 +404,7 @@ public class LatLngBounds implements Parcelable { return (longitude <= eastLon) && (longitude >= westLon); } - return (longitude < eastLon) || (longitude > westLon); + return (longitude <= eastLon) || (longitude >= westLon); } /** @@ -428,36 +435,94 @@ public class LatLngBounds implements Parcelable { * @param bounds LatLngBounds to add * @return LatLngBounds */ - public LatLngBounds union(LatLngBounds bounds) { - return union(bounds.getLatNorth(), bounds.getLonEast(), bounds.getLatSouth(), bounds.getLonWest()); + public @NonNull LatLngBounds union(@NonNull LatLngBounds bounds) { + return unionNoParamCheck(bounds.getLatNorth(), bounds.getLonEast(), bounds.getLatSouth(), bounds.getLonWest()); } /** * Returns a new LatLngBounds that stretches to include another LatLngBounds, * given by corner points. + * <p> + * This values of northLat and southLat should be in the range of [-90, 90], + * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, + * otherwise IllegalArgumentException will be thrown. + * northLat should be greater or equal southLat, otherwise IllegalArgumentException will be thrown. + * <p> + * This method doesn't recalculate most east or most west boundaries. + * Note that eastLon and westLon will be wrapped to be in the range of [-180, 180], + * see {@link GeometryConstants#MIN_LONGITUDE} and {@link GeometryConstants#MAX_LONGITUDE} * - * @param latNorth Northern Latitude - * @param lonEast Eastern Longitude - * @param latSouth Southern Latitude - * @param lonWest Western Longitude - * @return BoundingBox - */ - public LatLngBounds union(final double latNorth, final double lonEast, final double latSouth, final double lonWest) { - double north = (this.latitudeNorth < latNorth) ? latNorth : this.latitudeNorth; - double south = (this.latitudeSouth > latSouth) ? latSouth : this.latitudeSouth; - - if (LatLngSpan.getLongitudeSpan(lonEast, this.longitudeWest) - < LatLngSpan.getLongitudeSpan(this.longitudeEast, lonWest)) { - return new LatLngBounds(north, - lonEast, - south, + * @param northLat Northern Latitude corner point + * @param eastLon Eastern Longitude corner point + * @param southLat Southern Latitude corner point + * @param westLon Western Longitude corner point + * @return LatLngBounds + */ + public @NonNull LatLngBounds union( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE)double northLat, + double eastLon, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double southLat, + double westLon) { + + checkParams(northLat, eastLon, southLat, westLon); + + return unionNoParamCheck(northLat, eastLon, southLat, westLon); + } + + private @NonNull LatLngBounds unionNoParamCheck( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE)double northLat, + double eastLon, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double southLat, + double westLon) { + + northLat = (this.latitudeNorth < northLat) ? northLat : this.latitudeNorth; + southLat = (this.latitudeSouth > southLat) ? southLat : this.latitudeSouth; + + eastLon = LatLng.wrap(eastLon, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + westLon = LatLng.wrap(westLon, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + + // longitudes match + if (this.longitudeEast == eastLon && this.longitudeWest == westLon) { + return new LatLngBounds(northLat, eastLon, southLat, westLon); + } + + boolean eastInThis = containsLongitude(this.longitudeEast, this.longitudeWest, eastLon); + boolean westInThis = containsLongitude(this.longitudeEast, this.longitudeWest, westLon); + boolean thisEastInside = containsLongitude(eastLon, westLon, this.longitudeEast); + boolean thisWestInside = containsLongitude(eastLon, westLon, this.longitudeWest); + + // two intersections on each end - covers entire longitude + if (eastInThis && westInThis && thisEastInside && thisWestInside) { + return new LatLngBounds(northLat, GeometryConstants.MAX_LONGITUDE, southLat, GeometryConstants.MIN_LONGITUDE); + } + + if (eastInThis) { + if (westInThis) { + return new LatLngBounds(northLat, this.longitudeEast, southLat, this.longitudeWest); + } + return new LatLngBounds(northLat, this.longitudeEast, southLat, westLon); + } + + if (thisEastInside) { + if (thisWestInside) { + return new LatLngBounds(northLat, eastLon, southLat, westLon); + } + return new LatLngBounds(northLat, eastLon, southLat, this.longitudeWest); + } + + // bounds do not intersect, find where they will form shortest union + if (LatLngSpan.getLongitudeSpan(eastLon, this.longitudeWest) + < LatLngSpan.getLongitudeSpan(this.longitudeEast, westLon)) { + return new LatLngBounds(northLat, + eastLon, + southLat, this.longitudeWest); } - return new LatLngBounds(north, + return new LatLngBounds(northLat, this.longitudeEast, - south, - lonWest); + southLat, + westLon); } /** @@ -466,32 +531,89 @@ public class LatLngBounds implements Parcelable { * @param box LatLngBounds to intersect with * @return LatLngBounds */ - @Nullable - public LatLngBounds intersect(LatLngBounds box) { - double minLonWest = Math.max(getLonWest(), box.getLonWest()); - double maxLonEast = Math.min(getLonEast(), box.getLonEast()); - if (maxLonEast > minLonWest) { - double minLatSouth = Math.max(getLatSouth(), box.getLatSouth()); - double maxLatNorth = Math.min(getLatNorth(), box.getLatNorth()); - if (maxLatNorth > minLatSouth) { - return new LatLngBounds(maxLatNorth, maxLonEast, minLatSouth, minLonWest); - } - } - return null; + public @Nullable LatLngBounds intersect(@NonNull LatLngBounds box) { + return intersectNoParamCheck(box.getLatNorth(), box.getLonEast(), box.getLatSouth(), box.getLonWest()); } /** * Returns a new LatLngBounds that is the intersection of this with another LatLngBounds + * <p> + * This values of northLat and southLat should be in the range of [-90, 90], + * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, + * otherwise IllegalArgumentException will be thrown. + * northLat should be greater or equal southLat, otherwise IllegalArgumentException will be thrown. + * <p> + * This method doesn't recalculate most east or most west boundaries. + * Note that eastLon and westLon will be wrapped to be in the range of [-180, 180], + * see {@link GeometryConstants#MIN_LONGITUDE} and {@link GeometryConstants#MAX_LONGITUDE} * - * @param northLatitude Northern Longitude - * @param eastLongitude Eastern Latitude - * @param southLatitude Southern Longitude - * @param westLongitude Western Latitude + * @param northLat Northern Latitude corner point + * @param eastLon Eastern Longitude corner point + * @param southLat Southern Latitude corner point + * @param westLon Western Longitude corner point * @return LatLngBounds */ - public LatLngBounds intersect(double northLatitude, double eastLongitude, double southLatitude, - double westLongitude) { - return intersect(new LatLngBounds(northLatitude, eastLongitude, southLatitude, westLongitude)); + public @Nullable LatLngBounds intersect( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE)double northLat, + double eastLon, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double southLat, + double westLon) { + + checkParams(northLat, eastLon, southLat, westLon); + + return intersectNoParamCheck(northLat, eastLon, southLat, westLon); + } + + private @Nullable LatLngBounds intersectNoParamCheck( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE)double northLat, + double eastLon, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double southLat, + double westLon) { + + double maxsouthLat = Math.max(getLatSouth(), Math.min(GeometryConstants.MAX_LATITUDE, southLat)); + double minnorthLat = Math.min(getLatNorth(), Math.max(GeometryConstants.MIN_LATITUDE, northLat)); + if (minnorthLat < maxsouthLat) { + return null; + } + + eastLon = LatLng.wrap(eastLon, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + westLon = LatLng.wrap(westLon, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + + // longitudes match + if (this.longitudeEast == eastLon && this.longitudeWest == westLon) { + return new LatLngBounds(minnorthLat, eastLon, maxsouthLat, westLon); + } + + boolean eastInThis = containsLongitude(this.longitudeEast, this.longitudeWest, eastLon); + boolean westInThis = containsLongitude(this.longitudeEast, this.longitudeWest, westLon); + boolean thisEastInside = containsLongitude(eastLon, westLon, this.longitudeEast); + boolean thisWestInside = containsLongitude(eastLon, westLon, this.longitudeWest); + + // two intersections : find the one that has longest span + if (eastInThis && westInThis && thisEastInside && thisWestInside) { + + if (getLongitudeSpan(eastLon, this.longitudeWest) > getLongitudeSpan(this.longitudeEast, westLon)) { + return new LatLngBounds(minnorthLat, eastLon, maxsouthLat, this.longitudeWest); + } + + return new LatLngBounds(minnorthLat, this.longitudeEast, maxsouthLat, westLon); + } + + if (eastInThis) { + if (westInThis) { + return new LatLngBounds(minnorthLat, eastLon, maxsouthLat, westLon); + } + return new LatLngBounds(minnorthLat, eastLon, maxsouthLat, this.longitudeWest); + } + + if (thisEastInside) { + if (thisWestInside) { + return new LatLngBounds(minnorthLat, this.longitudeEast, maxsouthLat, this.longitudeWest); + } + return new LatLngBounds(minnorthLat, this.longitudeEast, maxsouthLat, westLon); + } + + return null; } /** @@ -520,7 +642,7 @@ public class LatLngBounds implements Parcelable { return (int) ((latitudeNorth + 90) + ((latitudeSouth + 90) * 1000) + ((longitudeEast + 180) * 1000000) - + ((longitudeEast + 180) * 1000000000)); + + ((longitudeWest + 180) * 1000000000)); } /** @@ -548,11 +670,11 @@ public class LatLngBounds implements Parcelable { } private static LatLngBounds readFromParcel(final Parcel in) { - final double lonNorth = in.readDouble(); - final double latEast = in.readDouble(); - final double lonSouth = in.readDouble(); - final double latWest = in.readDouble(); - return new LatLngBounds(lonNorth, latEast, lonSouth, latWest); + final double northLat = in.readDouble(); + final double eastLon = in.readDouble(); + final double southLat = in.readDouble(); + final double westLon = in.readDouble(); + return new LatLngBounds(northLat, eastLon, southLat, westLon); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java index ab1191c0cc..d0e51f941f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java @@ -1,6 +1,5 @@ package com.mapbox.mapboxsdk.http; - import android.content.Context; import android.content.pm.PackageInfo; import android.os.Build; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java index 5fcf206a5a..280877d61a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java @@ -8,7 +8,6 @@ import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import com.mapbox.mapboxsdk.utils.MapFragmentUtils; import java.util.ArrayList; @@ -31,6 +30,7 @@ import java.util.List; public final class MapFragment extends Fragment implements OnMapReadyCallback { private final List<OnMapReadyCallback> mapReadyCallbackList = new ArrayList<>(); + private OnMapViewReadyCallback mapViewReadyCallback; private MapboxMap mapboxMap; private MapView map; @@ -56,6 +56,19 @@ public final class MapFragment extends Fragment implements OnMapReadyCallback { } /** + * Called when the context attaches to this fragment. + * + * @param context the context attaching + */ + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnMapViewReadyCallback) { + mapViewReadyCallback = (OnMapViewReadyCallback) context; + } + } + + /** * Creates the fragment view hierarchy. * * @param inflater Inflater used to inflate content. @@ -75,15 +88,25 @@ public final class MapFragment extends Fragment implements OnMapReadyCallback { * Called when the fragment view hierarchy is created. * * @param view The content view of the fragment - * @param savedInstanceState THe saved instance state of the framgnt + * @param savedInstanceState The saved instance state of the fragment */ @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); map.onCreate(savedInstanceState); map.getMapAsync(this); + + // notify listeners about mapview creation + if (mapViewReadyCallback != null) { + mapViewReadyCallback.onMapViewReady(map); + } } + /** + * Called when the style of the map has successfully loaded. + * + * @param mapboxMap The public api controller of the map + */ @Override public void onMapReady(MapboxMap mapboxMap) { this.mapboxMap = mapboxMap; @@ -170,4 +193,21 @@ public final class MapFragment extends Fragment implements OnMapReadyCallback { onMapReadyCallback.onMapReady(mapboxMap); } } + + /** + * Callback to be invoked when the map fragment has inflated its MapView. + * <p> + * To use this interface the context hosting the fragment must implement this interface. + * That instance will be set as part of Fragment#onAttach(Context context). + * </p> + */ + public interface OnMapViewReadyCallback { + + /** + * Called when the map has been created. + * + * @param mapView The created mapview + */ + void onMapViewReady(MapView mapView); + } } 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 4ecd7c9246..0f19965224 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 @@ -23,7 +23,6 @@ import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; - import com.mapbox.android.gestures.AndroidGesturesManager; import com.mapbox.android.telemetry.AppUserTurnstile; import com.mapbox.android.telemetry.Event; @@ -33,6 +32,8 @@ import com.mapbox.mapboxsdk.BuildConfig; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; +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.maps.renderer.MapRenderer; @@ -40,9 +41,13 @@ 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.OfflineRegionDefinition; +import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.mapboxsdk.utils.BitmapUtils; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -51,9 +56,6 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; @@ -96,28 +98,30 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { @UiThread public MapView(@NonNull Context context) { super(context); - initialise(context, MapboxMapOptions.createFromAttributes(context, null)); + initialize(context, MapboxMapOptions.createFromAttributes(context, null)); } @UiThread public MapView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); - initialise(context, MapboxMapOptions.createFromAttributes(context, attrs)); + initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); } @UiThread public MapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - initialise(context, MapboxMapOptions.createFromAttributes(context, attrs)); + initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); } @UiThread public MapView(@NonNull Context context, @Nullable MapboxMapOptions options) { super(context); - initialise(context, options == null ? MapboxMapOptions.createFromAttributes(context, null) : options); + initialize(context, options == null ? MapboxMapOptions.createFromAttributes(context, null) : options); } - private void initialise(@NonNull final Context context, @NonNull final MapboxMapOptions options) { + @CallSuper + @UiThread + protected void initialize(@NonNull final Context context, @NonNull final MapboxMapOptions options) { if (isInEditMode()) { // in IDE layout editor, just return return; @@ -535,6 +539,35 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { nativeMapView.setStyleUrl(url); } + /** + * Loads a new style from the specified offline region definition and moves the map camera to that region. + * + * @param definition the offline region definition + * @see OfflineRegionDefinition + */ + public void setOfflineRegionDefinition(OfflineRegionDefinition definition) { + if (destroyed) { + return; + } + + OfflineTilePyramidRegionDefinition regionDefinition = (OfflineTilePyramidRegionDefinition) definition; + setStyleUrl(regionDefinition.getStyleURL()); + CameraPosition cameraPosition = new CameraPosition.Builder() + .target(regionDefinition.getBounds().getCenter()) + .zoom(regionDefinition.getMinZoom()) + .build(); + + if (!isMapInitialized()) { + mapboxMapOptions.camera(cameraPosition); + mapboxMapOptions.minZoomPreference(regionDefinition.getMinZoom()); + mapboxMapOptions.maxZoomPreference(regionDefinition.getMaxZoom()); + return; + } + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); + mapboxMap.setMinZoomPreference(regionDefinition.getMinZoom()); + mapboxMap.setMaxZoomPreference(regionDefinition.getMaxZoom()); + } + // // Rendering // @@ -598,10 +631,8 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { * @param listener The callback that's invoked on every frame rendered to the map view. * @see MapView#removeOnMapChangedListener(OnMapChangedListener) */ - public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) { - if (listener != null) { - onMapChangedListeners.add(listener); - } + public void addOnMapChangedListener(@NonNull OnMapChangedListener listener) { + onMapChangedListeners.add(listener); } /** @@ -610,8 +641,8 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { * @param listener The previously added callback to remove. * @see MapView#addOnMapChangedListener(OnMapChangedListener) */ - public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) { - if (listener != null && onMapChangedListeners.contains(listener)) { + public void removeOnMapChangedListener(@NonNull OnMapChangedListener listener) { + if (onMapChangedListeners.contains(listener)) { onMapChangedListeners.remove(listener); } } @@ -622,13 +653,11 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { * @param callback The callback object that will be triggered when the map is ready to be used. */ @UiThread - public void getMapAsync(final OnMapReadyCallback callback) { - if (!mapCallback.isInitialLoad() && callback != null) { + public void getMapAsync(final @NonNull OnMapReadyCallback callback) { + if (!mapCallback.isInitialLoad()) { callback.onMapReady(mapboxMap); } else { - if (callback != null) { - mapCallback.addOnMapReadyCallback(callback); - } + mapCallback.addOnMapReadyCallback(callback); } } 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 5e36dd0f78..aed918cb79 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 @@ -120,7 +120,7 @@ public final class MapboxMap { * * @param outState the bundle to save the state to. */ - void onSaveInstanceState(Bundle outState) { + void onSaveInstanceState(@NonNull Bundle outState) { outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, transform.getCameraPosition()); outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, nativeMapView.getDebug()); outState.putString(MapboxConstants.STATE_STYLE_URL, nativeMapView.getStyleUrl()); @@ -132,7 +132,7 @@ public final class MapboxMap { * * @param savedInstanceState the bundle containing the saved state */ - void onRestoreInstanceState(Bundle savedInstanceState) { + void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { final CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION); uiSettings.onRestoreInstanceState(savedInstanceState); @@ -266,6 +266,7 @@ public final class MapboxMap { * * @return all the layers in the current style */ + @NonNull public List<Layer> getLayers() { return nativeMapView.getLayers(); } @@ -377,6 +378,7 @@ public final class MapboxMap { * * @return all the sources in the current style */ + @NonNull public List<Source> getSources() { return nativeMapView.getSources(); } @@ -463,10 +465,11 @@ public final class MapboxMap { * * @param name the name of the image to remove */ - public void removeImage(String name) { + public void removeImage(@NonNull String name) { nativeMapView.removeImage(name); } + @Nullable public Bitmap getImage(@NonNull String name) { return nativeMapView.getImage(name); } @@ -537,6 +540,7 @@ public final class MapboxMap { * * @return the UiSettings associated with this map */ + @NonNull public UiSettings getUiSettings() { return uiSettings; } @@ -551,6 +555,7 @@ public final class MapboxMap { * * @return the Projection associated with this map */ + @NonNull public Projection getProjection() { return projection; } @@ -564,7 +569,7 @@ public final class MapboxMap { * * @return the global light source */ - @Nullable + @NonNull public Light getLight() { return nativeMapView.getLight(); } @@ -590,6 +595,7 @@ public final class MapboxMap { * * @return The current position of the Camera. */ + @NonNull public final CameraPosition getCameraPosition() { return transform.getCameraPosition(); } @@ -612,7 +618,7 @@ public final class MapboxMap { * * @param update The change that should be applied to the camera. */ - public final void moveCamera(CameraUpdate update) { + public final void moveCamera(@NonNull CameraUpdate update) { moveCamera(update, null); } @@ -624,7 +630,8 @@ public final class MapboxMap { * @param update The change that should be applied to the camera * @param callback the callback to be invoked when an animation finishes or is canceled */ - public final void moveCamera(final CameraUpdate update, final MapboxMap.CancelableCallback callback) { + public final void moveCamera(@NonNull final CameraUpdate update, + @Nullable final MapboxMap.CancelableCallback callback) { transform.moveCamera(MapboxMap.this, update, callback); } @@ -650,7 +657,7 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void easeCamera(CameraUpdate update, int durationMs) { + public final void easeCamera(@NonNull CameraUpdate update, int durationMs) { easeCamera(update, durationMs, null); } @@ -673,7 +680,8 @@ public final class MapboxMap { * Do not update or ease the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) { + public final void easeCamera(@NonNull CameraUpdate update, int durationMs, + @Nullable final MapboxMap.CancelableCallback callback) { easeCamera(update, durationMs, true, callback); } @@ -691,7 +699,7 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @param easingInterpolator True for easing interpolator, false for linear. */ - public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator) { + public final void easeCamera(@NonNull CameraUpdate update, int durationMs, boolean easingInterpolator) { easeCamera(update, durationMs, easingInterpolator, null); } @@ -711,8 +719,10 @@ public final class MapboxMap { * by a later camera movement or a user gesture, onCancel() will be called. * Do not update or ease the camera from within onCancel(). */ - public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, - final MapboxMap.CancelableCallback callback) { + public final void easeCamera(@NonNull final CameraUpdate update, + final int durationMs, + final boolean easingInterpolator, + @Nullable final MapboxMap.CancelableCallback callback) { easeCamera(update, durationMs, easingInterpolator, callback, false); } @@ -733,8 +743,9 @@ public final class MapboxMap { * Do not update or ease the camera from within onCancel(). * @param isDismissable true will allow animated camera changes dismiss a tracking mode. */ - public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, - final MapboxMap.CancelableCallback callback, final boolean isDismissable) { + public final void easeCamera(@NonNull final CameraUpdate update, final int durationMs, + final boolean easingInterpolator, @Nullable final MapboxMap.CancelableCallback callback, + final boolean isDismissable) { if (durationMs <= 0) { throw new IllegalArgumentException("Null duration passed into easeCamera"); @@ -751,7 +762,7 @@ public final class MapboxMap { * @param update The change that should be applied to the camera. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void animateCamera(CameraUpdate update) { + public final void animateCamera(@NonNull CameraUpdate update) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, null); } @@ -767,7 +778,7 @@ public final class MapboxMap { * called. Do not update or animate the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) { + public final void animateCamera(@NonNull CameraUpdate update, @Nullable MapboxMap.CancelableCallback callback) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, callback); } @@ -782,7 +793,7 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void animateCamera(CameraUpdate update, int durationMs) { + public final void animateCamera(@NonNull CameraUpdate update, int durationMs) { animateCamera(update, durationMs, null); } @@ -804,8 +815,8 @@ public final class MapboxMap { * isn't required, leave it as null. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void animateCamera(final CameraUpdate update, final int durationMs, - final MapboxMap.CancelableCallback callback) { + public final void animateCamera(@NonNull final CameraUpdate update, final int durationMs, + @Nullable final MapboxMap.CancelableCallback callback) { if (durationMs <= 0) { throw new IllegalArgumentException("Null duration passed into animageCamera"); } @@ -1069,6 +1080,7 @@ public final class MapboxMap { * * @return The json of the map style */ + @NonNull public String getStyleJson() { return nativeMapView.getStyleJson(); } @@ -1232,7 +1244,7 @@ public final class MapboxMap { * * @param polyline An updated polyline object. */ - public void updatePolyline(Polyline polyline) { + public void updatePolyline(@NonNull Polyline polyline) { annotationManager.updatePolyline(polyline); } @@ -1263,7 +1275,7 @@ public final class MapboxMap { * * @param polygon An updated polygon object */ - public void updatePolygon(Polygon polygon) { + public void updatePolygon(@NonNull Polygon polygon) { annotationManager.updatePolygon(polygon); } @@ -1468,6 +1480,7 @@ public final class MapboxMap { * * @return The currently selected marker. */ + @NonNull public List<Marker> getSelectedMarkers() { return annotationManager.getSelectedMarkers(); } @@ -1477,6 +1490,7 @@ public final class MapboxMap { * * @return the associated MarkerViewManager */ + @NonNull public MarkerViewManager getMarkerViewManager() { return annotationManager.getMarkerViewManager(); } @@ -1550,7 +1564,8 @@ public final class MapboxMap { * @param padding the padding to apply to the bounds * @return the camera position that fits the bounds and padding */ - public CameraPosition getCameraForLatLngBounds(@Nullable LatLngBounds latLngBounds, int[] padding) { + @NonNull + public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, int[] padding) { // get padded camera position from LatLngBounds return nativeMapView.getCameraForLatLngBounds(latLngBounds, padding); } @@ -1563,6 +1578,7 @@ public final class MapboxMap { * @param padding the padding to apply to the bounds * @return the camera position that fits the bounds and padding */ + @NonNull public CameraPosition getCameraForGeometry(Geometry geometry, double bearing, int[] padding) { // get padded camera position from Geometry return nativeMapView.getCameraForGeometry(geometry, bearing, padding); @@ -1591,11 +1607,7 @@ public final class MapboxMap { * @param bottom The bottom margin in pixels. */ public void setPadding(int left, int top, int right, int bottom) { - setPadding(new int[] {left, top, right, bottom}); - } - - private void setPadding(int[] padding) { - projection.setContentPadding(padding); + projection.setContentPadding(new int[] {left, top, right, bottom}); uiSettings.invalidate(); } @@ -1604,6 +1616,7 @@ public final class MapboxMap { * * @return An array with length 4 in the LTRB order. */ + @NonNull public int[] getPadding() { return projection.getContentPadding(); } @@ -1755,6 +1768,7 @@ public final class MapboxMap { } // used by MapView + @Nullable OnFpsChangedListener getOnFpsChangedListener() { return onFpsChangedListener; } @@ -1902,7 +1916,7 @@ public final class MapboxMap { * will be added to the passed gestures manager. * @see <a href="https://github.com/mapbox/mapbox-gestures-android">mapbox-gestures-android library</a> */ - public void setGesturesManager(AndroidGesturesManager androidGesturesManager, boolean attachDefaultListeners, + public void setGesturesManager(@NonNull AndroidGesturesManager androidGesturesManager, boolean attachDefaultListeners, boolean setDefaultMutuallyExclusives) { onGesturesManagerInteractionListener.setGesturesManager( androidGesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives); @@ -1914,6 +1928,7 @@ public final class MapboxMap { * * @return Current gestures manager. */ + @NonNull public AndroidGesturesManager getGesturesManager() { return onGesturesManagerInteractionListener.getGesturesManager(); } @@ -2000,6 +2015,7 @@ public final class MapboxMap { * * @return Current active InfoWindow Click Listener */ + @Nullable public OnInfoWindowClickListener getOnInfoWindowClickListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowClickListener(); } @@ -2020,6 +2036,7 @@ public final class MapboxMap { * * @return Current active InfoWindow long Click Listener */ + @Nullable public OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowLongClickListener(); } @@ -2038,6 +2055,7 @@ public final class MapboxMap { * * @return Current active InfoWindow Close Listener */ + @Nullable public OnInfoWindowCloseListener getOnInfoWindowCloseListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowCloseListener(); } @@ -2147,11 +2165,11 @@ public final class MapboxMap { * @see MapboxMap#addOnMoveListener(OnMoveListener) */ public interface OnMoveListener { - void onMoveBegin(MoveGestureDetector detector); + void onMoveBegin(@NonNull MoveGestureDetector detector); - void onMove(MoveGestureDetector detector); + void onMove(@NonNull MoveGestureDetector detector); - void onMoveEnd(MoveGestureDetector detector); + void onMoveEnd(@NonNull MoveGestureDetector detector); } /** @@ -2160,11 +2178,11 @@ public final class MapboxMap { * @see MapboxMap#addOnRotateListener(OnRotateListener) */ public interface OnRotateListener { - void onRotateBegin(RotateGestureDetector detector); + void onRotateBegin(@NonNull RotateGestureDetector detector); - void onRotate(RotateGestureDetector detector); + void onRotate(@NonNull RotateGestureDetector detector); - void onRotateEnd(RotateGestureDetector detector); + void onRotateEnd(@NonNull RotateGestureDetector detector); } /** @@ -2173,11 +2191,11 @@ public final class MapboxMap { * @see MapboxMap#addOnScaleListener(OnScaleListener) */ public interface OnScaleListener { - void onScaleBegin(StandardScaleGestureDetector detector); + void onScaleBegin(@NonNull StandardScaleGestureDetector detector); - void onScale(StandardScaleGestureDetector detector); + void onScale(@NonNull StandardScaleGestureDetector detector); - void onScaleEnd(StandardScaleGestureDetector detector); + void onScaleEnd(@NonNull StandardScaleGestureDetector detector); } /** @@ -2186,11 +2204,11 @@ public final class MapboxMap { * @see MapboxMap#addOnShoveListener(OnShoveListener) */ public interface OnShoveListener { - void onShoveBegin(ShoveGestureDetector detector); + void onShoveBegin(@NonNull ShoveGestureDetector detector); - void onShove(ShoveGestureDetector detector); + void onShove(@NonNull ShoveGestureDetector detector); - void onShoveEnd(ShoveGestureDetector detector); + void onShoveEnd(@NonNull ShoveGestureDetector detector); } /** @@ -2442,7 +2460,7 @@ public final class MapboxMap { * * @param marker The marker were the info window is attached to */ - void onInfoWindowLongClick(Marker marker); + void onInfoWindowLongClick(@NonNull Marker marker); } /** @@ -2457,7 +2475,7 @@ public final class MapboxMap { * * @param marker The marker of the info window that was closed. */ - void onInfoWindowClose(Marker marker); + void onInfoWindowClose(@NonNull Marker marker); } /** @@ -2637,7 +2655,7 @@ public final class MapboxMap { * * @param snapshot the snapshot bitmap */ - void onSnapshotReady(Bitmap snapshot); + void onSnapshotReady(@NonNull Bitmap snapshot); } /** @@ -2649,12 +2667,13 @@ public final class MapboxMap { * * @param style the style that has been loaded */ - void onStyleLoaded(String style); + void onStyleLoaded(@NonNull String style); } // // Used for instrumentation testing // + @NonNull Transform getTransform() { return transform; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index b2d7af7687..d258064908 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -759,9 +759,13 @@ final class NativeMapView { return null; } Source source = getSource(sourceId); - return removeSource(source); + if (source != null) { + return removeSource(source); + } + return null; } + @Nullable public Source removeSource(@NonNull Source source) { if (isDestroyedOn("removeSource")) { return null; @@ -775,8 +779,9 @@ final class NativeMapView { return; } - // Determine pixel ratio - nativeAddImage(name, image, image.getDensity() / DisplayMetrics.DENSITY_DEFAULT); + // Determine pixel ratio, cast to float to avoid rounding, see mapbox-gl-native/issues/11809 + float pixelRatio = (float) image.getDensity() / DisplayMetrics.DENSITY_DEFAULT; + nativeAddImage(name, image, pixelRatio); } public void addImages(@NonNull HashMap<String, Bitmap> bitmapHashMap) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index f35355533d..d5166c17b0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -43,14 +43,16 @@ public class Projection { /** * Returns the spherical Mercator projected meters for a LatLng. */ - public ProjectedMeters getProjectedMetersForLatLng(LatLng latLng) { + @NonNull + public ProjectedMeters getProjectedMetersForLatLng(@NonNull LatLng latLng) { return nativeMapView.projectedMetersForLatLng(latLng); } /** * Returns the LatLng for a spherical Mercator projected meters. */ - public LatLng getLatLngForProjectedMeters(ProjectedMeters projectedMeters) { + @NonNull + public LatLng getLatLngForProjectedMeters(@NonNull ProjectedMeters projectedMeters) { return nativeMapView.latLngForProjectedMeters(projectedMeters); } @@ -77,7 +79,8 @@ public class Projection { * @return The LatLng corresponding to the point on the screen, or null if the ray through * the given screen point does not intersect the ground plane. */ - public LatLng fromScreenLocation(PointF point) { + @NonNull + public LatLng fromScreenLocation(@NonNull PointF point) { return nativeMapView.latLngForPixel(point); } @@ -87,6 +90,7 @@ public class Projection { * * @return The projection of the viewing frustum in its current state. */ + @NonNull public VisibleRegion getVisibleRegion() { float left = 0; float right = nativeMapView.getWidth(); @@ -151,7 +155,8 @@ public class Projection { * @param location A LatLng on the map to convert to a screen location. * @return A Point representing the screen location in screen pixels. */ - public PointF toScreenLocation(LatLng location) { + @NonNull + public PointF toScreenLocation(@NonNull LatLng location) { return nativeMapView.pixelForLatLng(location); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java index 8aa4c7fd09..307b33b0c7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java @@ -31,6 +31,7 @@ import java.util.List; public class SupportMapFragment extends Fragment implements OnMapReadyCallback { private final List<OnMapReadyCallback> mapReadyCallbackList = new ArrayList<>(); + private MapFragment.OnMapViewReadyCallback mapViewReadyCallback; private MapboxMap mapboxMap; private MapView map; @@ -56,6 +57,19 @@ public class SupportMapFragment extends Fragment implements OnMapReadyCallback { } /** + * Called when the context attaches to this fragment. + * + * @param context the context attaching + */ + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof MapFragment.OnMapViewReadyCallback) { + mapViewReadyCallback = (MapFragment.OnMapViewReadyCallback) context; + } + } + + /** * Creates the fragment view hierarchy. * * @param inflater Inflater used to inflate content. @@ -82,6 +96,11 @@ public class SupportMapFragment extends Fragment implements OnMapReadyCallback { super.onViewCreated(view, savedInstanceState); map.onCreate(savedInstanceState); map.getMapAsync(this); + + // notify listeners about MapView creation + if (mapViewReadyCallback != null) { + mapViewReadyCallback.onMapViewReady(map); + } } @Override diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java index c1daebbe52..100787fbf0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java @@ -332,7 +332,7 @@ public final class UiSettings { * * @param compass the drawable to show as image compass */ - public void setCompassImage(Drawable compass) { + public void setCompassImage(@NonNull Drawable compass) { compassView.setCompassImage(compass); } @@ -409,6 +409,7 @@ public final class UiSettings { * * @return the drawable used as compass image */ + @NonNull public Drawable getCompassImage() { return compassView.getCompassImage(); } @@ -544,7 +545,7 @@ public final class UiSettings { * * @param attributionDialogManager the manager class used for showing attribution */ - public void setAttributionDialogManager(AttributionDialogManager attributionDialogManager) { + public void setAttributionDialogManager(@NonNull AttributionDialogManager attributionDialogManager) { this.attributionDialogManager = attributionDialogManager; } @@ -553,6 +554,7 @@ public final class UiSettings { * * @return the active manager class used for showing attribution */ + @NonNull public AttributionDialogManager getAttributionDialogManager() { return attributionDialogManager; } 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 f1c70325a0..fcee5bd179 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 @@ -37,11 +37,11 @@ public abstract class MapRenderer implements MapRendererScheduler { } public void onPause() { - nativeOnPause(); + // Implement if needed } public void onResume() { - nativeOnResume(); + // Implement if needed } public void onStop() { @@ -124,10 +124,6 @@ public abstract class MapRenderer implements MapRendererScheduler { private native void nativeRender(); - private native void nativeOnResume(); - - private native void nativeOnPause(); - private long frames; private long timeElapsed; 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 f0cb8d973a..929e4b4279 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 @@ -6,6 +6,8 @@ import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.os.Environment; import android.support.annotation.NonNull; +import android.support.annotation.UiThread; + import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.constants.MapboxConstants; import timber.log.Timber; @@ -43,6 +45,7 @@ public class FileSource { * @param context the context to derive the cache path from * @return the single instance of FileSource */ + @UiThread public static synchronized FileSource getInstance(Context context) { if (INSTANCE == null) { String cachePath = getCachePath(context); @@ -122,6 +125,8 @@ public class FileSource { initialize(Mapbox.getAccessToken(), cachePath, assetManager); } + public native boolean isActivated(); + public native void activate(); public native void deactivate(); 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 4289deeda3..1dd8eddab9 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 @@ -166,7 +166,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a String value * @return property wrapper around String @@ -176,7 +176,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement @@ -356,7 +356,7 @@ public class PropertyFactory { } /** - * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. + * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a Float[] value * @return property wrapper around Float[] @@ -366,7 +366,7 @@ public class PropertyFactory { } /** - * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. + * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement @@ -376,7 +376,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a String value * @return property wrapper around String @@ -386,7 +386,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement @@ -1156,7 +1156,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a String value * @return property wrapper around String @@ -1166,7 +1166,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement @@ -1536,7 +1536,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a String value * @return property wrapper around String @@ -1546,7 +1546,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ko/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..a292e52517 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ko/strings.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="mapbox_compassContentDescription">지도 나침반. 지도회전를 북쪽으로 재설정합니다.</string> + <string name="mapbox_attributionsIconContentDescription">속성 정보. 속성 대화를 표시합니다.</string> + <string name="mapbox_myLocationViewContentDescription">로케이션 뷰. 지도에서 현재 위치를 보여줍니다.</string> + <string name="mapbox_mapActionDescription">맵박스로 생성된 지도 표시. 두 손가락으로 드래그하여 화면을 위 아래로 움직이세요. 두 손가락을 이용해 화면을 확대 축소 하세요.</string> + <string name="mapbox_attributionsDialogTitle">안드로이드를 위한 맵박스 맵 SDK</string> + <string name="mapbox_attributionTelemetryTitle">더 나은 맵박스 지도 만들기</string> + <string name="mapbox_attributionTelemetryMessage">당신은 익명의 사용 데이터를 제공함으로써, 오픈스트리트맵과 맵박스 향상에 기여하고 있습니다.</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">제공된 오프라인지역정의가 월드바운즈에 적합하지 않습니다: %s</string> + <string name="mapbox_telemetrySettings">원격 측정 설정</string> + </resources> diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index e072f07fb9..789a1b2b37 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -357,6 +357,58 @@ public class LatLngBoundsTest { } @Test + public void intersectNorthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("latitude must be between -90 and 90"); + LatLngBounds intersectLatLngBounds = + LatLngBounds.from(10, 10, 0, 0) + .intersect(200, 200, 0, 0); + } + + @Test + public void intersectSouthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("latitude must be between -90 and 90"); + LatLngBounds intersectLatLngBounds = + LatLngBounds.from(0, 0, -10, -10) + .intersect(0, 0, -200, -200); + } + + @Test + public void intersectSouthLessThanNorthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("LatSouth cannot be less than latNorth"); + + LatLngBounds intersectLatLngBounds = + LatLngBounds.from(10, 10, 0, 0) + .intersect(0, 200, 20, 0); + } + + + @Test + public void intersectEastWrapCheck() { + + LatLngBounds latLngBounds1 = LatLngBounds.from(10, -150, 0, 0); + LatLngBounds latLngBounds2 = LatLngBounds.from(90, 200, 0, 0); + + LatLngBounds intersectLatLngBounds = LatLngBounds.from(10, -160, 0, 0); + + assertEquals(latLngBounds1.intersect(latLngBounds2), intersectLatLngBounds); + assertEquals(latLngBounds2.intersect(latLngBounds1), intersectLatLngBounds); + } + + @Test + public void intersectWestWrapCheck() { + LatLngBounds latLngBounds1 = LatLngBounds.from(0, 0, -10, 150); + LatLngBounds latLngBounds2 = LatLngBounds.from(0, 0, -90, -200); + + LatLngBounds intersectLatLngBounds = LatLngBounds.from(0, 0, -10, 160); + + assertEquals(latLngBounds1.intersect(latLngBounds2), intersectLatLngBounds); + assertEquals(latLngBounds2.intersect(latLngBounds1), intersectLatLngBounds); + } + + @Test public void innerUnion() { LatLngBounds latLngBounds = new LatLngBounds.Builder() .include(new LatLng(1, 1)) @@ -391,14 +443,176 @@ public class LatLngBoundsTest { .include(new LatLng(-10, -160)) .build(); - assertEquals("outer union should match", - latLngBounds1.union(latLngBounds2), + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, + new LatLngBounds.Builder() + .include(new LatLng(10, 160)) + .include(new LatLng(-10, -160)) + .build()); + + assertEquals(union1, union2); + } + + @Test + public void unionOverDateLine2() { + LatLngBounds latLngBounds1 = new LatLngBounds.Builder() + .include(new LatLng(10, 170)) + .include(new LatLng(0, 160)) + .build(); + + LatLngBounds latLngBounds2 = new LatLngBounds.Builder() + .include(new LatLng(0, 165)) + .include(new LatLng(-10, -160)) + .build(); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, new LatLngBounds.Builder() .include(new LatLng(10, 160)) .include(new LatLng(-10, -160)) .build()); + + assertEquals(union1, union2); } + @Test + public void unionOverDateLine3() { + LatLngBounds latLngBounds1 = new LatLngBounds.Builder() + .include(new LatLng(10, -165)) + .include(new LatLng(0, 160)) + .build(); + + LatLngBounds latLngBounds2 = new LatLngBounds.Builder() + .include(new LatLng(0, -170)) + .include(new LatLng(-10, -160)) + .build(); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, + new LatLngBounds.Builder() + .include(new LatLng(10, 160)) + .include(new LatLng(-10, -160)) + .build()); + + assertEquals(union1, union2); + } + + @Test + public void unionOverDateLine4() { + LatLngBounds latLngBounds1 = new LatLngBounds.Builder() + .include(new LatLng(10, -160)) + .include(new LatLng(0, 160)) + .build(); + + LatLngBounds latLngBounds2 = new LatLngBounds.Builder() + .include(new LatLng(0, -170)) + .include(new LatLng(-10, -175)) + .build(); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, + new LatLngBounds.Builder() + .include(new LatLng(10, 160)) + .include(new LatLng(-10, -160)) + .build()); + + assertEquals(union1, union2); + } + + @Test + public void unionOverDateLine5() { + LatLngBounds latLngBounds1 = new LatLngBounds.Builder() + .include(new LatLng(10, -160)) + .include(new LatLng(0, 160)) + .build(); + + LatLngBounds latLngBounds2 = new LatLngBounds.Builder() + .include(new LatLng(0, 170)) + .include(new LatLng(-10, 175)) + .build(); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, + new LatLngBounds.Builder() + .include(new LatLng(10, 160)) + .include(new LatLng(-10, -160)) + .build()); + + assertEquals(union1, union2); + } + + @Test + public void unionOverDateLineReturnWorldLonSpan() { + LatLngBounds latLngBounds1 = LatLngBounds.from(10, -160, -10, -10); + LatLngBounds latLngBounds2 = LatLngBounds.from(10, 10, -10, 160); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, union2); + assertEquals(union1, LatLngBounds.from(10, 180, -10, -180)); + } + + @Test + public void unionNorthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("latitude must be between -90 and 90"); + LatLngBounds unionLatLngBounds = + LatLngBounds.from(10, 10, 0, 0) + .union(200, 200, 0, 0); + } + + @Test + public void unionSouthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("latitude must be between -90 and 90"); + LatLngBounds unionLatLngBounds = + LatLngBounds.from(0, 0, -10, -10) + .union(0, 0, -200, -200); + } + + @Test + public void unionSouthLessThanNorthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("LatSouth cannot be less than latNorth"); + + LatLngBounds unionLatLngBounds = + LatLngBounds.from(10, 10, 0, 0) + .union(0, 200, 20, 0); + } + + + @Test + public void unionEastWrapCheck() { + + LatLngBounds latLngBounds1 = LatLngBounds.from(10, 10, 0, 0); + LatLngBounds latLngBounds2 = LatLngBounds.from(90, 200, 0, 0); + LatLngBounds unionLatLngBounds = LatLngBounds.from(90, -160, 0, 0); + + assertEquals(latLngBounds1.union(latLngBounds2), unionLatLngBounds); + assertEquals(latLngBounds2.union(latLngBounds1), unionLatLngBounds); + } + + @Test + public void unionWestWrapCheck() { + LatLngBounds latLngBounds1 = LatLngBounds.from(0, 0, -10, -10); + LatLngBounds latLngBounds2 = LatLngBounds.from(0, 0, -90, -200); + + LatLngBounds unionLatLngBounds = LatLngBounds.from(0, 0, -90, 160); + + assertEquals(latLngBounds1.union(latLngBounds2), unionLatLngBounds); + assertEquals(latLngBounds2.union(latLngBounds1), unionLatLngBounds); + } @Test public void northWest() { @@ -524,7 +738,6 @@ public class LatLngBoundsTest { LatLngBounds.from(0, Double.POSITIVE_INFINITY, -20, -20); } - @Test public void testConstructorChecksSouthLatitudeNaN() { exception.expect(IllegalArgumentException.class); @@ -543,7 +756,7 @@ public class LatLngBoundsTest { public void testConstructorChecksSouthLatitudeGreaterThan90() { exception.expect(IllegalArgumentException.class); exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds.from(20, 20,95, 0); + LatLngBounds.from(20, 20, 95, 0); } @Test diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 30e989c79b..80be26d3ae 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -41,6 +41,7 @@ android { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.debug } } @@ -72,6 +73,7 @@ dependencies { androidTestImplementation dependenciesList.testRules androidTestImplementation dependenciesList.testEspressoCore androidTestImplementation dependenciesList.testEspressoIntents + androidTestImplementation dependenciesList.testEspressoContrib } apply from: "${rootDir}/gradle/gradle-make.gradle" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java index 7a1fcbf5f3..89397c30eb 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java @@ -16,17 +16,17 @@ public class OrientationTest extends BaseActivityTest { @Test public void testChangeDeviceOrientation() { onView(isRoot()).perform(orientationLandscape()); - waitLoop(2200); + waitAction(2200); onView(isRoot()).perform(orientationPortrait()); - waitLoop(2500); + waitAction(2500); onView(isRoot()).perform(orientationLandscapeReverse()); - waitLoop(500); + waitAction(500); onView(isRoot()).perform(orientationPortraitReverse()); - waitLoop(1250); + waitAction(1250); onView(isRoot()).perform(orientationLandscape()); - waitLoop(750); + waitAction(750); onView(isRoot()).perform(orientationPortrait()); - waitLoop(950); + waitAction(950); onView(isRoot()).perform(orientationLandscapeReverse()); onView(isRoot()).perform(orientationPortraitReverse()); onView(isRoot()).perform(orientationLandscape()); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java new file mode 100644 index 0000000000..26a3a2e4ab --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java @@ -0,0 +1,39 @@ +package com.mapbox.mapboxsdk.testapp.action; + +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.view.View; + +import org.hamcrest.Matcher; + +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; + +public final class WaitAction implements ViewAction { + + private static final long DEFAULT_LOOP_TIME = 375; + private final long loopTime; + + public WaitAction() { + this(DEFAULT_LOOP_TIME); + } + + public WaitAction(long loopTime) { + this.loopTime = loopTime; + } + + @Override + public Matcher<View> getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return getClass().getSimpleName(); + } + + @Override + public void perform(UiController uiController, View view) { + uiController.loopMainThreadForAtLeast(loopTime); + } +} + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java index 3f32443021..6d90c20a46 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java @@ -6,18 +6,19 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.support.test.espresso.Espresso; import android.support.test.espresso.IdlingResourceTimeoutException; -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; import android.support.test.rule.ActivityTestRule; -import android.view.View; + import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.action.WaitAction; import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource; + import junit.framework.Assert; -import org.hamcrest.Matcher; + import org.junit.After; import org.junit.Before; import org.junit.Rule; + import timber.log.Timber; import static android.support.test.espresso.Espresso.onView; @@ -67,12 +68,12 @@ public abstract class BaseActivityTest { onView(withId(id)).check(matches(isDisplayed())); } - protected void waitLoop() { - waitLoop(500); + protected void waitAction() { + waitAction(500); } - protected void waitLoop(long waitTime) { - onView(withId(R.id.mapView)).perform(new LoopAction(waitTime)); + protected void waitAction(long waitTime) { + onView(withId(R.id.mapView)).perform(new WaitAction(waitTime)); } static boolean isConnected(Context context) { @@ -87,29 +88,5 @@ public abstract class BaseActivityTest { Timber.e("@After test: unregister idle resource"); Espresso.unregisterIdlingResources(idlingResource); } - - private class LoopAction implements ViewAction { - - private long loopTime; - - public LoopAction(long loopTime) { - this.loopTime = loopTime; - } - - @Override - public Matcher<View> getConstraints() { - return isDisplayed(); - } - - @Override - public String getDescription() { - return getClass().getSimpleName(); - } - - @Override - public void perform(UiController uiController, View view) { - uiController.loopMainThreadForAtLeast(loopTime); - } - } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java index 2a510b4dc5..26aee2de98 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java @@ -62,7 +62,7 @@ public class CompassViewTest extends BaseActivityTest { .build() ))); onView(withId(R.id.compassView)).perform(click()); - waitLoop(); + waitAction(); onView(withId(R.id.compassView)).check(matches(not(isDisplayed()))); invoke(mapboxMap, (uiController, mapboxMap) -> { CameraPosition cameraPosition = mapboxMap.getCameraPosition(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java new file mode 100644 index 0000000000..554bc988a6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java @@ -0,0 +1,129 @@ +package com.mapbox.mapboxsdk.testapp.storage; + +import android.os.Looper; +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; + +import com.mapbox.mapboxsdk.storage.FileSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.action.WaitAction; +import com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity; + +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.Espresso.pressBack; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.isRoot; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationLandscape; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class FileSourceTest { + + @Rule + public ActivityTestRule<FeatureOverviewActivity> rule = new ActivityTestRule<>(FeatureOverviewActivity.class); + + private FileSource fileSource; + + @Before + public void setUp() throws Exception { + onView(withId(R.id.recyclerView)).perform(new FileSourceCreator()); + } + + @Test + public void testDefault() throws Exception { + assertFalse("FileSource should not be active", fileSource.isActivated()); + } + + @Test + public void testActivateDeactivate() throws Exception { + assertFalse("1) FileSource should not be active", fileSource.isActivated()); + onView(withId(R.id.recyclerView)).perform(new FileSourceActivator(true)); + assertTrue("2) FileSource should be active", fileSource.isActivated()); + onView(withId(R.id.recyclerView)).perform(new FileSourceActivator(false)); + assertFalse("3) FileSource should not be active", fileSource.isActivated()); + } + + @Test + public void testOpenCloseMapView() throws Exception { + assertFalse("1) FileSource should not be active", fileSource.isActivated()); + onView(withText("Simple Map")).perform(click()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + assertTrue("2) FileSource should be active", fileSource.isActivated()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + pressBack(); + assertFalse("3) FileSource should not be active", fileSource.isActivated()); + } + + @Test + public void testRotateMapView() throws Exception { + assertFalse("1) FileSource should not be active", fileSource.isActivated()); + onView(withText("Simple Map")).perform(click()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + onView(isRoot()).perform(orientationLandscape()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + assertTrue("2) FileSource should be active", fileSource.isActivated()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + pressBack(); + assertFalse("3) FileSource should not be active", fileSource.isActivated()); + } + + private class FileSourceCreator implements ViewAction { + @Override + public Matcher<View> getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return "Creates the filesource instance on the UI thread"; + } + + @Override + public void perform(UiController uiController, View view) { + assertTrue(Looper.myLooper() == Looper.getMainLooper()); + fileSource = FileSource.getInstance(rule.getActivity()); + } + } + + private class FileSourceActivator implements ViewAction { + + private boolean activate; + + FileSourceActivator(boolean activate) { + this.activate = activate; + } + + @Override + public Matcher<View> getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return "Creates the filesource instance on the UI thread"; + } + + @Override + public void perform(UiController uiController, View view) { + assertTrue(Looper.myLooper() == Looper.getMainLooper()); + if (activate) { + fileSource.activate(); + } else { + fileSource.deactivate(); + } + } + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java index 2d16291832..2a8ac36507 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java @@ -160,4 +160,4 @@ public class BackgroundLayerTest extends BaseActivityTest { assertEquals((Float) layer.getBackgroundOpacity().getValue(), (Float) 0.3f); }); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java index a851fde6dd..101d22a531 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java @@ -518,4 +518,4 @@ public class CircleLayerTest extends BaseActivityTest { }); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java index f22956072d..84b3e7bd68 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java @@ -354,4 +354,4 @@ public class FillExtrusionLayerTest extends BaseActivityTest { }); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java index bdbd8958d2..3e1cbf666d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java @@ -353,4 +353,4 @@ public class FillLayerTest extends BaseActivityTest { assertEquals((String) layer.getFillPattern().getValue(), (String) "pedestrian-polygon"); }); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java index 549f309e4f..3a81786df4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java @@ -237,4 +237,4 @@ public class HeatmapLayerTest extends BaseActivityTest { assertEquals((Float) layer.getHeatmapOpacity().getValue(), (Float) 0.3f); }); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java index 1fdc6d6dab..e0121a704a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java @@ -252,4 +252,4 @@ public class HillshadeLayerTest extends BaseActivityTest { assertEquals(layer.getHillshadeAccentColorAsInt(), Color.RED); }); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java index 40cf0f2927..e35f0edcc4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java @@ -545,4 +545,4 @@ public class LineLayerTest extends BaseActivityTest { assertEquals((String) layer.getLinePattern().getValue(), (String) "pedestrian-polygon"); }); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java index 0410d09369..6e5afdb479 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java @@ -254,4 +254,4 @@ public class RasterLayerTest extends BaseActivityTest { assertEquals((Float) layer.getRasterFadeDuration().getValue(), (Float) 0.3f); }); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java index fc526176d4..23a75d1642 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java @@ -280,6 +280,20 @@ public class RuntimeStyleTests extends BaseActivityTest { }); } + @Test + public void testRemoveNonExistingSource() { + invoke(mapboxMap, (uiController, mapboxMap) -> mapboxMap.removeSource("source")); + } + + @Test + public void testRemoveNonExistingLayer() { + invoke(mapboxMap, (uiController, mapboxMap) -> { + mapboxMap.removeLayer("layer"); + mapboxMap.removeLayerAt(mapboxMap.getLayers().size() + 1); + mapboxMap.removeLayerAt(-1); + }); + } + /** * https://github.com/mapbox/mapbox-gl-native/issues/7973 */ diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java index 62f73b1507..fe38fef253 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java @@ -1392,4 +1392,4 @@ public class SymbolLayerTest extends BaseActivityTest { assertEquals((String) layer.getTextTranslateAnchor().getValue(), (String) TEXT_TRANSLATE_ANCHOR_MAP); }); } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs index 935813899f..575f64e809 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs @@ -150,7 +150,7 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest { assertEquals((<%- propertyType(property) %>) layer.get<%- camelize(property.name) %>().getValue(), (<%- propertyType(property) %>) <%- defaultValueJava(property) %>); }); } -<% if (isDataDriven(property)) { -%> +<% if (property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven') { -%> <% if (!(property.name.endsWith("-font")||property.name.endsWith("-offset"))) { -%> @Test @@ -188,4 +188,4 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest { <% } -%> <% } -%> <% } -%> -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 442635f909..fb1d0ef8a2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -69,9 +69,9 @@ android:value=".activity.FeatureOverviewActivity" /> </activity> <activity - android:name=".activity.annotation.AnimatedMarkerActivity" - android:description="@string/description_animated_marker" - android:label="@string/activity_animated_marker"> + android:name=".activity.annotation.AnimatedSymbolLayerActivity" + android:description="@string/description_animated_symbollayer" + android:label="@string/activity_animated_symbollayer"> <meta-data android:name="@string/category" android:value="@string/category_annotation" /> @@ -794,4 +794,4 @@ <!-- android:value="true" /> --> </application> -</manifest>
\ No newline at end of file +</manifest> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java deleted file mode 100644 index e6db071141..0000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java +++ /dev/null @@ -1,283 +0,0 @@ -package com.mapbox.mapboxsdk.testapp.activity.annotation; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.TypeEvaluator; -import android.animation.ValueAnimator; -import android.os.Bundle; -import android.support.annotation.DrawableRes; -import android.support.v4.content.res.ResourcesCompat; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; - -import com.mapbox.geojson.Point; -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.IconFactory; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerView; -import com.mapbox.mapboxsdk.annotations.MarkerViewManager; -import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.mapboxsdk.testapp.utils.IconUtils; -import com.mapbox.turf.TurfMeasurement; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Test activity showcasing animating MarkerViews. - */ -public class AnimatedMarkerActivity extends AppCompatActivity { - - private MapView mapView; - private MapboxMap mapboxMap; - - private LatLng dupontCircle = new LatLng(38.90962, -77.04341); - - private Marker passengerMarker = null; - private MarkerView carMarker = null; - - private Runnable animationRunnable; - - private List<MarkerView> markerViews = new ArrayList<>(); - private boolean stopped; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_animated_marker); - - mapView = (MapView) findViewById(R.id.mapView); - mapView.onCreate(savedInstanceState); - mapView.getMapAsync(mapboxMap -> { - AnimatedMarkerActivity.this.mapboxMap = mapboxMap; - setupMap(); - - animationRunnable = () -> { - for (int i = 0; i < 10; i++) { - addRandomCar(); - } - addPassenger(); - addMainCar(); - }; - mapView.post(animationRunnable); - }); - } - - private void setupMap() { - CameraPosition cameraPosition = new CameraPosition.Builder() - .target(dupontCircle) - .zoom(15) - .build(); - mapboxMap.setCameraPosition(cameraPosition); - } - - private void addPassenger() { - if (isActivityStopped()) { - return; - } - - LatLng randomLatLng = getLatLngInBounds(); - - if (passengerMarker == null) { - Icon icon = IconUtils.drawableToIcon(this, R.drawable.ic_directions_run_black, - ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme())); - passengerMarker = mapboxMap.addMarker(new MarkerViewOptions() - .position(randomLatLng) - .icon(icon)); - } else { - passengerMarker.setPosition(randomLatLng); - } - } - - private void addMainCar() { - if (isActivityStopped()) { - return; - } - - LatLng randomLatLng = getLatLngInBounds(); - - if (carMarker == null) { - carMarker = createCarMarker(randomLatLng, R.drawable.ic_taxi_top, - markerView -> { - // Make sure the car marker is selected so that it's always brought to the front (#5285) - mapboxMap.selectMarker(carMarker); - animateMoveToPassenger(carMarker); - }); - markerViews.add(carMarker); - } else { - carMarker.setPosition(randomLatLng); - } - } - - private void animateMoveToPassenger(final MarkerView car) { - if (isActivityStopped()) { - return; - } - - ValueAnimator animator = animateMoveMarker(car, passengerMarker.getPosition()); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - addPassenger(); - animateMoveToPassenger(car); - } - }); - } - - protected void addRandomCar() { - markerViews.add(createCarMarker(getLatLngInBounds(), R.drawable.ic_car_top, - markerView -> randomlyMoveMarker(markerView))); - } - - private void randomlyMoveMarker(final MarkerView marker) { - if (isActivityStopped()) { - return; - } - - ValueAnimator animator = animateMoveMarker(marker, getLatLngInBounds()); - - // Add listener to restart animation on end - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - randomlyMoveMarker(marker); - } - }); - } - - private ValueAnimator animateMoveMarker(final MarkerView marker, LatLng to) { - marker.setRotation((float) getBearing(marker.getPosition(), to)); - - final ValueAnimator markerAnimator = ObjectAnimator.ofObject( - marker, "position", new LatLngEvaluator(), marker.getPosition(), to); - markerAnimator.setDuration((long) (10 * marker.getPosition().distanceTo(to))); - markerAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); - - // Start - markerAnimator.start(); - - return markerAnimator; - } - - private MarkerView createCarMarker(LatLng start, @DrawableRes int carResource, - MarkerViewManager.OnMarkerViewAddedListener listener) { - Icon icon = IconFactory.getInstance(AnimatedMarkerActivity.this) - .fromResource(carResource); - - // View Markers - return mapboxMap.addMarker(new MarkerViewOptions() - .position(start) - .icon(icon), listener); - - // GL Markers -// return mapboxMap.addMarker(new MarkerOptions() -// .position(start) -// .icon(icon)); - - } - - private LatLng getLatLngInBounds() { - LatLngBounds bounds = mapboxMap.getProjection().getVisibleRegion().latLngBounds; - Random generator = new Random(); - double randomLat = bounds.getLatSouth() + generator.nextDouble() - * (bounds.getLatNorth() - bounds.getLatSouth()); - double randomLon = bounds.getLonWest() + generator.nextDouble() - * (bounds.getLonEast() - bounds.getLonWest()); - return new LatLng(randomLat, randomLon); - } - - @Override - protected void onStart() { - super.onStart(); - mapView.onStart(); - } - - @Override - protected void onResume() { - super.onResume(); - mapView.onResume(); - } - - @Override - protected void onPause() { - super.onPause(); - mapView.onPause(); - } - - @Override - protected void onStop() { - super.onStop(); - - stopped = true; - - // Stop ongoing animations, prevent memory leaks - if (mapboxMap != null) { - MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager(); - for (MarkerView markerView : markerViews) { - View view = markerViewManager.getView(markerView); - if (view != null) { - view.animate().cancel(); - } - } - } - - // onStop - mapView.onStop(); - mapView.removeCallbacks(animationRunnable); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - mapView.onSaveInstanceState(outState); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mapView.onDestroy(); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - mapView.onLowMemory(); - } - - /** - * Evaluator for LatLng pairs - */ - private static class LatLngEvaluator implements TypeEvaluator<LatLng> { - - private LatLng latLng = new LatLng(); - - @Override - public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { - latLng.setLatitude(startValue.getLatitude() - + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); - latLng.setLongitude(startValue.getLongitude() - + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); - return latLng; - } - } - - private double getBearing(LatLng from, LatLng to) { - return TurfMeasurement.bearing( - Point.fromLngLat(from.getLongitude(), from.getLatitude()), - Point.fromLngLat(to.getLongitude(), to.getLatitude()) - ); - } - - private boolean isActivityStopped() { - return stopped; - } -} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java index 6277dffe91..5983fb367a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java @@ -172,9 +172,12 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe if (mapboxMap == null) { return false; } - findViewById(R.id.fab).setVisibility(View.GONE); - resetCameraPosition(); - playAnimation(item.getItemId()); + + if (item.getItemId() != android.R.id.home) { + findViewById(R.id.fab).setVisibility(View.GONE); + resetCameraPosition(); + playAnimation(item.getItemId()); + } return super.onOptionsItemSelected(item); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java index 930626078d..f5e0371aad 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java @@ -1,14 +1,13 @@ package com.mapbox.mapboxsdk.testapp.activity.fragment; -import android.app.FragmentTransaction; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; - import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapFragment; +import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.MapboxMapOptions; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; @@ -20,53 +19,71 @@ import com.mapbox.mapboxsdk.testapp.R; * Uses MapboxMapOptions to initialise the Fragment. * </p> */ -public class MapFragmentActivity extends AppCompatActivity implements OnMapReadyCallback { +public class MapFragmentActivity extends AppCompatActivity implements MapFragment.OnMapViewReadyCallback, + OnMapReadyCallback, MapView.OnMapChangedListener { private MapboxMap mapboxMap; + private MapView mapView; + private boolean initialCameraAnimation = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map_fragment); - - MapFragment mapFragment; if (savedInstanceState == null) { - final FragmentTransaction transaction = getFragmentManager().beginTransaction(); + MapFragment mapFragment = MapFragment.newInstance(createFragmentOptions()); + getFragmentManager() + .beginTransaction() + .add(R.id.fragment_container,mapFragment, "com.mapbox.map") + .commit(); + mapFragment.getMapAsync(this); + } + } - MapboxMapOptions options = new MapboxMapOptions(); - options.styleUrl(Style.OUTDOORS); + private MapboxMapOptions createFragmentOptions() { + MapboxMapOptions options = new MapboxMapOptions(); + options.styleUrl(Style.OUTDOORS); - options.scrollGesturesEnabled(false); - options.zoomGesturesEnabled(false); - options.tiltGesturesEnabled(false); - options.rotateGesturesEnabled(false); + options.scrollGesturesEnabled(false); + options.zoomGesturesEnabled(false); + options.tiltGesturesEnabled(false); + options.rotateGesturesEnabled(false); + options.debugActive(false); - options.debugActive(false); + LatLng dc = new LatLng(38.90252, -77.02291); - LatLng dc = new LatLng(38.90252, -77.02291); + options.minZoomPreference(9); + options.maxZoomPreference(11); + options.camera(new CameraPosition.Builder() + .target(dc) + .zoom(11) + .build()); + return options; + } - options.minZoomPreference(9); - options.maxZoomPreference(11); - options.camera(new CameraPosition.Builder() - .target(dc) - .zoom(11) - .build()); + @Override + public void onMapViewReady(MapView map) { + mapView = map; + mapView.addOnMapChangedListener(this); + } - mapFragment = MapFragment.newInstance(options); + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + } - transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map"); - transaction.commit(); - } else { - mapFragment = (MapFragment) getFragmentManager().findFragmentByTag("com.mapbox.map"); + @Override + public void onMapChanged(int change) { + if (initialCameraAnimation && change == MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED) { + mapboxMap.animateCamera( + CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 5000); + initialCameraAnimation = false; } - - mapFragment.getMapAsync(this); } @Override - public void onMapReady(MapboxMap map) { - mapboxMap = map; - mapboxMap.animateCamera( - CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 10000); + protected void onDestroy() { + super.onDestroy(); + mapView.removeOnMapChangedListener(this); } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java index 4f828060ee..f494a9231e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java @@ -1,13 +1,13 @@ package com.mapbox.mapboxsdk.testapp.activity.fragment; import android.os.Bundle; -import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; - import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapFragment; +import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.MapboxMapOptions; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; @@ -20,51 +20,71 @@ import com.mapbox.mapboxsdk.testapp.R; * Uses MapboxMapOptions to initialise the Fragment. * </p> */ -public class SupportMapFragmentActivity extends AppCompatActivity implements OnMapReadyCallback { +public class SupportMapFragmentActivity extends AppCompatActivity implements MapFragment.OnMapViewReadyCallback, + OnMapReadyCallback, MapView.OnMapChangedListener { private MapboxMap mapboxMap; + private MapView mapView; + private boolean initialCameraAnimation = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map_fragment); - - SupportMapFragment mapFragment; if (savedInstanceState == null) { - final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - - MapboxMapOptions options = new MapboxMapOptions(); - options.styleUrl(Style.SATELLITE_STREETS); - - options.debugActive(false); - options.compassEnabled(false); - options.attributionEnabled(false); - options.logoEnabled(false); + SupportMapFragment mapFragment = SupportMapFragment.newInstance(createFragmentOptions()); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.fragment_container, mapFragment, "com.mapbox.map") + .commit(); + mapFragment.getMapAsync(this); + } + } - LatLng dc = new LatLng(38.90252, -77.02291); + private MapboxMapOptions createFragmentOptions() { + MapboxMapOptions options = new MapboxMapOptions(); + options.styleUrl(Style.MAPBOX_STREETS); - options.minZoomPreference(9); - options.maxZoomPreference(11); - options.camera(new CameraPosition.Builder() - .target(dc) - .zoom(11) - .build()); + options.scrollGesturesEnabled(false); + options.zoomGesturesEnabled(false); + options.tiltGesturesEnabled(false); + options.rotateGesturesEnabled(false); + options.debugActive(false); - mapFragment = SupportMapFragment.newInstance(options); + LatLng dc = new LatLng(38.90252, -77.02291); - transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map"); - transaction.commit(); - } else { - mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentByTag("com.mapbox.map"); - } + options.minZoomPreference(9); + options.maxZoomPreference(11); + options.camera(new CameraPosition.Builder() + .target(dc) + .zoom(11) + .build()); + return options; + } - mapFragment.getMapAsync(this); + @Override + public void onMapViewReady(MapView map) { + mapView = map; + mapView.addOnMapChangedListener(this); } @Override public void onMapReady(MapboxMap map) { mapboxMap = map; - mapboxMap.animateCamera( - CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 10000); } -} + + @Override + public void onMapChanged(int change) { + if (initialCameraAnimation && change == MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED) { + mapboxMap.animateCamera( + CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 5000); + initialCameraAnimation = false; + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.removeOnMapChangedListener(this); + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java index c5ad467673..e1a524790d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java @@ -14,7 +14,7 @@ import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; - +import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.offline.OfflineManager; import com.mapbox.mapboxsdk.offline.OfflineRegion; import com.mapbox.mapboxsdk.testapp.R; @@ -27,10 +27,13 @@ import java.util.List; /** * Test activity showing integration of updating metadata of an OfflineRegion. */ -public class UpdateMetadataActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { +public class UpdateMetadataActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, + AdapterView.OnItemLongClickListener { private OfflineRegionMetadataAdapter adapter; + private MapView mapView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -40,6 +43,7 @@ public class UpdateMetadataActivity extends AppCompatActivity implements Adapter listView.setAdapter(adapter = new OfflineRegionMetadataAdapter(this)); listView.setEmptyView(findViewById(android.R.id.empty)); listView.setOnItemClickListener(this); + listView.setOnItemLongClickListener(this); } @Override @@ -64,6 +68,18 @@ public class UpdateMetadataActivity extends AppCompatActivity implements Adapter builder.show(); } + @Override + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + ViewGroup container = (ViewGroup) findViewById(R.id.container); + container.removeAllViews(); + container.addView(mapView = new MapView(view.getContext())); + mapView.setOfflineRegionDefinition(adapter.getItem(position).getDefinition()); + mapView.onCreate(null); + mapView.onStart(); + mapView.onResume(); + return true; + } + private void updateMetadata(OfflineRegion region, byte[] metadata) { region.updateMetadata(metadata, new OfflineRegion.OfflineRegionUpdateMetadataCallback() { @Override @@ -104,6 +120,16 @@ public class UpdateMetadataActivity extends AppCompatActivity implements Adapter }); } + @Override + protected void onDestroy() { + super.onDestroy(); + if (mapView != null) { + mapView.onPause(); + mapView.onStop(); + mapView.onDestroy(); + } + } + private static class OfflineRegionMetadataAdapter extends BaseAdapter { private Context context; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java index df06c9c42d..81fd2c6ff8 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java @@ -5,7 +5,6 @@ import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; - import com.google.gson.JsonObject; import com.mapbox.geojson.Feature; import com.mapbox.geojson.FeatureCollection; @@ -16,16 +15,13 @@ 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 timber.log.Timber; import java.util.List; -import timber.log.Timber; - import static com.mapbox.mapboxsdk.style.expressions.Expression.get; -import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; -import static com.mapbox.mapboxsdk.style.expressions.Expression.linear; import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +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.switchCase; import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; @@ -44,7 +40,6 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity { private static final String BUS_MAKI_ICON_ID = "bus-11"; private static final String CAFE_MAKI_ICON_ID = "cafe-11"; private static final String KEY_PROPERTY_SELECTED = "selected"; - private static final float ZOOM_STOP_MIN_VALUE = 7.0f; private static final float ZOOM_STOP_MAX_VALUE = 12.0f; private MapView mapView; @@ -103,11 +98,9 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity { layer = new SymbolLayer(LAYER_ID, SOURCE_ID); layer.setProperties( iconImage( - interpolate( - linear(), zoom(), - stop(ZOOM_STOP_MIN_VALUE, BUS_MAKI_ICON_ID), - stop(ZOOM_STOP_MAX_VALUE, CAFE_MAKI_ICON_ID) - ) + step(zoom(), literal(BUS_MAKI_ICON_ID), + stop(ZOOM_STOP_MAX_VALUE, CAFE_MAKI_ICON_ID) + ) ), iconSize( switchCase( diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml index 0566757d58..252af714e7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml @@ -10,9 +10,9 @@ android:id="@id/mapView" android:layout_width="match_parent" android:layout_height="wrap_content" - app:mapbox_cameraTargetLat="51.502615" - app:mapbox_cameraTargetLng="4.972326" - app:mapbox_cameraZoom="6" + app:mapbox_cameraTargetLat="38.90962" + app:mapbox_cameraTargetLng="-77.04341" + app:mapbox_cameraZoom="15" app:mapbox_styleUrl="@string/mapbox_style_light"/> </LinearLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml index 1eb999caf5..501bc55743 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout + android:id="@+id/container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml index 5238176ce8..17d6ad57c6 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -19,7 +19,7 @@ <string name="description_offline">Offline Map example</string> <string name="description_update_metadata">Update metadata example</string> <string name="description_offline_region_delete">Delete region example</string> - <string name="description_animated_marker">Animate the position change of a marker</string> + <string name="description_animated_symbollayer">Animate the position change of a symbol layer</string> <string name="description_polyline">Add a polyline to a map</string> <string name="description_polygon">Add a polygon to a map</string> <string name="description_scroll_by">Scroll with pixels in x,y direction</string> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml index 89ec586950..3f011bd3ed 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml @@ -4,7 +4,7 @@ <string name="activity_map_fragment">Map Fragment</string> <string name="activity_multimap">Multiple Maps on Screen</string> <string name="activity_add_bulk_markers">Add Markers In Bulk</string> - <string name="activity_animated_marker">Animated Markers</string> + <string name="activity_animated_symbollayer">Animated SymbolLayer</string> <string name="activity_dynamic_marker">Dynamic Marker</string> <string name="activity_polyline">Polyline</string> <string name="activity_polygon">Polygon</string> diff --git a/platform/android/README.md b/platform/android/README.md index 1dbdeee343..31e0a94f2c 100644 --- a/platform/android/README.md +++ b/platform/android/README.md @@ -12,7 +12,7 @@ Alright. So, actually, you may be in the wrong place. From here on in, this READ **To install and use the Mapbox Maps SDK for Android in an application, see the [Mapbox Maps SDK for Android website](https://www.mapbox.com/install/android/).** -[![](https://www.mapbox.com/android-sdk/images/splash.png)](https://www.mapbox.com/android-sdk/) +[![](https://www.mapbox.com/android-docs/assets/overview-map-sdk-322-9abe118316efb5910b6101e222a2e57c.png)](https://www.mapbox.com/android-sdk/) ### Setup environment @@ -89,15 +89,14 @@ More information about building and distributing this project in [DISTRIBUTE.md] #### Using the SDK snapshot -Instead of using the latest stable release of the Maps SDK for Android, you can use a "snapshot" or the beta version if there is one available. Our snapshots are built every time a Github pull request adds code to this repository's `master` branch. If you'd like to use a snapshot build, your Android project's gradle file should have -SNAPSHOT appended to the SDK version number. For example `5.2.0-SNAPSHOT` or: +Instead of using the latest stable release of the Maps SDK for Android, you can use a "snapshot" or the beta version if there is one available. Our snapshots are built every time a Github pull request adds code to this repository's `master` branch. If you'd like to use a snapshot build, your Android project's gradle file should have -SNAPSHOT appended to the SDK version number. For example, the `5.2.0-SNAPSHOT` would look like: ```java // Mapbox SDK dependency -compile('com.mapbox.mapboxsdk:mapbox-android-sdk:5.2.0-SNAPSHOT@aar') { - transitive = true -} +implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:5.2.0-SNAPSHOT' ``` -You need to have the section below in your build.gradle root folder to be able to resolve the SNAPSHOT dependencies: + +You also need to have the section below in your build.gradle root folder to be able to resolve the SNAPSHOT dependencies: ``` allprojects { repositories { diff --git a/platform/android/build.gradle b/platform/android/build.gradle index 16238f41c1..45cddd9688 100644 --- a/platform/android/build.gradle +++ b/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.2' } } diff --git a/platform/android/config.cmake b/platform/android/config.cmake index 77074dc82c..c25e48de05 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -10,7 +10,7 @@ set(CMAKE_C_ARCHIVE_APPEND "<CMAKE_AR> ruT <TARGET> <LINK_FLAGS> <OBJECTS>") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections") -if ((ANDROID_ABI STREQUAL "armeabi") OR (ANDROID_ABI STREQUAL "armeabi-v7a") OR (ANDROID_ABI STREQUAL "arm64-v8a") OR +if ((ANDROID_ABI STREQUAL "armeabi-v7a") OR (ANDROID_ABI STREQUAL "arm64-v8a") OR (ANDROID_ABI STREQUAL "x86") OR (ANDROID_ABI STREQUAL "x86_64")) # Use Identical Code Folding on platforms that support the gold linker. set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=gold -Wl,--icf=safe ${CMAKE_EXE_LINKER_FLAGS}") diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index a5c6ad0da3..ff61b1d074 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -2,24 +2,23 @@ ext { androidVersions = [ minSdkVersion : 14, - targetSdkVersion : 25, - compileSdkVersion: 25, - buildToolsVersion: '26.0.3' + targetSdkVersion : 27, + compileSdkVersion: 27, + buildToolsVersion: '27.0.3' ] versions = [ - mapboxServices : '3.0.1', - mapboxTelemetry: '3.0.2', + mapboxServices : '3.1.0', + mapboxTelemetry: '3.1.1', mapboxGestures : '0.2.0', - supportLib : '25.4.0', - espresso : '3.0.1', - testRunner : '1.0.1', - leakCanary : '1.5.1', - lost : '3.0.4', + supportLib : '27.1.1', + espresso : '3.0.2', + testRunner : '1.0.2', + leakCanary : '1.5.4', junit : '4.12', - mockito : '2.10.0', - robolectric : '3.5.1', - timber : '4.5.1', + mockito : '2.18.3', + robolectric : '3.8', + timber : '4.7.0', okhttp : '3.10.0' ] @@ -40,6 +39,7 @@ ext { testRules : "com.android.support.test:rules:${versions.testRunner}", testEspressoCore : "com.android.support.test.espresso:espresso-core:${versions.espresso}", testEspressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}", + testEspressoContrib : "com.android.support.test.espresso:espresso-contrib:${versions.espresso}", supportAnnotations : "com.android.support:support-annotations:${versions.supportLib}", supportAppcompatV7 : "com.android.support:appcompat-v7:${versions.supportLib}", @@ -47,11 +47,10 @@ ext { supportDesign : "com.android.support:design:${versions.supportLib}", supportRecyclerView : "com.android.support:recyclerview-v7:${versions.supportLib}", - lost : "com.mapzen.android:lost:${versions.lost}", gmsLocation : 'com.google.android.gms:play-services-location:11.0.4', timber : "com.jakewharton.timber:timber:${versions.timber}", okhttp3 : "com.squareup.okhttp3:okhttp:${versions.okhttp}", leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${versions.leakCanary}", leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${versions.leakCanary}" ] -}
\ No newline at end of file +} diff --git a/platform/android/gradle/gradle-dependencies-graph.gradle b/platform/android/gradle/gradle-dependencies-graph.gradle new file mode 100644 index 0000000000..5cbc7b974f --- /dev/null +++ b/platform/android/gradle/gradle-dependencies-graph.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath "com.vanniktech:gradle-dependency-graph-generator-plugin:0.3.0" + } +} + +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorPlugin +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator +import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions +import com.vanniktech.dependency.graph.generator.dot.Color +import com.vanniktech.dependency.graph.generator.dot.Shape +import com.vanniktech.dependency.graph.generator.dot.Style + +plugins.apply(DependencyGraphGeneratorPlugin) + +def mapboxGenerator = new Generator( + "mapboxLibraries", // Suffix for our Gradle task. + "", // Root suffix that we don't want in this case. + { dependency -> dependency.getModuleGroup().startsWith("com.mapbox.mapboxsdk") }, // Only want Mapbox libs. + { dependency -> true }, // Include transitive dependencies. +) + +dependencyGraphGenerator { + generators = [mapboxGenerator] +} diff --git a/platform/android/gradle/wrapper/gradle-wrapper.properties b/platform/android/gradle/wrapper/gradle-wrapper.properties index bf1b63c346..84af82d181 100644 --- a/platform/android/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Mon May 14 12:12:39 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json index eff7fb9cbf..157808aa51 100644 --- a/platform/android/scripts/exclude-activity-gen.json +++ b/platform/android/scripts/exclude-activity-gen.json @@ -18,7 +18,7 @@ "LocationPickerActivity", "GeoJsonClusteringActivity", "RuntimeStyleTestActivity", - "AnimatedMarkerActivity", + "AnimatedSymbolLayerActivity", "ViewPagerActivity", "MapFragmentActivity", "SupportMapFragmentActivity", diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index 3b0363cc19..05ca957974 100755 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -267,19 +267,17 @@ global.propertyValueDoc = function (property, value) { return doc; }; -global.isDataDriven = function (property) { - return property['property-function'] === true; -}; - global.isLightProperty = function (property) { return property['light-property'] === true; }; global.propertyValueType = function (property) { - if (isDataDriven(property)) { - return `DataDrivenPropertyValue<${evaluatedType(property)}>`; - } else { - return `PropertyValue<${evaluatedType(property)}>`; + switch (property['property-type']) { + case 'data-driven': + case 'cross-faded-data-driven': + return `DataDrivenPropertyValue<${evaluatedType(property)}>`; + default: + return `PropertyValue<${evaluatedType(property)}>`; } }; @@ -318,11 +316,11 @@ global.evaluatedType = function (property) { }; global.supportsZoomFunction = function (property) { - return property['zoom-function'] === true; + return property.expression && property.expression.parameters.indexOf('zoom') > -1; }; global.supportsPropertyFunction = function (property) { - return property['property-function'] === true; + return property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven'; }; // Template processing // diff --git a/platform/android/src/bitmap.cpp b/platform/android/src/bitmap.cpp index 46e7253050..0d3670b666 100644 --- a/platform/android/src/bitmap.cpp +++ b/platform/android/src/bitmap.cpp @@ -1,6 +1,7 @@ #include "bitmap.hpp" #include <android/bitmap.h> +#include <mbgl/util/logging.hpp> namespace mbgl { namespace android { @@ -17,7 +18,7 @@ public: ~PixelGuard() { const int result = AndroidBitmap_unlockPixels(&env, jni::Unwrap(*bitmap)); if (result != ANDROID_BITMAP_RESULT_SUCCESS) { - throw std::runtime_error("bitmap decoding: could not unlock pixels"); + Log::Warning(mbgl::Event::General, "Bitmap decoding: could not unlock pixels"); } } diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp index d8d715dbd3..58a91f6cf0 100644 --- a/platform/android/src/file_source.cpp +++ b/platform/android/src/file_source.cpp @@ -83,6 +83,13 @@ void FileSource::pause(jni::JNIEnv&) { } } +jni::jboolean FileSource::isResumed(jni::JNIEnv&) { + if (activationCounter) { + return (jboolean) (activationCounter > 0); + } + return (jboolean) false; +} + jni::Class<FileSource> FileSource::javaClass; FileSource* FileSource::getNativePeer(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) { @@ -114,7 +121,8 @@ void FileSource::registerNative(jni::JNIEnv& env) { METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"), METHOD(&FileSource::setResourceTransform, "setResourceTransform"), METHOD(&FileSource::resume, "activate"), - METHOD(&FileSource::pause, "deactivate") + METHOD(&FileSource::pause, "deactivate"), + METHOD(&FileSource::isResumed, "isActivated") ); } diff --git a/platform/android/src/file_source.hpp b/platform/android/src/file_source.hpp index 194f784622..e4295e1b84 100644 --- a/platform/android/src/file_source.hpp +++ b/platform/android/src/file_source.hpp @@ -45,6 +45,8 @@ public: void pause(jni::JNIEnv&); + jni::jboolean isResumed(jni::JNIEnv&); + static jni::Class<FileSource> javaClass; static FileSource* getNativePeer(jni::JNIEnv&, jni::Object<FileSource>); diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp index aa9dc1a7f6..8a9656ea14 100644 --- a/platform/android/src/geojson/point.cpp +++ b/platform/android/src/geojson/point.cpp @@ -59,4 +59,4 @@ jni::Class<Point> Point::javaClass; } // namespace geojson } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/geometry/lat_lng_bounds.cpp b/platform/android/src/geometry/lat_lng_bounds.cpp index ec1a32fed5..827ee52e95 100644 --- a/platform/android/src/geometry/lat_lng_bounds.cpp +++ b/platform/android/src/geometry/lat_lng_bounds.cpp @@ -9,14 +9,17 @@ jni::Object<LatLngBounds> LatLngBounds::New(jni::JNIEnv& env, mbgl::LatLngBounds } mbgl::LatLngBounds LatLngBounds::getLatLngBounds(jni::JNIEnv& env, jni::Object<LatLngBounds> bounds) { - static auto swLat = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "latitudeSouth"); - static auto swLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "longitudeWest"); - static auto neLat = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "latitudeNorth"); - static auto neLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "longitudeEast"); - return mbgl::LatLngBounds::hull( - { bounds.Get(env, swLat), bounds.Get(env, swLon) }, - { bounds.Get(env, neLat), bounds.Get(env, neLon) } - ); + static auto swLatField = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "latitudeSouth"); + static auto swLonField = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "longitudeWest"); + static auto neLatField = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "latitudeNorth"); + static auto neLonField = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "longitudeEast"); + + mbgl::LatLng sw = { bounds.Get(env, swLatField), bounds.Get(env, swLonField) }; + mbgl::LatLng ne = { bounds.Get(env, neLatField), bounds.Get(env, neLonField) }; + + sw.unwrapForShortestPath(ne); + + return mbgl::LatLngBounds::hull(sw, ne); } void LatLngBounds::registerNative(jni::JNIEnv& env) { diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp index f7e16b7091..ba6fdc63b0 100644 --- a/platform/android/src/map_renderer.cpp +++ b/platform/android/src/map_renderer.cpp @@ -29,6 +29,7 @@ MapRenderer::MapRenderer(jni::JNIEnv& _env, jni::Object<MapRenderer> obj, MapRenderer::~MapRenderer() = default; void MapRenderer::reset() { + destroyed = true; // Make sure to destroy the renderer on the GL Thread auto self = ActorRef<MapRenderer>(*this, mailbox); self.ask(&MapRenderer::resetRenderer).wait(); @@ -88,8 +89,10 @@ void MapRenderer::requestSnapshot(SnapshotCallback callback) { self.invoke( &MapRenderer::scheduleSnapshot, std::make_unique<SnapshotCallback>([&, callback=std::move(callback), runloop=util::RunLoop::Get()](PremultipliedImage image) { - runloop->invoke([callback=std::move(callback), image=std::move(image)]() mutable { - callback(std::move(image)); + runloop->invoke([callback=std::move(callback), image=std::move(image), renderer=std::move(this)]() mutable { + if (renderer && !renderer->destroyed) { + callback(std::move(image)); + } }); snapshotCallback.reset(); }) @@ -136,7 +139,7 @@ void MapRenderer::render(JNIEnv&) { renderer->render(*params); // Deliver the snapshot if requested - if (snapshotCallback && !paused) { + if (snapshotCallback) { snapshotCallback->operator()(backend->readFramebuffer()); snapshotCallback.reset(); } @@ -174,14 +177,6 @@ void MapRenderer::onSurfaceChanged(JNIEnv&, jint width, jint height) { requestRender(); } -void MapRenderer::onResume(JNIEnv&) { - paused = false; -} - -void MapRenderer::onPause(JNIEnv&) { - paused = true; -} - // Static methods // jni::Class<MapRenderer> MapRenderer::javaClass; @@ -200,11 +195,7 @@ void MapRenderer::registerNative(jni::JNIEnv& env) { METHOD(&MapRenderer::onSurfaceCreated, "nativeOnSurfaceCreated"), METHOD(&MapRenderer::onSurfaceChanged, - "nativeOnSurfaceChanged"), - METHOD(&MapRenderer::onResume, - "nativeOnResume"), - METHOD(&MapRenderer::onPause, - "nativeOnPause")); + "nativeOnSurfaceChanged")); } MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, jni::Object<MapRenderer> jObject) { diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp index 5fb5ef1a61..97d2db4a91 100644 --- a/platform/android/src/map_renderer.hpp +++ b/platform/android/src/map_renderer.hpp @@ -98,10 +98,6 @@ private: void onSurfaceChanged(JNIEnv&, jint width, jint height); - void onResume(JNIEnv&); - - void onPause(JNIEnv&); - private: GenericUniqueWeakObject<MapRenderer> javaPeer; @@ -124,7 +120,7 @@ private: std::mutex updateMutex; bool framebufferSizeChanged = false; - std::atomic<bool> paused {false}; + std::atomic<bool> destroyed {false}; std::unique_ptr<SnapshotCallback> snapshotCallback; }; diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp index 34366d836a..f655f13ea8 100644 --- a/platform/android/src/run_loop.cpp +++ b/platform/android/src/run_loop.cpp @@ -5,6 +5,7 @@ #include <mbgl/util/thread.hpp> #include <mbgl/util/timer.hpp> #include <mbgl/actor/scheduler.hpp> +#include <mbgl/util/event.hpp> #include <android/looper.h> @@ -17,6 +18,7 @@ #include <fcntl.h> #include <unistd.h> +#include <mbgl/util/logging.hpp> #define PIPE_OUT 0 #define PIPE_IN 1 @@ -119,11 +121,11 @@ RunLoop::Impl::~Impl() { alarm.reset(); if (ALooper_removeFd(loop, fds[PIPE_OUT]) != 1) { - throw std::runtime_error("Failed to remove file descriptor from Looper."); + Log::Error(mbgl::Event::General, "Failed to remove file descriptor from Looper"); } if (close(fds[PIPE_IN]) || close(fds[PIPE_OUT])) { - throw std::runtime_error("Failed to close file descriptor."); + Log::Error(mbgl::Event::General, "Failed to close file descriptor."); } ALooper_release(loop); diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index 6fe6e3cb29..c7a6bcd3a3 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -157,14 +157,12 @@ namespace android { using namespace mbgl::style::conversion; Filter filter = layer.accept(GetFilterEvaluator()); - - jni::Object<gson::JsonElement> converted; - if (filter.is<ExpressionFilter>()) { - ExpressionFilter filterExpression = filter.get<ExpressionFilter>(); - mbgl::Value expressionValue = filterExpression.expression.get()->serialize(); - converted = gson::JsonElement::New(env, expressionValue); + if (filter.expression) { + mbgl::Value expressionValue = (*filter.expression)->serialize(); + return gson::JsonElement::New(env, expressionValue); + } else { + return jni::Object<gson::JsonElement>(); } - return converted; } struct SetSourceLayerEvaluator { diff --git a/platform/macos/app/LimeGreenStyleLayer.h b/platform/darwin/app/LimeGreenStyleLayer.h index 35480963a4..35480963a4 100644 --- a/platform/macos/app/LimeGreenStyleLayer.h +++ b/platform/darwin/app/LimeGreenStyleLayer.h diff --git a/platform/macos/app/LimeGreenStyleLayer.m b/platform/darwin/app/LimeGreenStyleLayer.m index 40c336cd98..98e96381b6 100644 --- a/platform/macos/app/LimeGreenStyleLayer.m +++ b/platform/darwin/app/LimeGreenStyleLayer.m @@ -1,7 +1,5 @@ #import "LimeGreenStyleLayer.h" - -#include <OpenGL/gl.h> -#include <OpenGL/glext.h> +@import GLKit; @implementation LimeGreenStyleLayer { GLuint _program; @@ -12,8 +10,8 @@ } - (void)didMoveToMapView:(MGLMapView *)mapView { - static const GLchar *vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }"; - static const GLchar *fragmentShaderSource = "void main() { gl_FragColor = vec4(0, 1, 0, 1); }"; + static const GLchar *vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 1, 1); }"; + static const GLchar *fragmentShaderSource = "void main() { gl_FragColor = vec4(0, 0.5, 0, 0.5); }"; _program = glCreateProgram(); _vertexShader = glCreateShader(GL_VERTEX_SHADER); @@ -28,10 +26,10 @@ glLinkProgram(_program); _aPos = glGetAttribLocation(_program, "a_pos"); - GLfloat background[] = { -1,-1, 1,-1, -1,1, 1,1 }; + GLfloat triangle[] = { 0, 0.5, 0.5, -0.5, -0.5, -0.5 }; glGenBuffers(1, &_buffer); glBindBuffer(GL_ARRAY_BUFFER, _buffer); - glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(GLfloat), triangle, GL_STATIC_DRAW); } - (void)drawInMapView:(MGLMapView *)mapView withContext:(MGLStyleLayerDrawingContext)context { @@ -41,7 +39,7 @@ glVertexAttribPointer(_aPos, 2, GL_FLOAT, GL_FALSE, 0, NULL); glDisable(GL_STENCIL_TEST); glDisable(GL_DEPTH_TEST); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); } - (void)willMoveFromMapView:(MGLMapView *)mapView { diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index 2ae6602224..60177e57c2 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -272,7 +272,7 @@ Each property representing a layout or paint attribute is set to an but you create the former using a very different syntax. `NSExpression`’s format string syntax is reminiscent of a spreadsheet formula or an expression in a database query. See the -“[Predicates and Expressions](Predicates and Expressions.md)” guide for an +“[Predicates and Expressions](predicates-and-expressions.html)” guide for an overview of the expression support in this SDK. This SDK no longer supports style functions; use expressions instead. @@ -335,7 +335,7 @@ In style specification | Method, function, or predicate type | Format string syn `number` | | `string` | | `to-boolean` | `boolValue` | -`to-color` | | +`to-color` | | `CAST(var, '<%- cocoaPrefix %>Color')` `to-number` | `mgl_numberWithFallbackValues:` | `CAST(zipCode, 'NSNumber')` `to-string` | `stringValue` | `CAST(ele, 'NSString')` `typeof` | | @@ -372,7 +372,7 @@ In style specification | Method, function, or predicate type | Format string syn `rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` | `rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` | <% } -%> -`to-rgba` | | +`to-rgba` | | `CAST(noindex(var), 'NSArray')` `-` | `from:subtract:` | `2 - 1` `*` | `multiply:by:` | `1 * 2` `/` | `divide:by:` | `1 / 2` @@ -427,5 +427,5 @@ In style JSON | In the format string `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` -See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for +See the “[Predicates and Expressions](predicates-and-expressions.html)” guide for a full description of the supported operators and operand types. diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md index c3b3d39a52..e0d4755d4a 100644 --- a/platform/darwin/docs/guides/Predicates and Expressions.md +++ b/platform/darwin/docs/guides/Predicates and Expressions.md @@ -70,6 +70,8 @@ path or variable into a matching type: * To cast a value to a number, use `CAST(key, 'NSNumber')`. * To cast a value to a string, use `CAST(key, 'NSString')`. +* To cast a value to a color, use `CAST(key, 'UIColor')` on iOS and `CAST(key, 'NSColor')` on macOS. +* To cast an `NSColor` or `UIColor` object to an array, use `CAST(noindex(color), 'NSArray')`. For details about the predicate format string syntax, consult the “Predicate Format String Syntax” chapter of the @@ -534,6 +536,10 @@ expression that contains references to those variables. An input expression, then any number of argument pairs, followed by a default expression. Each argument pair consists of a constant value followed by an expression to produce as a result of matching that constant value. + If the input value is an aggregate expression, then any of the constant values within + that aggregate expression result in the following argument. This is shorthand for + specifying an argument pair for each of the constant values within that aggregate + expression. It is not possible to match the aggregate expression itself. </dd> </dl> diff --git a/platform/darwin/resources/ko.lproj/Foundation.strings b/platform/darwin/resources/ko.lproj/Foundation.strings new file mode 100644 index 0000000000..68e6cf86fc --- /dev/null +++ b/platform/darwin/resources/ko.lproj/Foundation.strings @@ -0,0 +1,297 @@ +/* Clock position format, long: {hours} o’clock */ +"CLOCK_FMT_LONG" = "%@ 시"; + +/* Clock position format, medium: {hours} o’clock */ +"CLOCK_FMT_MEDIUM" = "%@ 시"; + +/* Clock position format, short: {hours}:00 */ +"CLOCK_FMT_SHORT" = "%@:00"; + +/* East, long */ +"COMPASS_E_LONG" = "동쪽"; + +/* East, short */ +"COMPASS_E_SHORT" = "동"; + +/* East by north, long */ +"COMPASS_EbN_LONG" = "동미북쪽"; + +/* East by north, short */ +"COMPASS_EbN_SHORT" = "동미북"; + +/* East by south, long */ +"COMPASS_EbS_LONG" = "동미남쪽"; + +/* East by south, short */ +"COMPASS_EbS_SHORT" = "동미남"; + +/* East-northeast, long */ +"COMPASS_ENE_LONG" = "동북동쪽"; + +/* East-northeast, short */ +"COMPASS_ENE_SHORT" = "동북동"; + +/* East-southeast, long */ +"COMPASS_ESE_LONG" = "동남동쪽"; + +/* East-southeast, short */ +"COMPASS_ESE_SHORT" = "동남동"; + +/* North, long */ +"COMPASS_N_LONG" = "북쪽"; + +/* North, short */ +"COMPASS_N_SHORT" = "북"; + +/* North by east, long */ +"COMPASS_NbE_LONG" = "북미동쪽"; + +/* North by east, short */ +"COMPASS_NbE_SHORT" = "북미동"; + +/* North by west, long */ +"COMPASS_NbW_LONG" = "북미서쪽"; + +/* North by west, short */ +"COMPASS_NbW_SHORT" = "북미서"; + +/* Northeast, long */ +"COMPASS_NE_LONG" = "북동쪽"; + +/* Northeast, short */ +"COMPASS_NE_SHORT" = "북동"; + +/* Northeast by east, long */ +"COMPASS_NEbE_LONG" = "북동미동쪽"; + +/* Northeast by east, short */ +"COMPASS_NEbE_SHORT" = "북동미동"; + +/* Northeast by north, long */ +"COMPASS_NEbN_LONG" = "북동미북쪽"; + +/* Northeast by north, short */ +"COMPASS_NEbN_SHORT" = "북동미북"; + +/* North-northeast, long */ +"COMPASS_NNE_LONG" = "북북동쪽"; + +/* North-northeast, short */ +"COMPASS_NNE_SHORT" = "북북동"; + +/* North-northwest, long */ +"COMPASS_NNW_LONG" = "북북서쪽"; + +/* North-northwest, short */ +"COMPASS_NNW_SHORT" = "북북서"; + +/* Northwest, long */ +"COMPASS_NW_LONG" = "북서쪽"; + +/* Northwest, short */ +"COMPASS_NW_SHORT" = "북서"; + +/* Northwest by north, long */ +"COMPASS_NWbN_LONG" = "북서미북쪽"; + +/* Northwest by north, short */ +"COMPASS_NWbN_SHORT" = "북서미북"; + +/* Northwest by west, long */ +"COMPASS_NWbW_LONG" = "북서미서쪽"; + +/* Northwest by west, short */ +"COMPASS_NWbW_SHORT" = "북서미서"; + +/* South, long */ +"COMPASS_S_LONG" = "남쪽"; + +/* South, short */ +"COMPASS_S_SHORT" = "남"; + +/* South by east, long */ +"COMPASS_SbE_LONG" = "남미동쪽"; + +/* South by east, short */ +"COMPASS_SbE_SHORT" = "남미동"; + +/* South by west, long */ +"COMPASS_SbW_LONG" = "남미서"; + +/* South by west, short */ +"COMPASS_SbW_SHORT" = "남미서"; + +/* Southeast, long */ +"COMPASS_SE_LONG" = "남동쪽"; + +/* Southeast, short */ +"COMPASS_SE_SHORT" = "남동"; + +/* Southeast by east, long */ +"COMPASS_SEbE_LONG" = "남동미동쪽"; + +/* Southeast by east, short */ +"COMPASS_SEbE_SHORT" = "남동미동"; + +/* Southeast by south, long */ +"COMPASS_SEbS_LONG" = "남동미남쪽"; + +/* Southeast by south, short */ +"COMPASS_SEbS_SHORT" = "남동미남"; + +/* South-southeast, long */ +"COMPASS_SSE_LONG" = "남남동쪽"; + +/* South-southeast, short */ +"COMPASS_SSE_SHORT" = "남남동"; + +/* South-southwest, long */ +"COMPASS_SSW_LONG" = "남남서쪽"; + +/* South-southwest, short */ +"COMPASS_SSW_SHORT" = "남남서"; + +/* Southwest, long */ +"COMPASS_SW_LONG" = "남서쪽"; + +/* Southwest, short */ +"COMPASS_SW_SHORT" = "남서"; + +/* Southwest by south, long */ +"COMPASS_SWbS_LONG" = "남서미남쪽"; + +/* Southwest by south, short */ +"COMPASS_SWbS_SHORT" = "남서미남"; + +/* Southwest by west, long */ +"COMPASS_SWbW_LONG" = "남서미서쪽"; + +/* Southwest by west, short */ +"COMPASS_SWbW_SHORT" = "남서미서"; + +/* West, long */ +"COMPASS_W_LONG" = "서쪽"; + +/* West, short */ +"COMPASS_W_SHORT" = "서"; + +/* West by north, long */ +"COMPASS_WbN_LONG" = "서미북쪽"; + +/* West by north, short */ +"COMPASS_WbN_SHORT" = "서미북"; + +/* West by south, long */ +"COMPASS_WbS_LONG" = "서미남쪽"; + +/* West by south, short */ +"COMPASS_WbS_SHORT" = "서미남"; + +/* West-northwest, long */ +"COMPASS_WNW_LONG" = "서북서쪽"; + +/* West-northwest, short */ +"COMPASS_WNW_SHORT" = "서북서"; + +/* West-southwest, long */ +"COMPASS_WSW_LONG" = "서남서쪽"; + +/* West-southwest, short */ +"COMPASS_WSW_SHORT" = "서남서"; + +/* Degrees format, long */ +"COORD_DEG_LONG" = "%d 도"; + +/* Degrees format, medium: {degrees} */ +"COORD_DEG_MEDIUM" = "%d°"; + +/* Degrees format, short: {degrees} */ +"COORD_DEG_SHORT" = "%d°"; + +/* Coordinate format, long: {degrees}{minutes} */ +"COORD_DM_LONG" = "%1$@ 와 %2$@"; + +/* Coordinate format, medium: {degrees}{minutes} */ +"COORD_DM_MEDIUM" = "%1$@%2$@"; + +/* Coordinate format, short: {degrees}{minutes} */ +"COORD_DM_SHORT" = "%1$@%2$@"; + +/* Coordinate format, long: {degrees}{minutes}{seconds} */ +"COORD_DMS_LONG" = "%1$@, %2$@, 와 %3$@"; + +/* Coordinate format, medium: {degrees}{minutes}{seconds} */ +"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@"; + +/* Coordinate format, short: {degrees}{minutes}{seconds} */ +"COORD_DMS_SHORT" = "%1$@%2$@%3$@"; + +/* East longitude format, long: {longitude} */ +"COORD_E_LONG" = "%@ 동쪽"; + +/* East longitude format, medium: {longitude} */ +"COORD_E_MEDIUM" = "%@ 동쪽"; + +/* East longitude format, short: {longitude} */ +"COORD_E_SHORT" = "%@동"; + +/* Coordinate pair format, long: {latitude}, {longitude} */ +"COORD_FMT_LONG" = "%1$@ by %2$@"; + +/* Coordinate pair format, medium: {latitude}, {longitude} */ +"COORD_FMT_MEDIUM" = "%1$@, %2$@"; + +/* Coordinate pair format, short: {latitude}, {longitude} */ +"COORD_FMT_SHORT" = "%1$@, %2$@"; + +/* Minutes format, long */ +"COORD_MIN_LONG" = "%d 분"; + +/* Minutes format, medium: {minutes} */ +"COORD_MIN_MEDIUM" = "%d′"; + +/* Minutes format, short: {minutes} */ +"COORD_MIN_SHORT" = "%d′"; + +/* North latitude format, long: {latitude} */ +"COORD_N_LONG" = "%@ 북쪽"; + +/* North latitude format, medium: {latitude} */ +"COORD_N_MEDIUM" = "%@ 북쪽"; + +/* North latitude format, short: {latitude} */ +"COORD_N_SHORT" = "%@북"; + +/* South latitude format, long: {latitude} */ +"COORD_S_LONG" = "%@ 남쪽"; + +/* South latitude format, medium: {latitude} */ +"COORD_S_MEDIUM" = "%@ 남쪽"; + +/* South latitude format, short: {latitude} */ +"COORD_S_SHORT" = "%@남"; + +/* Seconds format, long */ +"COORD_SEC_LONG" = "%d 초"; + +/* Seconds format, medium: {seconds} */ +"COORD_SEC_MEDIUM" = "%d″"; + +/* Seconds format, short: {seconds} */ +"COORD_SEC_SHORT" = "%d″"; + +/* West longitude format, long: {longitude} */ +"COORD_W_LONG" = "%@ 서쪽"; + +/* West longitude format, medium: {longitude} */ +"COORD_W_MEDIUM" = "%@ 서쪽"; + +/* West longitude format, short: {longitude} */ +"COORD_W_SHORT" = "%@서"; + +/* OpenStreetMap full name attribution */ +"OSM_FULL_NAME" = "오픈스트리트맵"; + +/* OpenStreetMap short name attribution */ +"OSM_SHORT_NAME" = "오픈스트리트맵"; + diff --git a/platform/darwin/resources/ko.lproj/Foundation.stringsdict b/platform/darwin/resources/ko.lproj/Foundation.stringsdict new file mode 100644 index 0000000000..56d26aa949 --- /dev/null +++ b/platform/darwin/resources/ko.lproj/Foundation.stringsdict @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>COORD_DEG_LONG</key> + <dict> + <key>NSStringLocalizedFormatKey</key> + <string>%#@degrees@</string> + <key>degrees</key> + <dict> + <key>NSStringFormatSpecTypeKey</key> + <string>NSStringPluralRuleType</string> + <key>NSStringFormatValueTypeKey</key> + <string>d</string> + <key>other</key> + <string>%d 도</string> + </dict> + </dict> + <key>COORD_MIN_LONG</key> + <dict> + <key>NSStringLocalizedFormatKey</key> + <string>%#@minutes@</string> + <key>minutes</key> + <dict> + <key>NSStringFormatSpecTypeKey</key> + <string>NSStringPluralRuleType</string> + <key>NSStringFormatValueTypeKey</key> + <string>d</string> + <key>other</key> + <string>%d 분</string> + </dict> + </dict> + <key>COORD_SEC_LONG</key> + <dict> + <key>NSStringLocalizedFormatKey</key> + <string>%#@seconds@</string> + <key>seconds</key> + <dict> + <key>NSStringFormatSpecTypeKey</key> + <string>NSStringPluralRuleType</string> + <key>NSStringFormatValueTypeKey</key> + <string>d</string> + <key>other</key> + <string>%d 초</string> + </dict> + </dict> +</dict> +</plist> diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index c7b54b326a..9089e57ad5 100755 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -318,10 +318,11 @@ global.propertyDoc = function (propertyName, property, layerType, kind) { doc += '* Predefined functions, including mathematical and string operators\n' + '* Conditional expressions\n' + '* Variable assignments and references to assigned variables\n'; - const inputVariable = property.name === 'heatmap-color' ? '$heatmapDensity' : '$zoomLevel'; - if (property["property-function"]) { + const inputVariable = property.expression && property['property-type'] === 'color-ramp' ? + '$' + camelizeWithLeadingLowercase(property.expression.parameters[0]) : '$zoomLevel'; + if (isDataDriven(property)) { doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable and/or feature attributes\n`; - } else if (property.function === "interpolated") { + } else if (property.expression && property.expression.interpolated) { doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable\n\n` + 'This property does not support applying interpolation or step functions to feature attributes.'; } else { @@ -332,6 +333,10 @@ global.propertyDoc = function (propertyName, property, layerType, kind) { return doc; }; +global.isDataDriven = function (property) { + return property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven'; +}; + global.propertyReqs = function (property, propertiesByName, type) { return 'This property is only applied to the style if ' + property.requires.map(function (req) { if (typeof req === 'string') { @@ -403,7 +408,7 @@ global.describeValue = function (value, property, layerType) { case 'boolean': return value ? '`YES`' : '`NO`'; case 'number': - return 'the float ' + formatNumber(value); + return 'the float ' + '`' + formatNumber(value) + '`'; case 'string': if (value === '') { return 'the empty string'; diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm index 07d10e852b..e8d6a203d0 100644 --- a/platform/darwin/src/MGLAttributionInfo.mm +++ b/platform/darwin/src/MGLAttributionInfo.mm @@ -16,7 +16,7 @@ @implementation MGLAttributionInfo -+ (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { ++ (NSArray<MGLAttributionInfo *> *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { if (!htmlString) { return @[]; } @@ -112,7 +112,7 @@ return infos; } -+ (NSAttributedString *)attributedStringForAttributionInfos:(NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos { ++ (NSAttributedString *)attributedStringForAttributionInfos:(NSArray<MGLAttributionInfo *> *)attributionInfos { NSMutableArray *titles = [NSMutableArray arrayWithCapacity:attributionInfos.count]; for (MGLAttributionInfo *info in attributionInfos) { NSMutableAttributedString *title = info.title.mutableCopy; @@ -259,7 +259,7 @@ } } -- (void)growArrayByAddingAttributionInfosFromArray:(NS_ARRAY_OF(MGLAttributionInfo *) *)infos { +- (void)growArrayByAddingAttributionInfosFromArray:(NSArray<MGLAttributionInfo *> *)infos { for (MGLAttributionInfo *info in infos) { [self growArrayByAddingAttributionInfo:info]; } diff --git a/platform/darwin/src/MGLAttributionInfo_Private.h b/platform/darwin/src/MGLAttributionInfo_Private.h index c639752ac3..85c9ed796f 100644 --- a/platform/darwin/src/MGLAttributionInfo_Private.h +++ b/platform/darwin/src/MGLAttributionInfo_Private.h @@ -16,9 +16,9 @@ NS_ASSUME_NONNULL_BEGIN @param fontSize The default text size in points. @param linkColor The default link color. */ -+ (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; ++ (NSArray<MGLAttributionInfo *> *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; -+ (NSAttributedString *)attributedStringForAttributionInfos:(NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos; ++ (NSAttributedString *)attributedStringForAttributionInfos:(NSArray<MGLAttributionInfo *> *)attributionInfos; /** Returns a copy of the `URL` property modified to account for the given style @@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN @param infos An array of info objects to add to the receiver. */ -- (void)growArrayByAddingAttributionInfosFromArray:(NS_ARRAY_OF(MGLAttributionInfo *) *)infos; +- (void)growArrayByAddingAttributionInfosFromArray:(NSArray<MGLAttributionInfo *> *)infos; @end diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.h b/platform/darwin/src/MGLBackgroundStyleLayer.h index d5c3ed5403..31755c8bad 100644 --- a/platform/darwin/src/MGLBackgroundStyleLayer.h +++ b/platform/darwin/src/MGLBackgroundStyleLayer.h @@ -96,7 +96,7 @@ which it is added. The opacity at which the background will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.mm b/platform/darwin/src/MGLBackgroundStyleLayer.mm index 993645d3a8..acea3441fa 100644 --- a/platform/darwin/src/MGLBackgroundStyleLayer.mm +++ b/platform/darwin/src/MGLBackgroundStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h index 06b4de32f0..69b6e41c9c 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.h +++ b/platform/darwin/src/MGLCircleStyleLayer.h @@ -117,7 +117,7 @@ MGL_EXPORT full opacity. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -186,7 +186,7 @@ MGL_EXPORT The opacity at which the circle will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -235,7 +235,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 5. Set this property to `nil` to reset it to the default value. + `5`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -334,7 +334,7 @@ MGL_EXPORT The opacity of the circle's stroke. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -361,7 +361,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLCircleStyleLayer.mm b/platform/darwin/src/MGLCircleStyleLayer.mm index 0be3920987..b03ab8a7a6 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.mm +++ b/platform/darwin/src/MGLCircleStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" @@ -75,7 +75,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLCompassDirectionFormatter.m b/platform/darwin/src/MGLCompassDirectionFormatter.m index 5f0cfae6f7..1ac6a82162 100644 --- a/platform/darwin/src/MGLCompassDirectionFormatter.m +++ b/platform/darwin/src/MGLCompassDirectionFormatter.m @@ -15,8 +15,8 @@ } - (NSString *)stringFromDirection:(CLLocationDirection)direction { - static NS_ARRAY_OF(NSString *) *shortStrings; - static NS_ARRAY_OF(NSString *) *longStrings; + static NSArray<NSString *> *shortStrings; + static NSArray<NSString *> *longStrings; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shortStrings = @[ diff --git a/platform/darwin/src/MGLComputedShapeSource.h b/platform/darwin/src/MGLComputedShapeSource.h index 068c49245c..7e0037df89 100644 --- a/platform/darwin/src/MGLComputedShapeSource.h +++ b/platform/darwin/src/MGLComputedShapeSource.h @@ -101,7 +101,7 @@ MGL_EXPORT @param identifier A string that uniquely identifies the source. @param options An `NSDictionary` of options for this source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options NS_DESIGNATED_INITIALIZER; /** Returns a custom shape data source initialized with an identifier, data source, and a @@ -120,7 +120,7 @@ MGL_EXPORT @param identifier A string that uniquely identifies the source. @param options An `NSDictionary` of options for this source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MGLComputedShapeSourceDataSource>)dataSource options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options; +- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MGLComputedShapeSourceDataSource>)dataSource options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options; /** Invalidates all the features and properties intersecting with or contained in diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm index fb25eb8eb4..04734d0ef5 100644 --- a/platform/darwin/src/MGLComputedShapeSource.mm +++ b/platform/darwin/src/MGLComputedShapeSource.mm @@ -13,7 +13,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates = @"MGLShapeSourceOptionWrapsCoordinates"; const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates = @"MGLShapeSourceOptionClipsCoordinates"; -mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) { +mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NSDictionary<MGLShapeSourceOption, id> *options) { mbgl::style::CustomGeometrySource::Options sourceOptions; if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) { @@ -148,7 +148,7 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi @implementation MGLComputedShapeSource -- (instancetype)initWithIdentifier:(NSString *)identifier options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier options:(NSDictionary<MGLShapeSourceOption, id> *)options { NSOperationQueue *requestQueue = [[NSOperationQueue alloc] init]; requestQueue.name = [NSString stringWithFormat:@"mgl.MGLComputedShapeSource.%@", identifier]; requestQueue.qualityOfService = NSQualityOfServiceUtility; @@ -176,7 +176,7 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi return self; } -- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MGLComputedShapeSourceDataSource>)dataSource options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MGLComputedShapeSourceDataSource>)dataSource options:(NSDictionary<MGLShapeSourceOption, id> *)options { if (self = [self initWithIdentifier:identifier options:options]) { [self setDataSource:dataSource]; } diff --git a/platform/darwin/src/MGLComputedShapeSource_Private.h b/platform/darwin/src/MGLComputedShapeSource_Private.h index e1887caf8d..ec075e4bd7 100644 --- a/platform/darwin/src/MGLComputedShapeSource_Private.h +++ b/platform/darwin/src/MGLComputedShapeSource_Private.h @@ -7,6 +7,6 @@ NS_ASSUME_NONNULL_BEGIN MGL_EXPORT -mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options); +mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NSDictionary<MGLShapeSourceOption, id> *options); NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h index d0c9e26062..62471abb16 100644 --- a/platform/darwin/src/MGLFeature.h +++ b/platform/darwin/src/MGLFeature.h @@ -148,7 +148,7 @@ NS_ASSUME_NONNULL_BEGIN when the feature instance is used to initialize an `MGLShapeSource` and that source is added to the map and styled. */ -@property (nonatomic, copy) NS_DICTIONARY_OF(NSString *, id) *attributes; +@property (nonatomic, copy) NSDictionary<NSString *, id> *attributes; /** Returns the feature attribute for the given attribute name. @@ -167,7 +167,7 @@ NS_ASSUME_NONNULL_BEGIN `attributes` property, and an `id` key corresponding to the receiver’s `identifier` property. */ -- (NS_DICTIONARY_OF(NSString *, id) *)geoJSONDictionary; +- (NSDictionary<NSString *, id> *)geoJSONDictionary; @end @@ -252,9 +252,9 @@ MGL_EXPORT MGL_EXPORT @interface MGLShapeCollectionFeature : MGLShapeCollection <MGLFeature> -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLShape<MGLFeature> *) *shapes; +@property (nonatomic, copy, readonly) NSArray<MGLShape<MGLFeature> *> *shapes; -+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)shapes; ++ (instancetype)shapeCollectionWithShapes:(NSArray<MGLShape<MGLFeature> *> *)shapes; @end diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm index ee2c71be21..02f67dca6e 100644 --- a/platform/darwin/src/MGLFeature.mm +++ b/platform/darwin/src/MGLFeature.mm @@ -233,7 +233,7 @@ MGL_DEFINE_FEATURE_IS_EQUAL(); @dynamic shapes; -+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)shapes { ++ (instancetype)shapeCollectionWithShapes:(NSArray<MGLShape<MGLFeature> *> *)shapes { return [super shapeCollectionWithShapes:shapes]; } @@ -373,7 +373,7 @@ public: } }; -NS_ARRAY_OF(MGLShape <MGLFeature> *) *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features) { +NSArray<MGLShape <MGLFeature> *> *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features) { NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()]; for (const auto &feature : features) { [shapes addObject:MGLFeatureFromMBGLFeature(feature)]; @@ -414,7 +414,7 @@ mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary *at return feature; } -NS_DICTIONARY_OF(NSString *, id) *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier) { +NSDictionary<NSString *, id> *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier) { NSMutableDictionary *feature = [@{@"type": @"Feature", @"properties": (attributes) ?: [NSNull null], @"geometry": geometry} mutableCopy]; diff --git a/platform/darwin/src/MGLFeature_Private.h b/platform/darwin/src/MGLFeature_Private.h index 4137200b98..d4074b53ed 100644 --- a/platform/darwin/src/MGLFeature_Private.h +++ b/platform/darwin/src/MGLFeature_Private.h @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN vector tile features. */ MGL_EXPORT -NS_ARRAY_OF(MGLShape <MGLFeature> *) *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features); +NSArray<MGLShape <MGLFeature> *> *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features); /** Returns an `MGLFeature` object converted from the given mbgl::Feature @@ -36,7 +36,7 @@ mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary *at /** Returns an `NSDictionary` representation of an `MGLFeature`. */ -NS_DICTIONARY_OF(NSString *, id) *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier); +NSDictionary<NSString *, id> *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier); NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h index 7c3a0773e4..bca2a99f1e 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.h +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h @@ -78,7 +78,7 @@ MGL_EXPORT This property is measured in meters. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `fillExtrusionHeight` is non-`nil`. Otherwise, it is ignored. @@ -164,7 +164,7 @@ MGL_EXPORT This property is measured in meters. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -189,7 +189,7 @@ MGL_EXPORT per-layer, not per-feature, basis, and data-driven styling is not available. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm index 688ce4c1ac..4f3bfee18e 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" @@ -65,7 +65,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLFillStyleLayer.h b/platform/darwin/src/MGLFillStyleLayer.h index a159a924e6..af82482c62 100644 --- a/platform/darwin/src/MGLFillStyleLayer.h +++ b/platform/darwin/src/MGLFillStyleLayer.h @@ -151,7 +151,7 @@ MGL_EXPORT value will also affect the 1pt stroke around the fill, if the stroke is used. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -218,7 +218,8 @@ MGL_EXPORT /** Name of image in sprite to use for drawing image fills. For seamless patterns, - image width and height must be a factor of two (2, 4, 8, ..., 512). + image width and height must be a factor of two (2, 4, 8, ..., 512). Note that + zoom-dependent expressions will be evaluated only at integer zoom levels. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLFillStyleLayer.mm b/platform/darwin/src/MGLFillStyleLayer.mm index c975e28d6b..12e3643ce6 100644 --- a/platform/darwin/src/MGLFillStyleLayer.mm +++ b/platform/darwin/src/MGLFillStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" @@ -65,7 +65,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h index 0bff9b09f5..d0d9446a5f 100644 --- a/platform/darwin/src/MGLGeometry_Private.h +++ b/platform/darwin/src/MGLGeometry_Private.h @@ -71,6 +71,17 @@ NS_INLINE MGLCoordinateQuad MGLCoordinateQuadFromLatLngArray(std::array<mbgl::La MGLLocationCoordinate2DFromLatLng(quad[1]) }; } +/** + YES if the coordinate is valid or NO if it is not. + Considers extended coordinates. + */ +NS_INLINE BOOL MGLLocationCoordinate2DIsValid(CLLocationCoordinate2D coordinate) { + return (coordinate.latitude <= 90.0 && + coordinate.latitude >= -90.0 && + coordinate.longitude <= 360.0 && + coordinate.longitude >= -360.0); +} + #if TARGET_OS_IPHONE NS_INLINE mbgl::EdgeInsets MGLEdgeInsetsFromNSEdgeInsets(UIEdgeInsets insets) { return { insets.top, insets.left, insets.bottom, insets.right }; diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.h b/platform/darwin/src/MGLHeatmapStyleLayer.h index ad7ba5de01..167c5bafbe 100644 --- a/platform/darwin/src/MGLHeatmapStyleLayer.h +++ b/platform/darwin/src/MGLHeatmapStyleLayer.h @@ -116,7 +116,7 @@ MGL_EXPORT Primarily used for adjusting the heatmap based on zoom level. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -142,7 +142,7 @@ MGL_EXPORT The global opacity at which the heatmap layer will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -171,7 +171,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 30. Set this property to `nil` to reset it to the default value. + `30`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -197,7 +197,7 @@ MGL_EXPORT Especially useful when combined with clustering. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.mm b/platform/darwin/src/MGLHeatmapStyleLayer.mm index a394dbda3b..925b3ac750 100644 --- a/platform/darwin/src/MGLHeatmapStyleLayer.mm +++ b/platform/darwin/src/MGLHeatmapStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" @@ -56,7 +56,7 @@ { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.h b/platform/darwin/src/MGLHillshadeStyleLayer.h index 224680160a..45b0e66751 100644 --- a/platform/darwin/src/MGLHillshadeStyleLayer.h +++ b/platform/darwin/src/MGLHillshadeStyleLayer.h @@ -130,7 +130,7 @@ MGL_EXPORT Intensity of the hillshade The default value of this property is an expression that evaluates to the float - 0.5. Set this property to `nil` to reset it to the default value. + `0.5`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -232,7 +232,7 @@ MGL_EXPORT `hillshadeIlluminationAnchor` is set to `MGLHillshadeIlluminationAnchorMap`. The default value of this property is an expression that evaluates to the float - 335. Set this property to `nil` to reset it to the default value. + `335`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.mm b/platform/darwin/src/MGLHillshadeStyleLayer.mm index 2383c1ce26..70fab24e33 100644 --- a/platform/darwin/src/MGLHillshadeStyleLayer.mm +++ b/platform/darwin/src/MGLHillshadeStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h index cf4aa50112..13c925d9bd 100644 --- a/platform/darwin/src/MGLLight.h +++ b/platform/darwin/src/MGLLight.h @@ -188,7 +188,7 @@ MGL_EXPORT more extreme contrast. The default value of this property is an expression that evaluates to the float - 0.5. + `0.5`. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h index a7510142fc..1080244bdb 100644 --- a/platform/darwin/src/MGLLineStyleLayer.h +++ b/platform/darwin/src/MGLLineStyleLayer.h @@ -180,7 +180,7 @@ MGL_EXPORT Used to automatically convert miter joins to bevel joins for sharp angles. The default value of this property is an expression that evaluates to the float - 2. Set this property to `nil` to reset it to the default value. + `2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `lineJoin` is set to an expression that evaluates to `miter`. Otherwise, it is ignored. @@ -202,7 +202,7 @@ MGL_EXPORT Used to automatically convert round joins to miter joins for shallow angles. The default value of this property is an expression that evaluates to the float - 1.05. Set this property to `nil` to reset it to the default value. + `1.05`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `lineJoin` is set to an expression that evaluates to `round`. Otherwise, it is ignored. @@ -228,7 +228,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -302,7 +302,10 @@ MGL_EXPORT /** Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash - length to points, multiply the length by the current line width. + length to points, multiply the length by the current line width. Note that + GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to + the expected scale. Also note that zoom-dependent expressions will be evaluated + only at integer zoom levels. This property is measured in line widths. @@ -343,7 +346,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -372,7 +375,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -396,7 +399,7 @@ MGL_EXPORT The opacity at which the line will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -542,7 +545,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm index 619cc70afe..84412073cd 100644 --- a/platform/darwin/src/MGLLineStyleLayer.mm +++ b/platform/darwin/src/MGLLineStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" @@ -77,7 +77,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm index 11a5442761..923c8fb2a1 100644 --- a/platform/darwin/src/MGLMapSnapshotter.mm +++ b/platform/darwin/src/MGLMapSnapshotter.mm @@ -89,7 +89,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; std::shared_ptr<mbgl::ThreadPool> _mbglThreadPool; std::unique_ptr<mbgl::MapSnapshotter> _mbglMapSnapshotter; std::unique_ptr<mbgl::Actor<mbgl::MapSnapshotter::Callback>> _snapshotCallback; - NS_ARRAY_OF(MGLAttributionInfo *) *_attributionInfo; + NSArray<MGLAttributionInfo *> *_attributionInfo; } @@ -122,8 +122,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; _snapshotCallback = std::make_unique<mbgl::Actor<mbgl::MapSnapshotter::Callback>>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn) { __typeof__(self) strongSelf = weakSelf; strongSelf.loading = false; - - + if (mbglError) { NSString *description = @(mbgl::util::toString(mbglError).c_str()); NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description}; @@ -145,9 +144,13 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; } _snapshotCallback = NULL; }); - dispatch_async(queue, ^{ - _mbglMapSnapshotter->snapshot(_snapshotCallback->self()); + dispatch_async(queue, ^{ + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + strongSelf->_mbglMapSnapshotter->snapshot(strongSelf->_snapshotCallback->self()); }); } diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm index 240dad9614..d4518e3d8f 100644 --- a/platform/darwin/src/MGLMultiPoint.mm +++ b/platform/darwin/src/MGLMultiPoint.mm @@ -70,7 +70,7 @@ return _coordinates.size(); } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPointCount ++ (NSSet<NSString *> *)keyPathsForValuesAffectingPointCount { return [NSSet setWithObjects:@"coordinates", nil]; } @@ -163,7 +163,7 @@ if (!_bounds) { mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); for (auto coordinate : _coordinates) { - if (!CLLocationCoordinate2DIsValid(coordinate)) { + if (!MGLLocationCoordinate2DIsValid(coordinate)) { bounds = mbgl::LatLngBounds::empty(); break; } diff --git a/platform/darwin/src/MGLOfflinePack.mm b/platform/darwin/src/MGLOfflinePack.mm index 60a7b55531..4653021a58 100644 --- a/platform/darwin/src/MGLOfflinePack.mm +++ b/platform/darwin/src/MGLOfflinePack.mm @@ -227,19 +227,22 @@ NSError *MGLErrorFromResponseError(mbgl::Response::Error error) { @end void MBGLOfflineRegionObserver::statusChanged(mbgl::OfflineRegionStatus status) { + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack offlineRegionStatusDidChange:status]; + [weakPack offlineRegionStatusDidChange:status]; }); } void MBGLOfflineRegionObserver::responseError(mbgl::Response::Error error) { + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack didReceiveError:MGLErrorFromResponseError(error)]; + [weakPack didReceiveError:MGLErrorFromResponseError(error)]; }); } void MBGLOfflineRegionObserver::mapboxTileCountLimitExceeded(uint64_t limit) { + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack didReceiveMaximumAllowedMapboxTiles:limit]; + [weakPack didReceiveMaximumAllowedMapboxTiles:limit]; }); } diff --git a/platform/darwin/src/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h index a1f3e686c2..ab36592634 100644 --- a/platform/darwin/src/MGLOfflineStorage.h +++ b/platform/darwin/src/MGLOfflineStorage.h @@ -199,7 +199,7 @@ MGL_EXPORT `packs` property, observe KVO change notifications on the `packs` key path. The initial load results in an `NSKeyValueChangeSetting` change. */ -@property (nonatomic, strong, readonly, nullable) NS_ARRAY_OF(MGLOfflinePack *) *packs; +@property (nonatomic, strong, readonly, nullable) NSArray<MGLOfflinePack *> *packs; /** Creates and registers an offline pack that downloads the resources needed to diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 4d999144e8..f4e454534d 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -32,7 +32,7 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu @interface MGLOfflineStorage () -@property (nonatomic, strong, readwrite) NS_MUTABLE_ARRAY_OF(MGLOfflinePack *) *packs; +@property (nonatomic, strong, readwrite) NSMutableArray<MGLOfflinePack *> *packs; @property (nonatomic) mbgl::DefaultFileSource *mbglFileSource; @property (nonatomic, getter=isPaused) BOOL paused; @@ -243,7 +243,7 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu _mbglFileSource = nullptr; } -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS_DICTIONARY_OF(NSString *, id) *)change context:(void *)context { +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context { // Synchronize the file source’s access token with the global one in MGLAccountManager. if ([keyPath isEqualToString:@"accessToken"] && object == [MGLAccountManager sharedManager]) { NSString *accessToken = change[NSKeyValueChangeNewKey]; @@ -336,7 +336,7 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu } - (void)reloadPacks { - [self getPacksWithCompletionHandler:^(NS_ARRAY_OF(MGLOfflinePack *) *packs, __unused NSError * _Nullable error) { + [self getPacksWithCompletionHandler:^(NSArray<MGLOfflinePack *> *packs, __unused NSError * _Nullable error) { for (MGLOfflinePack *pack in self.packs) { [pack invalidate]; } @@ -344,7 +344,7 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu }]; } -- (void)getPacksWithCompletionHandler:(void (^)(NS_ARRAY_OF(MGLOfflinePack *) *packs, NSError * _Nullable error))completion { +- (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) { NSError *error; if (exception) { diff --git a/platform/darwin/src/MGLOverlay.h b/platform/darwin/src/MGLOverlay.h index 462a0c1031..7706b741e2 100644 --- a/platform/darwin/src/MGLOverlay.h +++ b/platform/darwin/src/MGLOverlay.h @@ -30,6 +30,11 @@ NS_ASSUME_NONNULL_BEGIN This property contains the smallest rectangle that completely encompasses the overlay. Implementers of this protocol must set this area when implementing their overlay class, and after setting it, you must not change it. + + If this overlay spans the antimeridian, its bounds may extend west of −180 degrees + longitude or east of 180 degrees longitude. For example, an overlay covering the + Pacific Ocean from Tokyo to San Francisco might have a bounds extending + from (35.68476, −220.24257) to (37.78428, −122.41310). */ @property (nonatomic, readonly) MGLCoordinateBounds overlayBounds; diff --git a/platform/darwin/src/MGLPointCollection.mm b/platform/darwin/src/MGLPointCollection.mm index 8f20d91a42..efb9497a1f 100644 --- a/platform/darwin/src/MGLPointCollection.mm +++ b/platform/darwin/src/MGLPointCollection.mm @@ -54,7 +54,7 @@ NS_ASSUME_NONNULL_BEGIN if (!_bounds) { mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); for (auto coordinate : _coordinates) { - if (!CLLocationCoordinate2DIsValid(coordinate)) { + if (!MGLLocationCoordinate2DIsValid(coordinate)) { bounds = mbgl::LatLngBounds::empty(); break; } diff --git a/platform/darwin/src/MGLPolygon.h b/platform/darwin/src/MGLPolygon.h index 190b6df9c5..810a8b78ae 100644 --- a/platform/darwin/src/MGLPolygon.h +++ b/platform/darwin/src/MGLPolygon.h @@ -57,7 +57,7 @@ MGL_EXPORT If there are no interior polygons, the value of this property is `nil`. */ -@property (nonatomic, nullable, readonly) NS_ARRAY_OF(MGLPolygon *) *interiorPolygons; +@property (nonatomic, nullable, readonly) NSArray<MGLPolygon *> *interiorPolygons; /** Creates and returns an `MGLPolygon` object from the specified set of @@ -82,7 +82,7 @@ MGL_EXPORT is considered to have no interior polygons. @return A new polygon object. */ -+ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(nullable NS_ARRAY_OF(MGLPolygon *) *)interiorPolygons; ++ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(nullable NSArray<MGLPolygon *> *)interiorPolygons; @end @@ -109,7 +109,7 @@ MGL_EXPORT /** An array of polygons forming the multipolygon. */ -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLPolygon *) *polygons; +@property (nonatomic, copy, readonly) NSArray<MGLPolygon *> *polygons; /** Creates and returns a multipolygon object consisting of the given polygons. @@ -117,7 +117,7 @@ MGL_EXPORT @param polygons The array of polygons defining the shape. @return A new multipolygon object. */ -+ (instancetype)multiPolygonWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons; ++ (instancetype)multiPolygonWithPolygons:(NSArray<MGLPolygon *> *)polygons; @end diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm index 2af768d514..b80504707b 100644 --- a/platform/darwin/src/MGLPolygon.mm +++ b/platform/darwin/src/MGLPolygon.mm @@ -102,7 +102,7 @@ @"coordinates": self.mgl_coordinates}; } -- (NS_ARRAY_OF(id) *)mgl_coordinates { +- (NSArray<id> *)mgl_coordinates { NSMutableArray *coordinates = [NSMutableArray array]; NSMutableArray *exteriorRing = [NSMutableArray array]; @@ -128,7 +128,7 @@ @interface MGLMultiPolygon () -@property (nonatomic, copy, readwrite) NS_ARRAY_OF(MGLPolygon *) *polygons; +@property (nonatomic, copy, readwrite) NSArray<MGLPolygon *> *polygons; @end @@ -138,11 +138,11 @@ @synthesize overlayBounds = _overlayBounds; -+ (instancetype)multiPolygonWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons { ++ (instancetype)multiPolygonWithPolygons:(NSArray<MGLPolygon *> *)polygons { return [[self alloc] initWithPolygons:polygons]; } -- (instancetype)initWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons { +- (instancetype)initWithPolygons:(NSArray<MGLPolygon *> *)polygons { if (self = [super init]) { _polygons = polygons; diff --git a/platform/darwin/src/MGLPolygon_Private.h b/platform/darwin/src/MGLPolygon_Private.h index 75afcd61f6..b006f2d77f 100644 --- a/platform/darwin/src/MGLPolygon_Private.h +++ b/platform/darwin/src/MGLPolygon_Private.h @@ -4,7 +4,7 @@ NS_ASSUME_NONNULL_BEGIN @interface MGLPolygon (Private) -- (NS_ARRAY_OF(id) *)mgl_coordinates; +- (NSArray<id> *)mgl_coordinates; @end diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h index b1fca5bf28..8e9007686b 100644 --- a/platform/darwin/src/MGLPolyline.h +++ b/platform/darwin/src/MGLPolyline.h @@ -92,7 +92,7 @@ MGL_EXPORT /** An array of polygons forming the multipolyline. */ -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLPolyline *) *polylines; +@property (nonatomic, copy, readonly) NSArray<MGLPolyline *> *polylines; /** Creates and returns a multipolyline object consisting of the given polylines. @@ -100,7 +100,7 @@ MGL_EXPORT @param polylines The array of polylines defining the shape. @return A new multipolyline object. */ -+ (instancetype)multiPolylineWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines; ++ (instancetype)multiPolylineWithPolylines:(NSArray<MGLPolyline *> *)polylines; @end diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm index 26e3518cd8..a028db8176 100644 --- a/platform/darwin/src/MGLPolyline.mm +++ b/platform/darwin/src/MGLPolyline.mm @@ -49,7 +49,7 @@ @"coordinates": self.mgl_coordinates}; } -- (NS_ARRAY_OF(id) *)mgl_coordinates { +- (NSArray<id> *)mgl_coordinates { NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount]; for (NSUInteger index = 0; index < self.pointCount; index++) { CLLocationCoordinate2D coordinate = self.coordinates[index]; @@ -123,7 +123,7 @@ @interface MGLMultiPolyline () -@property (nonatomic, copy, readwrite) NS_ARRAY_OF(MGLPolyline *) *polylines; +@property (nonatomic, copy, readwrite) NSArray<MGLPolyline *> *polylines; @end @@ -133,11 +133,11 @@ @synthesize overlayBounds = _overlayBounds; -+ (instancetype)multiPolylineWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines { ++ (instancetype)multiPolylineWithPolylines:(NSArray<MGLPolyline *> *)polylines { return [[self alloc] initWithPolylines:polylines]; } -- (instancetype)initWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines { +- (instancetype)initWithPolylines:(NSArray<MGLPolyline *> *)polylines { if (self = [super init]) { _polylines = polylines; diff --git a/platform/darwin/src/MGLPolyline_Private.h b/platform/darwin/src/MGLPolyline_Private.h index 405a0c5bc9..ff4fabaa78 100644 --- a/platform/darwin/src/MGLPolyline_Private.h +++ b/platform/darwin/src/MGLPolyline_Private.h @@ -4,7 +4,7 @@ NS_ASSUME_NONNULL_BEGIN @interface MGLPolyline (Private) -- (NS_ARRAY_OF(id) *)mgl_coordinates; +- (NSArray<id> *)mgl_coordinates; @end diff --git a/platform/darwin/src/MGLRasterStyleLayer.h b/platform/darwin/src/MGLRasterStyleLayer.h index bca9649e5d..ff055d24f6 100644 --- a/platform/darwin/src/MGLRasterStyleLayer.h +++ b/platform/darwin/src/MGLRasterStyleLayer.h @@ -64,7 +64,7 @@ MGL_EXPORT brightness. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-max"><code>raster-brightness-max</code></a> @@ -97,7 +97,7 @@ MGL_EXPORT brightness. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-brightness-min"><code>raster-brightness-min</code></a> @@ -129,7 +129,7 @@ MGL_EXPORT Increase or reduce the contrast of the image. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -157,7 +157,7 @@ MGL_EXPORT This property is measured in milliseconds. The default value of this property is an expression that evaluates to the float - 300. Set this property to `nil` to reset it to the default value. + `300`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -178,7 +178,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This attribute corresponds to the <a href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-raster-hue-rotate"><code>raster-hue-rotate</code></a> @@ -210,7 +210,7 @@ MGL_EXPORT The opacity at which the image will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -236,7 +236,7 @@ MGL_EXPORT Increase or reduce the saturation of the image. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLRasterStyleLayer.mm b/platform/darwin/src/MGLRasterStyleLayer.mm index 94a58409de..0e31512491 100644 --- a/platform/darwin/src/MGLRasterStyleLayer.mm +++ b/platform/darwin/src/MGLRasterStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLRasterTileSource.h b/platform/darwin/src/MGLRasterTileSource.h index 59b256d5e5..f27cbc285f 100644 --- a/platform/darwin/src/MGLRasterTileSource.h +++ b/platform/darwin/src/MGLRasterTileSource.h @@ -127,7 +127,7 @@ MGL_EXPORT the default values. @return An initialized tile source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MGLTileSourceOption, id> *)options NS_DESIGNATED_INITIALIZER; @end diff --git a/platform/darwin/src/MGLRasterTileSource.mm b/platform/darwin/src/MGLRasterTileSource.mm index 02cfef4ae8..61e9ef97fd 100644 --- a/platform/darwin/src/MGLRasterTileSource.mm +++ b/platform/darwin/src/MGLRasterTileSource.mm @@ -44,7 +44,7 @@ static const CGFloat MGLRasterTileSourceRetinaTileSize = 512; uint16_t(round(tileSize))); } -- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MGLTileSourceOption, id> *)options { mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options); uint16_t tileSize = MGLRasterTileSourceRetinaTileSize; diff --git a/platform/darwin/src/MGLShapeCollection.h b/platform/darwin/src/MGLShapeCollection.h index bec482ca61..08f3276496 100644 --- a/platform/darwin/src/MGLShapeCollection.h +++ b/platform/darwin/src/MGLShapeCollection.h @@ -40,7 +40,7 @@ MGL_EXPORT /** An array of shapes forming the shape collection. */ -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLShape *) *shapes; +@property (nonatomic, copy, readonly) NSArray<MGLShape *> *shapes; /** Creates and returns a shape collection consisting of the given shapes. @@ -49,7 +49,7 @@ MGL_EXPORT this array is copied to the new object. @return A new shape collection object. */ -+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes; ++ (instancetype)shapeCollectionWithShapes:(NSArray<MGLShape *> *)shapes; @end diff --git a/platform/darwin/src/MGLShapeCollection.mm b/platform/darwin/src/MGLShapeCollection.mm index 03cab0043f..74e78a764a 100644 --- a/platform/darwin/src/MGLShapeCollection.mm +++ b/platform/darwin/src/MGLShapeCollection.mm @@ -6,11 +6,11 @@ @implementation MGLShapeCollection -+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes { ++ (instancetype)shapeCollectionWithShapes:(NSArray<MGLShape *> *)shapes { return [[self alloc] initWithShapes:shapes]; } -- (instancetype)initWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes { +- (instancetype)initWithShapes:(NSArray<MGLShape *> *)shapes { if (self = [super init]) { _shapes = shapes.copy; } diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h index 6fa93476be..1fc00d4de0 100644 --- a/platform/darwin/src/MGLShapeSource.h +++ b/platform/darwin/src/MGLShapeSource.h @@ -154,7 +154,7 @@ MGL_EXPORT @param options An `NSDictionary` of options for this source. @return An initialized shape source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options NS_DESIGNATED_INITIALIZER; /** Returns a shape source with an identifier, a shape, and dictionary of options @@ -183,7 +183,7 @@ MGL_EXPORT @param options An `NSDictionary` of options for this source. @return An initialized shape source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options NS_DESIGNATED_INITIALIZER; /** Returns a shape source with an identifier, an array of features, and a dictionary @@ -210,7 +210,7 @@ MGL_EXPORT @param options An `NSDictionary` of options for this source. @return An initialized shape source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options; +- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray<MGLShape<MGLFeature> *> *)features options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options; /** Returns a shape source with an identifier, an array of shapes, and a dictionary of @@ -238,7 +238,7 @@ MGL_EXPORT @param options An `NSDictionary` of options for this source. @return An initialized shape source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options; +- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NSArray<MGLShape *> *)shapes options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options; #pragma mark Accessing a Source’s Content @@ -291,7 +291,7 @@ MGL_EXPORT @return An array of objects conforming to the `MGLFeature` protocol that represent features in the source that match the predicate. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)featuresMatchingPredicate:(nullable NSPredicate *)predicate; +- (NSArray<id <MGLFeature>> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate; @end diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm index dcc3fd97f5..1425269012 100644 --- a/platform/darwin/src/MGLShapeSource.mm +++ b/platform/darwin/src/MGLShapeSource.mm @@ -6,7 +6,7 @@ #import "MGLFeature_Private.h" #import "MGLShape_Private.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSURL+MGLAdditions.h" #include <mbgl/map/map.hpp> @@ -21,7 +21,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @ const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel"; const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance"; -mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) { +mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShapeSourceOption, id> *options) { auto geoJSONOptions = mbgl::style::GeoJSONOptions(); if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) { @@ -92,7 +92,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL @implementation MGLShapeSource -- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(NS_DICTIONARY_OF(NSString *, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options { auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options); auto source = std::make_unique<mbgl::style::GeoJSONSource>(identifier.UTF8String, geoJSONOptions); if (self = [super initWithPendingSource:std::move(source)]) { @@ -101,7 +101,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL return self; } -- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(NSDictionary<MGLShapeSourceOption, id> *)options { auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options); auto source = std::make_unique<mbgl::style::GeoJSONSource>(identifier.UTF8String, geoJSONOptions); if (self = [super initWithPendingSource:std::move(source)]) { @@ -110,7 +110,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL return self; } -- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray<MGLShape<MGLFeature> *> *)features options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options { for (id <MGLFeature> feature in features) { if (![feature conformsToProtocol:@protocol(MGLFeature)]) { [NSException raise:NSInvalidArgumentException format:@"The object %@ included in the features argument does not conform to the MGLFeature protocol.", feature]; @@ -120,7 +120,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL return [self initWithIdentifier:identifier shape:shapeCollectionFeature options:options]; } -- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NSArray<MGLShape *> *)shapes options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options { MGLShapeCollection *shapeCollection = [MGLShapeCollection shapeCollectionWithShapes:shapes]; return [self initWithIdentifier:identifier shape:shapeCollection options:options]; } @@ -153,7 +153,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL NSStringFromClass([self class]), (void *)self, self.identifier, self.URL, self.shape]; } -- (NS_ARRAY_OF(id <MGLFeature>) *)featuresMatchingPredicate:(nullable NSPredicate *)predicate { +- (NSArray<id <MGLFeature>> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate { mbgl::optional<mbgl::style::Filter> optionalFilter; if (predicate) { diff --git a/platform/darwin/src/MGLShapeSource_Private.h b/platform/darwin/src/MGLShapeSource_Private.h index 0720074d1d..83872afcbc 100644 --- a/platform/darwin/src/MGLShapeSource_Private.h +++ b/platform/darwin/src/MGLShapeSource_Private.h @@ -10,6 +10,6 @@ namespace mbgl { } MGL_EXPORT -mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options); +mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShapeSourceOption, id> *options); NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index 6df627d7af..814a09ed21 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -241,7 +241,7 @@ MGL_EXPORT /** A set containing the style’s sources. */ -@property (nonatomic, strong) NS_SET_OF(__kindof MGLSource *) *sources; +@property (nonatomic, strong) NSSet<__kindof MGLSource *> *sources; /** Values describing animated transitions to changes on a style's individual @@ -303,7 +303,7 @@ MGL_EXPORT The layers included in the style, arranged according to their back-to-front ordering on the screen. */ -@property (nonatomic, strong) NS_ARRAY_OF(__kindof MGLStyleLayer *) *layers; +@property (nonatomic, strong) NSArray<__kindof MGLStyleLayer *> *layers; /** Returns a style layer with the given identifier in the current style. @@ -417,7 +417,7 @@ MGL_EXPORT #pragma mark Managing Style Classes -@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((unavailable("Support for style classes has been removed."))); +@property (nonatomic) NSArray<NSString *> *styleClasses __attribute__((unavailable("Support for style classes has been removed."))); - (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed."))); diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 867ac6c451..3f9bfbf8ca 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -82,8 +82,8 @@ @property (nonatomic, readonly, weak) MGLMapView *mapView; @property (nonatomic, readonly) mbgl::style::Style *rawStyle; @property (readonly, copy, nullable) NSURL *URL; -@property (nonatomic, readwrite, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers; -@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier; +@property (nonatomic, readwrite, strong) NSMutableDictionary<NSString *, MGLOpenGLStyleLayer *> *openGLLayers; +@property (nonatomic) NSMutableDictionary<NSString *, NSDictionary<NSObject *, MGLTextLanguage *> *> *localizedLayersByIdentifier; @end @@ -142,9 +142,9 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, #pragma mark Sources -- (NS_SET_OF(__kindof MGLSource *) *)sources { +- (NSSet<__kindof MGLSource *> *)sources { auto rawSources = self.rawStyle->getSources(); - NS_MUTABLE_SET_OF(__kindof MGLSource *) *sources = [NSMutableSet setWithCapacity:rawSources.size()]; + NSMutableSet<__kindof MGLSource *> *sources = [NSMutableSet setWithCapacity:rawSources.size()]; for (auto rawSource = rawSources.begin(); rawSource != rawSources.end(); ++rawSource) { MGLSource *source = [self sourceFromMBGLSource:*rawSource]; [sources addObject:source]; @@ -152,7 +152,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, return sources; } -- (void)setSources:(NS_SET_OF(__kindof MGLSource *) *)sources { +- (void)setSources:(NSSet<__kindof MGLSource *> *)sources { for (MGLSource *source in self.sources) { [self removeSource:source]; } @@ -225,7 +225,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, [source removeFromMapView:self.mapView]; } -- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { +- (nullable NSArray<MGLAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { // It’d be incredibly convenient to use -sources here, but this operation // depends on the sources being sorted in ascending order by creation, as // with the std::vector used in mbgl. @@ -245,10 +245,10 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, #pragma mark Style layers -- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers +- (NSArray<__kindof MGLStyleLayer *> *)layers { auto layers = self.rawStyle->getLayers(); - NS_MUTABLE_ARRAY_OF(__kindof MGLStyleLayer *) *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()]; + NSMutableArray<__kindof MGLStyleLayer *> *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()]; for (auto layer : layers) { MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:layer]; [styleLayers addObject:styleLayer]; @@ -256,7 +256,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, return styleLayers; } -- (void)setLayers:(NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers { +- (void)setLayers:(NSArray<__kindof MGLStyleLayer *> *)layers { for (MGLStyleLayer *layer in self.layers) { [self removeLayer:layer]; } @@ -594,13 +594,13 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, } } -- (NS_SET_OF(MGLVectorTileSource *) *)mapboxStreetsSources { +- (NSSet<MGLVectorTileSource *> *)mapboxStreetsSources { return [self.sources objectsPassingTest:^BOOL (__kindof MGLVectorTileSource * _Nonnull source, BOOL * _Nonnull stop) { return [source isKindOfClass:[MGLVectorTileSource class]] && source.mapboxStreets; }]; } -- (NS_ARRAY_OF(MGLStyleLayer *) *)placeStyleLayers { +- (NSArray<MGLStyleLayer *> *)placeStyleLayers { NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"]; NSSet *placeSourceLayerIdentifiers = [NSSet setWithObjects:@"marine_label", @"country_label", @"state_label", @"place_label", @"water_label", @"poi_label", @"rail_station_label", @"mountain_peak_label", nil]; @@ -610,7 +610,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, return [self.layers filteredArrayUsingPredicate:isPlacePredicate]; } -- (NS_ARRAY_OF(MGLStyleLayer *) *)roadStyleLayers { +- (NSArray<MGLStyleLayer *> *)roadStyleLayers { NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"]; NSPredicate *isPlacePredicate = [NSPredicate predicateWithBlock:^BOOL (MGLVectorStyleLayer * _Nullable layer, NSDictionary<NSString *, id> * _Nullable bindings) { diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs index 42940083b5..f3e75b492c 100644 --- a/platform/darwin/src/MGLStyleLayer.mm.ejs +++ b/platform/darwin/src/MGLStyleLayer.mm.ejs @@ -8,7 +8,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" @@ -103,7 +103,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate @@ -121,7 +121,7 @@ namespace mbgl { - (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> { MGLAssertStyleLayerIsValid(); -<% if (property["property-function"]) { -%> +<% if (isDataDriven(property)) { -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>); <% } else { -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>); @@ -162,11 +162,15 @@ namespace mbgl { - (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> { MGLAssertStyleLayerIsValid(); -<% if (property.name === 'heatmap-color') { -%> +<% switch (property['property-type']) { + case 'color-ramp': -%> auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toPropertyValue<mbgl::style::HeatmapColorPropertyValue>(heatmapColor); -<% } else if (property["property-function"]) { -%> +<% break + case 'data-driven': + case 'cross-faded-data-driven': -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::DataDrivenPropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>); -<% } else { -%> +<% break + default: -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue<mbgl::style::PropertyValue<<%- valueTransformerArguments(property)[0] %>>>(<%- objCName(property) %>); <% } -%> self.rawLayer->set<%- camelize(originalPropertyName(property)) %>(mbglValue); diff --git a/platform/darwin/src/MGLStyle_Private.h b/platform/darwin/src/MGLStyle_Private.h index 24466b8018..1294b9ad1c 100644 --- a/platform/darwin/src/MGLStyle_Private.h +++ b/platform/darwin/src/MGLStyle_Private.h @@ -24,16 +24,16 @@ namespace mbgl { @property (nonatomic, readonly, weak) MGLMapView *mapView; @property (nonatomic, readonly) mbgl::style::Style *rawStyle; -- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; -@property (nonatomic, readonly, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers; -- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration; +- (nullable NSArray<MGLAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; +@property (nonatomic, readonly, strong) NSMutableDictionary<NSString *, MGLOpenGLStyleLayer *> *openGLLayers; +- (void)setStyleClasses:(NSArray<NSString *> *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration; @end @interface MGLStyle (MGLStreetsAdditions) -@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *placeStyleLayers; -@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *roadStyleLayers; +@property (nonatomic, readonly, copy) NSArray<MGLVectorStyleLayer *> *placeStyleLayers; +@property (nonatomic, readonly, copy) NSArray<MGLVectorStyleLayer *> *roadStyleLayers; @end diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index e27f039b75..2c899fe76f 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.h +++ b/platform/darwin/src/MGLSymbolStyleLayer.h @@ -564,7 +564,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 2. Set this property to `nil` to reset it to the default value. + `2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -615,7 +615,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -678,7 +678,7 @@ MGL_EXPORT This property is measured in factor of the original icon sizes. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -858,7 +858,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - 45. Set this property to `nil` to reset it to the default value. + `45`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`, and `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise, @@ -890,7 +890,7 @@ MGL_EXPORT This property is measured in ems. The default value of this property is an expression that evaluates to the float - 10. Set this property to `nil` to reset it to the default value. + `10`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -973,7 +973,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 250. Set this property to `nil` to reset it to the default value. + `250`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise, it is ignored. @@ -1129,7 +1129,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 16. Set this property to `nil` to reset it to the default value. + `16`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1219,7 +1219,7 @@ MGL_EXPORT This property is measured in ems. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1241,7 +1241,7 @@ MGL_EXPORT This property is measured in ems. The default value of this property is an expression that evaluates to the float - 1.2. Set this property to `nil` to reset it to the default value. + `1.2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1338,7 +1338,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 2. Set this property to `nil` to reset it to the default value. + `2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1389,7 +1389,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1529,7 +1529,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -1611,7 +1611,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -1638,7 +1638,7 @@ MGL_EXPORT The opacity at which the icon will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -1819,7 +1819,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1900,7 +1900,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1927,7 +1927,7 @@ MGL_EXPORT The opacity at which the text will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm index 0d9fac4808..7ec7816c3b 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.mm +++ b/platform/darwin/src/MGLSymbolStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" @@ -142,7 +142,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h index b0020e4a19..667e1e66ca 100644 --- a/platform/darwin/src/MGLTileSource.h +++ b/platform/darwin/src/MGLTileSource.h @@ -117,6 +117,7 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionAttributionInfos; */ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem; + /** Tile coordinate systems that determine how tile coordinates in tile URLs are interpreted. @@ -141,6 +142,18 @@ typedef NS_ENUM(NSUInteger, MGLTileCoordinateSystem) { MGLTileCoordinateSystemTMS }; + +/** + An `NSNumber` object containing an unsigned integer that specifies the encoding + formula for raster-dem tilesets. The integer corresponds to one of + the constants described in `MGLDEMEncoding`. + + The default value for this option is `MGLDEMEncodingMapbox`. + + This option is not supported by the TileJSON spec. + */ +extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionDEMEncoding; + /** The encoding formula used to generate the raster-dem tileset */ @@ -199,7 +212,7 @@ MGL_EXPORT configuration URL, this array is also empty until the configuration JSON file is loaded. */ -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLAttributionInfo *) *attributionInfos; +@property (nonatomic, copy, readonly) NSArray<MGLAttributionInfo *> *attributionInfos; @end diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm index 87ac5be9c2..2fafc6fb51 100644 --- a/platform/darwin/src/MGLTileSource.mm +++ b/platform/darwin/src/MGLTileSource.mm @@ -30,11 +30,11 @@ const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOption return nil; } -- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos { +- (NSArray<MGLAttributionInfo *> *)attributionInfos { return [self attributionInfosWithFontSize:0 linkColor:nil]; } -- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { +- (NSArray<MGLAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { return [MGLAttributionInfo attributionInfosFromHTMLString:self.attributionHTMLString fontSize:fontSize linkColor:linkColor]; @@ -48,7 +48,7 @@ const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOption @end -mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTemplates, NS_DICTIONARY_OF(MGLTileSourceOption, id) * _Nullable options) { +mbgl::Tileset MGLTileSetFromTileURLTemplates(NSArray<NSString *> *tileURLTemplates, NSDictionary<MGLTileSourceOption, id> * _Nullable options) { mbgl::Tileset tileSet; for (NSString *tileURLTemplate in tileURLTemplates) { diff --git a/platform/darwin/src/MGLTileSource_Private.h b/platform/darwin/src/MGLTileSource_Private.h index 0d9876d412..1b260ca86a 100644 --- a/platform/darwin/src/MGLTileSource_Private.h +++ b/platform/darwin/src/MGLTileSource_Private.h @@ -28,11 +28,11 @@ namespace mbgl { @param fontSize The default text size in points, or 0 to use the default. @param linkColor The default link color, or `nil` to use the default. */ -- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; +- (NSArray<MGLAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; @end MGL_EXPORT -mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTemplates, NS_DICTIONARY_OF(MGLTileSourceOption, id) * _Nullable options); +mbgl::Tileset MGLTileSetFromTileURLTemplates(NSArray<NSString *> *tileURLTemplates, NSDictionary<MGLTileSourceOption, id> * _Nullable options); NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h index 5c32791c2f..fbc3a43ea2 100644 --- a/platform/darwin/src/MGLTypes.h +++ b/platform/darwin/src/MGLTypes.h @@ -111,23 +111,3 @@ NS_INLINE MGLTransition MGLTransitionMake(NSTimeInterval duration, NSTimeInterva } NS_ASSUME_NONNULL_END - -#ifndef NS_ARRAY_OF - // Foundation collection classes adopted lightweight generics in iOS 9.0 and OS X 10.11 SDKs. - #if __has_feature(objc_generics) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100) - /** Inserts a type specifier for a pointer to a lightweight generic with the given collection and object classes. Use a `*` for any non-`id` object classes but no `*` for the collection class. */ - #define NS_ARRAY_OF(ObjectClass...) NSArray <ObjectClass> - #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray <ObjectClass> - #define NS_SET_OF(ObjectClass...) NSSet <ObjectClass> - #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet <ObjectClass> - #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary <ObjectClass> - #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary <ObjectClass> - #else - #define NS_ARRAY_OF(ObjectClass...) NSArray - #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray - #define NS_SET_OF(ObjectClass...) NSSet - #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet - #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary - #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary - #endif -#endif diff --git a/platform/darwin/src/MGLVectorTileSource.h b/platform/darwin/src/MGLVectorTileSource.h index 790c9d4d42..70d2f6e8ec 100644 --- a/platform/darwin/src/MGLVectorTileSource.h +++ b/platform/darwin/src/MGLVectorTileSource.h @@ -99,7 +99,7 @@ MGL_EXPORT the default values. @return An initialized tile source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MGLTileSourceOption, id> *)options NS_DESIGNATED_INITIALIZER; #pragma mark Accessing a Source’s Content @@ -138,7 +138,7 @@ MGL_EXPORT @return An array of objects conforming to the `MGLFeature` protocol that represent features loaded by the source that match the predicate. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)featuresInSourceLayersWithIdentifiers:(NS_SET_OF(NSString *) *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(features(sourceLayerIdentifiers:predicate:)); +- (NSArray<id <MGLFeature>> *)featuresInSourceLayersWithIdentifiers:(NSSet<NSString *> *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(features(sourceLayerIdentifiers:predicate:)); @end diff --git a/platform/darwin/src/MGLVectorTileSource.mm b/platform/darwin/src/MGLVectorTileSource.mm index c1f7267e4a..e55ed13060 100644 --- a/platform/darwin/src/MGLVectorTileSource.mm +++ b/platform/darwin/src/MGLVectorTileSource.mm @@ -6,7 +6,7 @@ #import "MGLStyle_Private.h" #import "MGLMapView_Private.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSURL+MGLAdditions.h" #include <mbgl/map/map.hpp> @@ -27,7 +27,7 @@ return self = [super initWithPendingSource:std::move(source)]; } -- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MGLTileSourceOption, id> *)options { mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options); auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String, tileSet); return self = [super initWithPendingSource:std::move(source)]; @@ -47,7 +47,7 @@ return attribution ? @(attribution->c_str()) : nil; } -- (NS_ARRAY_OF(id <MGLFeature>) *)featuresInSourceLayersWithIdentifiers:(NS_SET_OF(NSString *) *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate { +- (NSArray<id <MGLFeature>> *)featuresInSourceLayersWithIdentifiers:(NSSet<NSString *> *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate { mbgl::optional<std::vector<std::string>> optionalSourceLayerIDs; if (sourceLayerIdentifiers) { @@ -93,9 +93,9 @@ static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[ @"mul", @"ar", @"de", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", ]; -+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages { ++ (NSSet<NSString *> *)mapboxStreetsLanguages { static dispatch_once_t onceToken; - static NS_SET_OF(NSString *) *mapboxStreetsLanguages; + static NSSet<NSString *> *mapboxStreetsLanguages; dispatch_once(&onceToken, ^{ mapboxStreetsLanguages = [NSSet setWithArray:MGLMapboxStreetsLanguages]; }); @@ -112,7 +112,8 @@ static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[ return [[NSLocale localeWithLocaleIdentifier:language].languageCode isEqualToString:@"en"]; }]].count; - NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:MGLMapboxStreetsAlternativeLanguages + NSArray<NSString *> *availableLanguages = acceptsEnglish ? MGLMapboxStreetsLanguages : MGLMapboxStreetsAlternativeLanguages; + NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:availableLanguages forPreferences:preferencesArray]; NSString *mostSpecificLanguage; for (NSString *language in preferredLanguages) { @@ -120,10 +121,7 @@ static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[ mostSpecificLanguage = language; } } - if ([mostSpecificLanguage isEqualToString:@"mul"]) { - return acceptsEnglish ? @"en" : nil; - } - return mostSpecificLanguage; + return [mostSpecificLanguage isEqualToString:@"mul"] ? nil : mostSpecificLanguage; } - (BOOL)isMapboxStreets { @@ -135,19 +133,4 @@ static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[ return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"]; } -- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage { - if (!self.mapboxStreets) { - return @{}; - } - - // Replace {name} and {name_*} with the matching localized name tag. - NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name"; - NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"]; - for (NSString *languageCode in [MGLVectorTileSource mapboxStreetsLanguages]) { - NSString *key = [NSString stringWithFormat:@"name_%@", languageCode]; - localizedKeysByKey[key] = localizedKey; - } - return localizedKeysByKey; -} - @end diff --git a/platform/darwin/src/MGLVectorTileSource_Private.h b/platform/darwin/src/MGLVectorTileSource_Private.h index 77521869f1..8d287ae4c4 100644 --- a/platform/darwin/src/MGLVectorTileSource_Private.h +++ b/platform/darwin/src/MGLVectorTileSource_Private.h @@ -6,13 +6,11 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets; -+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages; ++ (NSSet<NSString *> *)mapboxStreetsLanguages; + (nullable NSString *)preferredMapboxStreetsLanguage; + (nullable NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray<NSString *> *)preferencesArray; -- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage; - @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/NSBundle+MGLAdditions.h b/platform/darwin/src/NSBundle+MGLAdditions.h index ad5e9d5369..86dc27f22c 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.h +++ b/platform/darwin/src/NSBundle+MGLAdditions.h @@ -34,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN + (nullable NSString *)mgl_frameworkBundleIdentifier; -+ (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary; ++ (nullable NSDictionary<NSString *, id> *)mgl_frameworkInfoDictionary; + (nullable NSString *)mgl_applicationBundleIdentifier; diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m index f55059324e..37f78963d3 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.m +++ b/platform/darwin/src/NSBundle+MGLAdditions.m @@ -26,7 +26,7 @@ return self.mgl_frameworkInfoDictionary[@"CFBundleIdentifier"]; } -+ (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary { ++ (nullable NSDictionary<NSString *, id> *)mgl_frameworkInfoDictionary { NSBundle *bundle = self.mgl_frameworkBundle; return bundle.infoDictionary; } diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm index 380215ff32..15aa71419d 100644 --- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm @@ -115,15 +115,14 @@ return @[op, leftHandPredicate.mgl_jsonExpressionObject, rightHandPredicate.mgl_jsonExpressionObject]; } case NSInPredicateOperatorType: { - NSMutableArray *elements = [NSMutableArray arrayWithObjects:@"match", self.leftExpression.mgl_jsonExpressionObject, nil]; - NSArray *optionsExpressions = self.rightExpression.constantValue; - for (id object in optionsExpressions) { - id option = ((NSExpression *)object).mgl_jsonExpressionObject; - [elements addObject:option]; - [elements addObject:@YES]; - } - [elements addObject:@NO]; - return elements; + + NSExpression *matchExpression = [NSExpression expressionForFunction:@"MGL_MATCH" + arguments:@[self.leftExpression, + self.rightExpression, + [NSExpression expressionForConstantValue:@YES], + [NSExpression expressionForConstantValue:@NO]]]; + + return matchExpression.mgl_jsonExpressionObject; } case NSContainsPredicateOperatorType: { NSPredicate *inPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression diff --git a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm index 5a98b763ea..a8ae19b172 100644 --- a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm @@ -2,7 +2,7 @@ #import "MGLStyleValue_Private.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSExpression+MGLPrivateAdditions.h" #include <mbgl/style/conversion/property_value.hpp> diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index 8db839c5da..af4a197662 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -403,7 +403,7 @@ static NSArray * const MGLTokenizedFunctions = @[ If no replacements take place, this method returns the original collection. */ -NS_ARRAY_OF(NSExpression *) *MGLCollectionByReplacingTokensWithKeyPaths(NS_ARRAY_OF(NSExpression *) *collection) { +NSArray<NSExpression *> *MGLCollectionByReplacingTokensWithKeyPaths(NSArray<NSExpression *> *collection) { __block NSMutableArray *upgradedCollection; [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) { NSExpression *upgradedItem = item.mgl_expressionByReplacingTokensWithKeyPaths; @@ -424,7 +424,7 @@ NS_ARRAY_OF(NSExpression *) *MGLCollectionByReplacingTokensWithKeyPaths(NS_ARRAY If no replacements take place, this method returns the original stop dictionary. */ -NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLStopDictionaryByReplacingTokensWithKeyPaths(NS_DICTIONARY_OF(NSNumber *, NSExpression *) *stops) { +NSDictionary<NSNumber *, NSExpression *> *MGLStopDictionaryByReplacingTokensWithKeyPaths(NSDictionary<NSNumber *, NSExpression *> *stops) { __block NSMutableDictionary *upgradedStops; [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) { if (![value isKindOfClass:[NSExpression class]]) { @@ -665,11 +665,7 @@ NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLStopDictionaryByReplacingTokens } + (instancetype)mgl_expressionForConditional:(nonnull NSPredicate *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression { - if (@available(iOS 9.0, *)) { - return [NSExpression expressionForConditional:conditionPredicate trueExpression:trueExpression falseExpression:falseExpression]; - } else { - return [NSExpression expressionForFunction:@"MGL_IF" arguments:@[[NSExpression expressionWithFormat:@"%@", conditionPredicate], trueExpression, falseExpression]]; - } + return [NSExpression expressionForConditional:conditionPredicate trueExpression:trueExpression falseExpression:falseExpression]; } + (instancetype)mgl_expressionForSteppingExpression:(nonnull NSExpression *)steppingExpression fromExpression:(nonnull NSExpression *)minimumExpression stops:(nonnull NSExpression *)stops { @@ -767,6 +763,11 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { NSArray *array = (NSArray *)object; NSString *op = array.firstObject; + if (![op isKindOfClass:[NSString class]]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(array); + return [NSExpression expressionForFunction:@"MGL_FUNCTION" arguments:subexpressions]; + } + NSArray *argumentObjects = [array subarrayWithRange:NSMakeRange(1, array.count - 1)]; NSString *functionName = MGLFunctionNamesByExpressionOperator[op]; @@ -802,6 +803,22 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { } else if ([op isEqualToString:@"to-string"] || [op isEqualToString:@"string"]) { NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject]; return [NSExpression expressionWithFormat:@"CAST(%@, 'NSString')", operand]; + } else if ([op isEqualToString:@"to-color"]) { + NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject]; + + if (argumentObjects.count == 1) { +#if TARGET_OS_IPHONE + return [NSExpression expressionWithFormat:@"CAST(%@, 'UIColor')", operand]; +#else + return [NSExpression expressionWithFormat:@"CAST(%@, 'NSColor')", operand]; +#endif + } + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(array); + return [NSExpression expressionForFunction:@"MGL_FUNCTION" arguments:subexpressions]; + + } else if ([op isEqualToString:@"to-rgba"]) { + NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject]; + return [NSExpression expressionWithFormat:@"CAST(noindex(%@), 'NSArray')", operand]; } else if ([op isEqualToString:@"get"]) { if (argumentObjects.count == 2) { NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.lastObject]; @@ -913,26 +930,28 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { for (NSUInteger index = 0; index < argumentObjects.count; index++) { if (index % 2 == 0 && index != argumentObjects.count - 1) { - NSPredicate *predicate = [NSPredicate mgl_predicateWithJSONObject:argumentObjects[index]]; + NSPredicate *predicate = [NSPredicate predicateWithMGLJSONObject:argumentObjects[index]]; NSExpression *argument = [NSExpression expressionForConstantValue:predicate]; [arguments addObject:argument]; } else { [arguments addObject:[NSExpression expressionWithMGLJSONObject:argumentObjects[index]]]; } } - - if (@available(iOS 9.0, *)) { - if (arguments.count == 3) { - NSPredicate *conditional = [arguments.firstObject constantValue]; - return [NSExpression expressionForConditional:conditional trueExpression:arguments[1] falseExpression:arguments[2]]; - } + + if (arguments.count == 3) { + NSPredicate *conditional = [arguments.firstObject constantValue]; + return [NSExpression expressionForConditional:conditional trueExpression:arguments[1] falseExpression:arguments[2]]; } return [NSExpression expressionForFunction:@"MGL_IF" arguments:arguments]; } else if ([op isEqualToString:@"match"]) { NSMutableArray *optionsArray = [NSMutableArray array]; - NSEnumerator *optionsEnumerator = argumentObjects.objectEnumerator; - while (id object = optionsEnumerator.nextObject) { - NSExpression *option = [NSExpression expressionWithMGLJSONObject:object]; + + for (NSUInteger index = 0; index < argumentObjects.count; index++) { + NSExpression *option = [NSExpression expressionWithMGLJSONObject:argumentObjects[index]]; + // match operators with arrays as matching values should not parse arrays as generic functions. + if (index > 0 && index < argumentObjects.count - 1 && !(index % 2 == 0) && [argumentObjects[index] isKindOfClass:[NSArray class]]) { + option = [NSExpression expressionForAggregate:MGLSubexpressionsWithJSONObjects(argumentObjects[index])]; + } [optionsArray addObject:option]; } @@ -1097,7 +1116,22 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { NSArray *arguments = self.arguments.mgl_jsonExpressionObject; return [@[@"concat", self.operand.mgl_jsonExpressionObject] arrayByAddingObjectsFromArray:arguments]; } else if ([function isEqualToString:@"objectFrom:withIndex:"]) { - return @[@"at", self.arguments[1].mgl_jsonExpressionObject, self.arguments[0].mgl_jsonExpressionObject]; + id index = self.arguments[1].mgl_jsonExpressionObject; + + if ([self.arguments[1] expressionType] == NSConstantValueExpressionType + && [[self.arguments[1] constantValue] isKindOfClass:[NSString class]]) { + id value = self.arguments[1].constantValue; + + if ([value isEqualToString:@"FIRST"]) { + index = [NSExpression expressionForConstantValue:@0].mgl_jsonExpressionObject; + } else if ([value isEqualToString:@"LAST"]) { + index = [NSExpression expressionWithFormat:@"count(%@) - 1", self.arguments[0]].mgl_jsonExpressionObject; + } else if ([value isEqualToString:@"SIZE"]) { + return [NSExpression expressionWithFormat:@"count(%@)", self.arguments[0]].mgl_jsonExpressionObject; + } + } + + return @[@"at", index, self.arguments[0].mgl_jsonExpressionObject]; } else if ([function isEqualToString:@"boolValue"]) { return @[@"to-boolean", self.operand.mgl_jsonExpressionObject]; } else if ([function isEqualToString:@"mgl_number"] || @@ -1150,6 +1184,26 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { } else if ([type isEqualToString:@"NSNumber"]) { return @[@"to-number", object]; } +#if TARGET_OS_IPHONE + else if ([type isEqualToString:@"UIColor"] || [type isEqualToString:@"MGLColor"]) { + return @[@"to-color", object]; + } +#else + else if ([type isEqualToString:@"NSColor"] || [type isEqualToString:@"MGLColor"]) { + return @[@"to-color", object]; + } +#endif + else if ([type isEqualToString:@"NSArray"]) { + NSExpression *operand = self.arguments.firstObject; + if ([operand expressionType] == NSFunctionExpressionType ) { + operand = self.arguments.firstObject.arguments.firstObject; + } + if (([operand expressionType] != NSConstantValueExpressionType) || + ([operand expressionType] == NSConstantValueExpressionType && + [[operand constantValue] isKindOfClass:[MGLColor class]])) { + return @[@"to-rgba", object]; + } + } [NSException raise:NSInvalidArgumentException format:@"Casting expression to %@ not yet implemented.", type]; } else if ([function isEqualToString:@"MGL_FUNCTION"]) { @@ -1218,7 +1272,7 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { case NSAnyKeyExpressionType: case NSBlockExpressionType: [NSException raise:NSInvalidArgumentException - format:@"Expression type %lu not yet implemented.", self.expressionType]; + format:@"Expression type %lu not yet implemented.", (unsigned long)self.expressionType]; } // NSKeyPathSpecifierExpression @@ -1240,11 +1294,11 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { if (self.arguments.count < expectedArgumentCount) { [NSException raise:NSInvalidArgumentException format: @"Too few arguments to ‘%@’ function; expected %lu arguments.", - self.function, expectedArgumentCount]; + self.function, (unsigned long)expectedArgumentCount]; } else if (self.arguments.count > expectedArgumentCount) { [NSException raise:NSInvalidArgumentException format: @"%lu unexpected arguments to ‘%@’ function; expected %lu arguments.", - self.arguments.count - expectedArgumentCount, self.function, expectedArgumentCount]; + self.arguments.count - (unsigned long)expectedArgumentCount, self.function, (unsigned long)expectedArgumentCount]; } BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_interpolate:withCurveType:parameters:stops:"]; @@ -1301,7 +1355,15 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { NSArray<NSExpression *> *arguments = isAftermarketFunction ? self.arguments : self.arguments[minimumIndex].constantValue; for (NSUInteger index = minimumIndex; index < arguments.count; index++) { - [expressionObject addObject:arguments[index].mgl_jsonExpressionObject]; + NSArray *argumentObject = arguments[index].mgl_jsonExpressionObject; + // match operators with arrays as matching values should not parse arrays using the literal operator. + if (index > 0 && index < arguments.count - 1 && !(index % 2 == 0) + && (arguments[index].expressionType == NSAggregateExpressionType || + (arguments[index].expressionType == NSConstantValueExpressionType && [arguments[index].constantValue isKindOfClass:[NSArray class]]))) { + + argumentObject = argumentObject.count == 2 ? argumentObject[1] : argumentObject; + } + [expressionObject addObject:argumentObject]; } return expressionObject; @@ -1371,7 +1433,7 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { If no localization takes place, this method returns the original collection. */ -NS_ARRAY_OF(NSExpression *) *MGLLocalizedCollection(NS_ARRAY_OF(NSExpression *) *collection, NSLocale * _Nullable locale) { +NSArray<NSExpression *> *MGLLocalizedCollection(NSArray<NSExpression *> *collection, NSLocale * _Nullable locale) { __block NSMutableArray *localizedCollection; [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) { NSExpression *localizedItem = [item mgl_expressionLocalizedIntoLocale:locale]; @@ -1391,7 +1453,7 @@ NS_ARRAY_OF(NSExpression *) *MGLLocalizedCollection(NS_ARRAY_OF(NSExpression *) If no localization takes place, this method returns the original stop dictionary. */ -NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLLocalizedStopDictionary(NS_DICTIONARY_OF(NSNumber *, NSExpression *) *stops, NSLocale * _Nullable locale) { +NSDictionary<NSNumber *, NSExpression *> *MGLLocalizedStopDictionary(NSDictionary<NSNumber *, NSExpression *> *stops, NSLocale * _Nullable locale) { __block NSMutableDictionary *localizedStops; [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) { if (![value isKindOfClass:[NSExpression class]]) { @@ -1456,16 +1518,14 @@ NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLLocalizedStopDictionary(NS_DICT } case NSConditionalExpressionType: { - if (@available(iOS 9.0, *)) { - NSExpression *trueExpression = self.trueExpression; - NSExpression *localizedTrueExpression = [trueExpression mgl_expressionLocalizedIntoLocale:locale]; - NSExpression *falseExpression = self.falseExpression; - NSExpression *localizedFalseExpression = [falseExpression mgl_expressionLocalizedIntoLocale:locale]; - if (localizedTrueExpression != trueExpression || localizedFalseExpression != falseExpression) { - return [NSExpression expressionForConditional:self.predicate - trueExpression:localizedTrueExpression - falseExpression:localizedFalseExpression]; - } + NSExpression *trueExpression = self.trueExpression; + NSExpression *localizedTrueExpression = [trueExpression mgl_expressionLocalizedIntoLocale:locale]; + NSExpression *falseExpression = self.falseExpression; + NSExpression *localizedFalseExpression = [falseExpression mgl_expressionLocalizedIntoLocale:locale]; + if (localizedTrueExpression != trueExpression || localizedFalseExpression != falseExpression) { + return [NSExpression expressionForConditional:self.predicate + trueExpression:localizedTrueExpression + falseExpression:localizedFalseExpression]; } return self; } diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.h b/platform/darwin/src/NSPredicate+MGLAdditions.h index a73b1a61ba..6c4b878d37 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.h +++ b/platform/darwin/src/NSPredicate+MGLAdditions.h @@ -1,23 +1,44 @@ #import <Foundation/Foundation.h> -#import "NSExpression+MGLPrivateAdditions.h" +NS_ASSUME_NONNULL_BEGIN @interface NSPredicate (MGLAdditions) -- (mbgl::style::Filter)mgl_filter; - -+ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter; - -@end - -@interface NSPredicate (MGLExpressionAdditions) - -+ (instancetype)mgl_predicateWithJSONObject:(id)object; - +#pragma mark Converting JSON Expressions + +/** + Returns a predicate equivalent to the given Foundation object deserialized + from JSON data. + + The Foundation object is interpreted according to the + [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions). + See the + “[Predicates and Expressions](../predicates-and-expressions.html)” + guide for a correspondence of operators and types between the style + specification and the `NSPredicate` representation used by this SDK. + + @param object A Foundation object deserialized from JSON data, for example + using `NSJSONSerialization`. + @return An initialized predicate equivalent to `object`, suitable for use + with the `MGLVectorStyleLayer.predicate` property. + */ ++ (instancetype)predicateWithMGLJSONObject:(id)object NS_SWIFT_NAME(init(mglJSONObject:)); + +/** + An equivalent Foundation object that can be serialized as JSON. + + The Foundation object conforms to the + [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions). + See the + “[Predicates and Expressions](../predicates-and-expressions.html)” + guide for a correspondence of operators and types between the style + specification and the `NSPredicate` representation used by this SDK. + + You can use `NSJSONSerialization` to serialize the Foundation object as data to + write to a file. + */ @property (nonatomic, readonly) id mgl_jsonExpressionObject; -- (id)mgl_if:(id)firstValue, ...; - -- (id)mgl_match:(NSExpression *)firstCase, ...; - @end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index bbd324bb63..4b9a4177cb 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -1,210 +1,11 @@ -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "MGLValueEvaluator.h" #import "MGLStyleValue_Private.h" #include <mbgl/style/conversion/filter.hpp> -class FilterEvaluator { -public: - - NSArray *getPredicates(std::vector<mbgl::style::Filter> filters) { - NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:filters.size()]; - for (auto filter : filters) { - [predicates addObject:mbgl::style::Filter::visit(filter, FilterEvaluator())]; - } - return predicates; - } - - template <typename MBGLType> - NSExpression *getValues(std::vector<MBGLType> values) { - NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()]; - for (auto value : values) { - id constantValue = MBGLType::visit(value, ValueEvaluator()); - [array addObject:[NSExpression expressionForConstantValue:constantValue]]; - } - return [NSExpression expressionForAggregate:array]; - } - - NSString *getFeatureTypeString(mbgl::FeatureType type) { - switch (type) { - case mbgl::FeatureType::Point: - return @"Point"; - - case mbgl::FeatureType::LineString: - return @"LineString"; - - case mbgl::FeatureType::Polygon: - return @"Polygon"; - - default: - NSCAssert(NO, @"Unrecognized feature type %hhu", type); - return nil; - } - } - - NSExpression *getFeatureTypeStrings(std::vector<mbgl::FeatureType> values) { - NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()]; - for (auto value : values) { - id typeString = getFeatureTypeString(value); - [array addObject:[NSExpression expressionForConstantValue:typeString]]; - } - return [NSExpression expressionForAggregate:array]; - } - - NSPredicate *operator()(mbgl::style::NullFilter filter) { - return nil; - } - - NSPredicate *operator()(mbgl::style::EqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::NotEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::GreaterThanFilter filter) { - return [NSPredicate predicateWithFormat:@"%K > %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::GreaterThanEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K >= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::LessThanFilter filter) { - return [NSPredicate predicateWithFormat:@"%K < %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::LessThanEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K <= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::InFilter filter) { - return [NSPredicate predicateWithFormat:@"%K IN %@", @(filter.key.c_str()), getValues(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::NotInFilter filter) { - return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @(filter.key.c_str()), getValues(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::TypeEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == %@", @"$type", getFeatureTypeString(filter.value)]; - } - - NSPredicate *operator()(mbgl::style::TypeNotEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != %@", @"$type", getFeatureTypeString(filter.value)]; - } - - NSPredicate *operator()(mbgl::style::TypeInFilter filter) { - return [NSPredicate predicateWithFormat:@"%K IN %@", @"$type", getFeatureTypeStrings(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::TypeNotInFilter filter) { - return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @"$type", getFeatureTypeStrings(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::IdentifierEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == %@", @"$id", mbgl::FeatureIdentifier::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::IdentifierNotEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != %@", @"$id", mbgl::FeatureIdentifier::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::IdentifierInFilter filter) { - return [NSPredicate predicateWithFormat:@"%K IN %@", @"$id", getValues(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::IdentifierNotInFilter filter) { - return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @"$id", getValues(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::AnyFilter filter) { - NSArray *subpredicates = getPredicates(filter.filters); - if (subpredicates.count) { - return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; - } - return [NSPredicate predicateWithValue:NO]; - } - - NSPredicate *operator()(mbgl::style::AllFilter filter) { - // Convert [all, [>=, key, lower], [<=, key, upper]] to key BETWEEN {lower, upper} - if (filter.filters.size() == 2) { - auto leftFilter = filter.filters[0]; - auto rightFilter = filter.filters[1]; - - std::string lowerKey; - std::string upperKey; - mbgl::Value lowerBound; - mbgl::Value upperBound; - if (leftFilter.is<mbgl::style::GreaterThanEqualsFilter>()) { - lowerKey = leftFilter.get<mbgl::style::GreaterThanEqualsFilter>().key; - lowerBound = leftFilter.get<mbgl::style::GreaterThanEqualsFilter>().value; - } else if (rightFilter.is<mbgl::style::GreaterThanEqualsFilter>()) { - lowerKey = rightFilter.get<mbgl::style::GreaterThanEqualsFilter>().key; - lowerBound = rightFilter.get<mbgl::style::GreaterThanEqualsFilter>().value; - } - - if (leftFilter.is<mbgl::style::LessThanEqualsFilter>()) { - upperKey = leftFilter.get<mbgl::style::LessThanEqualsFilter>().key; - upperBound = leftFilter.get<mbgl::style::LessThanEqualsFilter>().value; - } else if (rightFilter.is<mbgl::style::LessThanEqualsFilter>()) { - upperKey = rightFilter.get<mbgl::style::LessThanEqualsFilter>().key; - upperBound = rightFilter.get<mbgl::style::LessThanEqualsFilter>().value; - } - - if (!lowerBound.is<mbgl::NullValue>() && !upperBound.is<mbgl::NullValue>() - && lowerKey == upperKey) { - return [NSPredicate predicateWithFormat:@"%K BETWEEN {%@, %@}", - @(lowerKey.c_str()), - mbgl::Value::visit(lowerBound, ValueEvaluator()), - mbgl::Value::visit(upperBound, ValueEvaluator())]; - } - } - - NSArray *subpredicates = getPredicates(filter.filters); - if (subpredicates.count) { - return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; - } - return [NSPredicate predicateWithValue:YES]; - } - - NSPredicate *operator()(mbgl::style::NoneFilter filter) { - NSArray *subpredicates = getPredicates(filter.filters); - if (subpredicates.count > 1) { - NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; - return [NSCompoundPredicate notPredicateWithSubpredicate:predicate]; - } else if (subpredicates.count) { - return [NSCompoundPredicate notPredicateWithSubpredicate:subpredicates.firstObject]; - } else { - return [NSPredicate predicateWithValue:YES]; - } - } - - NSPredicate *operator()(mbgl::style::HasFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != nil", @(filter.key.c_str())]; - } - - NSPredicate *operator()(mbgl::style::NotHasFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == nil", @(filter.key.c_str())]; - } - - NSPredicate *operator()(mbgl::style::HasIdentifierFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != nil", @"$id"]; - } - - NSPredicate *operator()(mbgl::style::NotHasIdentifierFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == nil", @"$id"]; - } - - NSPredicate *operator()(mbgl::style::ExpressionFilter filter) { - id jsonObject = MGLJSONObjectFromMBGLExpression(*filter.expression); - return [NSPredicate mgl_predicateWithJSONObject:jsonObject]; - } -}; - -@implementation NSPredicate (MGLAdditions) +@implementation NSPredicate (MGLPrivateAdditions) - (mbgl::style::Filter)mgl_filter { @@ -224,24 +25,28 @@ public: + (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter { - FilterEvaluator evaluator; - return mbgl::style::Filter::visit(filter, evaluator); + if (filter.expression) { + id jsonObject = MGLJSONObjectFromMBGLExpression(**filter.expression); + return [NSPredicate predicateWithMGLJSONObject:jsonObject]; + } else { + return nil; + } } @end -@implementation NSPredicate (MGLExpressionAdditions) +@implementation NSPredicate (MGLAdditions) NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { NSMutableArray *subpredicates = [NSMutableArray arrayWithCapacity:objects.count]; for (id object in objects) { - NSPredicate *predicate = [NSPredicate mgl_predicateWithJSONObject:object]; + NSPredicate *predicate = [NSPredicate predicateWithMGLJSONObject:object]; [subpredicates addObject:predicate]; } return subpredicates; } -+ (instancetype)mgl_predicateWithJSONObject:(id)object { ++ (instancetype)predicateWithMGLJSONObject:(id)object { if ([object isEqual:@YES]) { return [NSPredicate predicateWithValue:YES]; } @@ -360,6 +165,10 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { return nil; } +@end + +@implementation NSPredicate (MGLExpressionAdditions) + - (id)mgl_if:(id)firstValue, ... { if ([self evaluateWithObject:nil]) { diff --git a/platform/darwin/src/NSPredicate+MGLPrivateAdditions.h b/platform/darwin/src/NSPredicate+MGLPrivateAdditions.h new file mode 100644 index 0000000000..1828009678 --- /dev/null +++ b/platform/darwin/src/NSPredicate+MGLPrivateAdditions.h @@ -0,0 +1,25 @@ +#import <Foundation/Foundation.h> + +#import "NSPredicate+MGLAdditions.h" + +#include <mbgl/style/filter.hpp> + +NS_ASSUME_NONNULL_BEGIN + +@interface NSPredicate (MGLPrivateAdditions) + +- (mbgl::style::Filter)mgl_filter; + ++ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter; + +@end + +@interface NSPredicate (MGLExpressionAdditions) + +- (id)mgl_if:(id)firstValue, ...; + +- (id)mgl_match:(NSExpression *)firstCase, ...; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/NSString+MGLAdditions.h b/platform/darwin/src/NSString+MGLAdditions.h index 75c593c10b..4888c7a00f 100644 --- a/platform/darwin/src/NSString+MGLAdditions.h +++ b/platform/darwin/src/NSString+MGLAdditions.h @@ -26,9 +26,6 @@ NS_ASSUME_NONNULL_BEGIN Only supports scripts for languages used by Mapbox Streets. - On iOS 8 or older, this will method will always return the untransliterated - receiver. - @param script The four-letter code representing the name of the script, as specified by ISO 15924. */ diff --git a/platform/darwin/src/NSString+MGLAdditions.m b/platform/darwin/src/NSString+MGLAdditions.m index a61f229511..d645490eb3 100644 --- a/platform/darwin/src/NSString+MGLAdditions.m +++ b/platform/darwin/src/NSString+MGLAdditions.m @@ -17,14 +17,9 @@ - (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale { NSMutableString *string = self.mutableCopy; NSOrthography *orthography; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability-new" - if ([NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]) { + if (@available(iOS 11.0, macOS 10.13.0, *)) { orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier]; } -#pragma clang diagnostic pop -#endif [string enumerateLinguisticTagsInRange:string.mgl_wholeRange scheme:NSLinguisticTagSchemeLexicalClass options:0 orthography:orthography usingBlock:^(NSString * _Nonnull tag, NSRange tokenRange, NSRange sentenceRange, BOOL * _Nonnull stop) { NSString *word = [string substringWithRange:tokenRange]; if (word.length > 3 @@ -46,22 +41,18 @@ } - (NSString *)mgl_stringByTransliteratingIntoScript:(NSString *)script { - if (@available(iOS 9.0, *)) { - NSMutableString *string = self.mutableCopy; - NSStringTransform transform; - if ([script isEqualToString:@"Latn"]) { - transform = NSStringTransformToLatin; - } else if ([script isEqualToString:@"Hans"]) { - // No transform available. - } else if ([script isEqualToString:@"Cyrl"]) { - transform = @"Any-Latin; Latin-Cyrillic"; - } else if ([script isEqualToString:@"Arab"]) { - transform = @"Any-Latin; Latin-Arabic"; - } - return transform ? [string stringByApplyingTransform:transform reverse:NO] : string; - } else { - return self; + NSMutableString *string = self.mutableCopy; + NSStringTransform transform; + if ([script isEqualToString:@"Latn"]) { + transform = NSStringTransformToLatin; + } else if ([script isEqualToString:@"Hans"]) { + // No transform available. + } else if ([script isEqualToString:@"Cyrl"]) { + transform = @"Any-Latin; Latin-Cyrillic"; + } else if ([script isEqualToString:@"Arab"]) { + transform = @"Any-Latin; Latin-Arabic"; } + return transform ? [string stringByApplyingTransform:transform reverse:NO] : string; } @end diff --git a/platform/darwin/test/MGLAttributionInfoTests.m b/platform/darwin/test/MGLAttributionInfoTests.m index eccc6ceece..5961b61133 100644 --- a/platform/darwin/test/MGLAttributionInfoTests.m +++ b/platform/darwin/test/MGLAttributionInfoTests.m @@ -17,7 +17,7 @@ @"<a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a>", }; - NS_MUTABLE_ARRAY_OF(MGLAttributionInfo *) *infos = [NSMutableArray array]; + NSMutableArray<MGLAttributionInfo *> *infos = [NSMutableArray array]; for (NSUInteger i = 0; i < sizeof(htmlStrings) / sizeof(htmlStrings[0]); i++) { NSArray *subinfos = [MGLAttributionInfo attributionInfosFromHTMLString:htmlStrings[i] fontSize:0 @@ -67,7 +67,7 @@ CGFloat fontSize = 72; MGLColor *color = [MGLColor redColor]; - NS_MUTABLE_ARRAY_OF(MGLAttributionInfo *) *infos = [NSMutableArray array]; + NSMutableArray<MGLAttributionInfo *> *infos = [NSMutableArray array]; for (NSUInteger i = 0; i < sizeof(htmlStrings) / sizeof(htmlStrings[0]); i++) { NSArray *subinfos = [MGLAttributionInfo attributionInfosFromHTMLString:htmlStrings[i] fontSize:72 @@ -109,7 +109,7 @@ @"Hello World", }; - NS_MUTABLE_ARRAY_OF(MGLAttributionInfo *) *infos = [NSMutableArray array]; + NSMutableArray<MGLAttributionInfo *> *infos = [NSMutableArray array]; for (NSUInteger i = 0; i < sizeof(htmlStrings) / sizeof(htmlStrings[0]); i++) { NSArray *subinfos = [MGLAttributionInfo attributionInfosFromHTMLString:htmlStrings[i] fontSize:0 diff --git a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm index de8080f425..bf18b2d30e 100644 --- a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm +++ b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm @@ -50,13 +50,12 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getBackgroundColor(), propertyValue, @"Setting backgroundColor to a camera expression should update background-color."); XCTAssertEqualObjects(layer.backgroundColor, functionExpression, @"backgroundColor should round-trip camera expressions."); - layer.backgroundColor = nil; XCTAssertTrue(rawLayer->getBackgroundColor().isUndefined(), @@ -103,13 +102,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getBackgroundOpacity(), propertyValue, @"Setting backgroundOpacity to a camera expression should update background-opacity."); XCTAssertEqualObjects(layer.backgroundOpacity, functionExpression, @"backgroundOpacity should round-trip camera expressions."); - layer.backgroundOpacity = nil; XCTAssertTrue(rawLayer->getBackgroundOpacity().isUndefined(), @@ -156,13 +154,12 @@ { 18, "Background Pattern" }, }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; - + XCTAssertEqual(rawLayer->getBackgroundPattern(), propertyValue, @"Setting backgroundPattern to a camera expression should update background-pattern."); XCTAssertEqualObjects(layer.backgroundPattern, functionExpression, @"backgroundPattern should round-trip camera expressions."); - layer.backgroundPattern = nil; XCTAssertTrue(rawLayer->getBackgroundPattern().isUndefined(), diff --git a/platform/darwin/test/MGLCircleStyleLayerTests.mm b/platform/darwin/test/MGLCircleStyleLayerTests.mm index d7bf2a5afd..41f7238da2 100644 --- a/platform/darwin/test/MGLCircleStyleLayerTests.mm +++ b/platform/darwin/test/MGLCircleStyleLayerTests.mm @@ -71,7 +71,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue, @"Setting circleBlur to a camera expression should update circle-blur."); XCTAssertEqualObjects(layer.circleBlur, functionExpression, @@ -102,7 +102,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleBlur, pedanticFunctionExpression, @"circleBlur should round-trip camera-data expressions."); - layer.circleBlur = nil; XCTAssertTrue(rawLayer->getCircleBlur().isUndefined(), @@ -143,7 +142,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleColor(), propertyValue, @"Setting circleColor to a camera expression should update circle-color."); XCTAssertEqualObjects(layer.circleColor, functionExpression, @@ -174,7 +173,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleColor, pedanticFunctionExpression, @"circleColor should round-trip camera-data expressions."); - layer.circleColor = nil; XCTAssertTrue(rawLayer->getCircleColor().isUndefined(), @@ -215,7 +213,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue, @"Setting circleOpacity to a camera expression should update circle-opacity."); XCTAssertEqualObjects(layer.circleOpacity, functionExpression, @@ -246,7 +244,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleOpacity, pedanticFunctionExpression, @"circleOpacity should round-trip camera-data expressions."); - layer.circleOpacity = nil; XCTAssertTrue(rawLayer->getCircleOpacity().isUndefined(), @@ -287,13 +284,12 @@ { 18, mbgl::style::AlignmentType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; - + XCTAssertEqual(rawLayer->getCirclePitchAlignment(), propertyValue, @"Setting circlePitchAlignment to a camera expression should update circle-pitch-alignment."); XCTAssertEqualObjects(layer.circlePitchAlignment, functionExpression, @"circlePitchAlignment should round-trip camera expressions."); - layer.circlePitchAlignment = nil; XCTAssertTrue(rawLayer->getCirclePitchAlignment().isUndefined(), @@ -331,7 +327,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue, @"Setting circleRadius to a camera expression should update circle-radius."); XCTAssertEqualObjects(layer.circleRadius, functionExpression, @@ -362,7 +358,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleRadius, pedanticFunctionExpression, @"circleRadius should round-trip camera-data expressions."); - layer.circleRadius = nil; XCTAssertTrue(rawLayer->getCircleRadius().isUndefined(), @@ -403,13 +398,12 @@ { 18, mbgl::style::CirclePitchScaleType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::CirclePitchScaleType> { intervalStops }; - + XCTAssertEqual(rawLayer->getCirclePitchScale(), propertyValue, @"Setting circleScaleAlignment to a camera expression should update circle-pitch-scale."); XCTAssertEqualObjects(layer.circleScaleAlignment, functionExpression, @"circleScaleAlignment should round-trip camera expressions."); - layer.circleScaleAlignment = nil; XCTAssertTrue(rawLayer->getCirclePitchScale().isUndefined(), @@ -447,7 +441,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue, @"Setting circleStrokeColor to a camera expression should update circle-stroke-color."); XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression, @@ -478,7 +472,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleStrokeColor, pedanticFunctionExpression, @"circleStrokeColor should round-trip camera-data expressions."); - layer.circleStrokeColor = nil; XCTAssertTrue(rawLayer->getCircleStrokeColor().isUndefined(), @@ -519,7 +512,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue, @"Setting circleStrokeOpacity to a camera expression should update circle-stroke-opacity."); XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression, @@ -550,7 +543,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleStrokeOpacity, pedanticFunctionExpression, @"circleStrokeOpacity should round-trip camera-data expressions."); - layer.circleStrokeOpacity = nil; XCTAssertTrue(rawLayer->getCircleStrokeOpacity().isUndefined(), @@ -591,7 +583,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue, @"Setting circleStrokeWidth to a camera expression should update circle-stroke-width."); XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression, @@ -622,7 +614,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleStrokeWidth, pedanticFunctionExpression, @"circleStrokeWidth should round-trip camera-data expressions."); - layer.circleStrokeWidth = nil; XCTAssertTrue(rawLayer->getCircleStrokeWidth().isUndefined(), @@ -669,13 +660,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleTranslate(), propertyValue, @"Setting circleTranslation to a camera expression should update circle-translate."); XCTAssertEqualObjects(layer.circleTranslation, functionExpression, @"circleTranslation should round-trip camera expressions."); - layer.circleTranslation = nil; XCTAssertTrue(rawLayer->getCircleTranslate().isUndefined(), @@ -713,13 +703,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleTranslateAnchor(), propertyValue, @"Setting circleTranslationAnchor to a camera expression should update circle-translate-anchor."); XCTAssertEqualObjects(layer.circleTranslationAnchor, functionExpression, @"circleTranslationAnchor should round-trip camera expressions."); - layer.circleTranslationAnchor = nil; XCTAssertTrue(rawLayer->getCircleTranslateAnchor().isUndefined(), diff --git a/platform/darwin/test/MGLCoordinateFormatterTests.m b/platform/darwin/test/MGLCoordinateFormatterTests.m index d693f739ec..ac083fa103 100644 --- a/platform/darwin/test/MGLCoordinateFormatterTests.m +++ b/platform/darwin/test/MGLCoordinateFormatterTests.m @@ -24,12 +24,7 @@ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°54′48″N, 77°1′57″W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°54′48″ north, 77°1′57″ west"); - if (@available(iOS 9.0, *)) { - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west"); - } else { - // Foundation in iOS 8 does not know how to pluralize coordinates. - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degree(s), 54 minute(s), and 48 second(s) north by 77 degree(s), 1 minute(s), and 57 second(s) west"); - } + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west"); shortFormatter.allowsSeconds = NO; mediumFormatter.allowsSeconds = NO; @@ -38,12 +33,7 @@ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°55′N, 77°2′W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°55′ north, 77°2′ west"); - if (@available(iOS 9.0, *)) { - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west"); - } else { - // Foundation in iOS 8 does not know how to pluralize coordinates. - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degree(s) and 55 minute(s) north by 77 degree(s) and 2 minute(s) west"); - } + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west"); shortFormatter.allowsMinutes = NO; mediumFormatter.allowsMinutes = NO; @@ -52,12 +42,7 @@ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"39°N, 77°W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"39° north, 77° west"); - if (@available(iOS 9.0, *)) { - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west"); - } else { - // Foundation in iOS 8 does not know how to pluralize coordinates. - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degree(s) north by 77 degree(s) west"); - } + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west"); } @end diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index 9c75dedaa3..dbdad2ed13 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -668,6 +668,38 @@ using namespace std::string_literals; XCTAssertEqualObjects([compatibilityExpression expressionValueWithObject:@{@"number": @1.5} context:nil], @"1.5"); XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); } + { +#if TARGET_OS_IPHONE + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(x, 'UIColor')"]; +#else + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(x, 'NSColor')"]; +#endif + + NSArray *jsonExpression = @[@"to-color", @[@"get", @"x"]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('to-color', x, y, z)"]; + NSArray *jsonExpression = @[@"to-color", @[@"get", @"x"], @[@"get", @"y"], @[@"get", @"z"]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(noindex(x), 'NSArray')"]; + NSArray *jsonExpression = @[@"to-rgba", @[@"get", @"x"]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(noindex(%@), 'NSArray')", MGLConstantExpression(MGLColor.blueColor)]; + NSArray *jsonExpression = @[@"to-rgba", @[@"rgb", @0, @0, @255]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(noindex('x'), 'NSArray')"]; + XCTAssertThrowsSpecificNamed(expression.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } } - (void)testInterpolationExpressionObject { @@ -745,6 +777,12 @@ using namespace std::string_literals; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_MATCH(x, {'a', 'A'}, 'Apple', {'b', 'B'}, 'Banana', 'Kumquat')"]; + NSArray *jsonExpression = @[@"match", @[@"get", @"x"], @[@"a", @"A"], @"Apple", @[@"b", @"B"], @"Banana", @"Kumquat"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } } - (void)testCoalesceExpressionObject { @@ -767,30 +805,26 @@ using namespace std::string_literals; } - (void)testConditionalExpressionObject { - // This test crashes on iOS 8, which doesn't have `+[NSExpression expressionForConditional:trueExpression:falseExpression:]`. - // https://github.com/mapbox/mapbox-gl-native/issues/11007 - if (@available(iOS 9.0, *)) { - { - NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; - NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; - NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; - NSExpression *expression = [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; - NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TERNARY(1 = 2, TRUE, FALSE)"].mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); - XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); - } - { - NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TRUE, TERNARY(1 = 2, TRUE, FALSE))"]; - NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @YES, @[@"==", @1, @2], @YES, @NO]; - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); - expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, TRUE, %@, TRUE, FALSE)", - MGLConstantExpression([NSPredicate predicateWithFormat:@"0 = 1"]), - MGLConstantExpression([NSPredicate predicateWithFormat:@"1 = 2"])]; - XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); - } + { + NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; + NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; + NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; + NSExpression *expression = [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; + NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TERNARY(1 = 2, TRUE, FALSE)"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TRUE, TERNARY(1 = 2, TRUE, FALSE))"]; + NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @YES, @[@"==", @1, @2], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); + expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, TRUE, %@, TRUE, FALSE)", + MGLConstantExpression([NSPredicate predicateWithFormat:@"0 = 1"]), + MGLConstantExpression([NSPredicate predicateWithFormat:@"1 = 2"])]; + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); } { NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, %@, %@)", @@ -801,9 +835,7 @@ using namespace std::string_literals; NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression); - if (@available(iOS 9.0, *)) { - expression = [NSExpression expressionWithFormat:@"TERNARY(1 = 2, YES, NO)"]; - } + expression = [NSExpression expressionWithFormat:@"TERNARY(1 = 2, YES, NO)"]; XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); } @@ -842,6 +874,33 @@ using namespace std::string_literals; MGLConstantExpression(@8), MGLConstantExpression(@7)]]; NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:" + arguments:@[array, MGLConstantExpression(@"FIRST")]]; + NSArray *jsonExpression = @[@"at", @0, @[ @"literal", @[@9, @8, @7]]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + } + { + NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9), + MGLConstantExpression(@8), + MGLConstantExpression(@7)]]; + NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:" + arguments:@[array, MGLConstantExpression(@"LAST")]]; + NSArray *jsonExpression = @[@"at", @[@"-", @[@"length", @[ @"literal", @[@9, @8, @7]]], @1], @[ @"literal", @[@9, @8, @7]]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + } + { + NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9), + MGLConstantExpression(@8), + MGLConstantExpression(@7)]]; + NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:" + arguments:@[array, MGLConstantExpression(@"SIZE")]]; + NSArray *jsonExpression = @[@"length", @[ @"literal", @[@9, @8, @7]]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + } + { + NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9), + MGLConstantExpression(@8), + MGLConstantExpression(@7)]]; + NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:" arguments:@[array, MGLConstantExpression(@1)]]; NSArray *jsonExpression = @[@"at", @1, @[ @"literal", @[@9, @8, @7]]]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); @@ -885,12 +944,7 @@ using namespace std::string_literals; } { NSExpression *expression; - if (@available(iOS 9.0, *)) { - expression = [NSExpression expressionWithFormat:@"TERNARY(key != nil, 1, 0)"]; - } else { - expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, 1, 0)", - MGLConstantExpression([NSPredicate predicateWithFormat:@"key != nil"])]; - } + expression = [NSExpression expressionWithFormat:@"TERNARY(key != nil, 1, 0)"]; NSArray *jsonExpression = @[@"case", @[@"!=", @[@"get", @"key"], [NSNull null]], @1, @0]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); @@ -900,13 +954,7 @@ using namespace std::string_literals; { NSDictionary *dictionary = @{@"key": @"🔑"}; NSExpression *expression; - if (@available(iOS 9.0, *)) { - expression = [NSExpression expressionWithFormat:@"TERNARY(%@.key != nil, 1, 0)", dictionary]; - } else { - NSPredicate *conditional = [NSPredicate predicateWithFormat:@"%@.key != nil", dictionary]; - expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, 1, 0)", - MGLConstantExpression(conditional)]; - } + expression = [NSExpression expressionWithFormat:@"TERNARY(%@.key != nil, 1, 0)", dictionary]; NSArray *jsonExpression = @[@"case", @[@"!=", @[@"get", @"key", @[@"literal", dictionary]], [NSNull null]], @1, @0]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); // The dictionary isn’t equal enough. diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm index 14b64be854..d135b018f4 100644 --- a/platform/darwin/test/MGLFeatureTests.mm +++ b/platform/darwin/test/MGLFeatureTests.mm @@ -38,7 +38,7 @@ }; features.push_back(mbgl::Feature { polygon }); - NS_ARRAY_OF(MGLShape <MGLFeature> *) *shapes = MGLFeaturesFromMBGLFeatures(features); + NSArray<MGLShape <MGLFeature> *> *shapes = MGLFeaturesFromMBGLFeatures(features); XCTAssertEqual(shapes.count, 3, @"All features should be converted into shapes"); MGLPointFeature *pointShape = (MGLPointFeature *)shapes[0]; @@ -69,7 +69,7 @@ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(4, 4)]); XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:polygonCoordinates[3]], [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(4, 1)]); - NS_ARRAY_OF(MGLPolygon *) *interiorPolygons = polygonShape.interiorPolygons; + NSArray<MGLPolygon *> *interiorPolygons = polygonShape.interiorPolygons; XCTAssertEqual(interiorPolygons.count, 1); MGLPolygon *interiorPolygon = interiorPolygons.firstObject; XCTAssertEqual(interiorPolygon.pointCount, 4); @@ -103,7 +103,7 @@ vector.push_back(true); features.push_back(pointFeature); - NS_ARRAY_OF(MGLShape <MGLFeature> *) *shapes = MGLFeaturesFromMBGLFeatures(features); + NSArray<MGLShape <MGLFeature> *> *shapes = MGLFeaturesFromMBGLFeatures(features); XCTAssertEqual(shapes.count, 1, @"All features should be converted into shapes"); MGLShape <MGLFeature> *shape = shapes.firstObject; diff --git a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm index 6081d104e1..ac858d0edc 100644 --- a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm +++ b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm @@ -71,7 +71,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, @"Setting fillExtrusionBase to a camera expression should update fill-extrusion-base."); XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression, @@ -102,7 +102,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillExtrusionBase, pedanticFunctionExpression, @"fillExtrusionBase should round-trip camera-data expressions."); - layer.fillExtrusionBase = nil; XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(), @@ -143,7 +142,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, @"Setting fillExtrusionColor to a camera expression should update fill-extrusion-color."); XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression, @@ -174,7 +173,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillExtrusionColor, pedanticFunctionExpression, @"fillExtrusionColor should round-trip camera-data expressions."); - layer.fillExtrusionColor = nil; XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(), @@ -215,7 +213,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, @"Setting fillExtrusionHeight to a camera expression should update fill-extrusion-height."); XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression, @@ -246,7 +244,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillExtrusionHeight, pedanticFunctionExpression, @"fillExtrusionHeight should round-trip camera-data expressions."); - layer.fillExtrusionHeight = nil; XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(), @@ -287,13 +284,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue, @"Setting fillExtrusionOpacity to a camera expression should update fill-extrusion-opacity."); XCTAssertEqualObjects(layer.fillExtrusionOpacity, functionExpression, @"fillExtrusionOpacity should round-trip camera expressions."); - layer.fillExtrusionOpacity = nil; XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(), @@ -340,13 +336,12 @@ { 18, "Fill Extrusion Pattern" }, }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue, @"Setting fillExtrusionPattern to a camera expression should update fill-extrusion-pattern."); XCTAssertEqualObjects(layer.fillExtrusionPattern, functionExpression, @"fillExtrusionPattern should round-trip camera expressions."); - layer.fillExtrusionPattern = nil; XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(), @@ -399,13 +394,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue, @"Setting fillExtrusionTranslation to a camera expression should update fill-extrusion-translate."); XCTAssertEqualObjects(layer.fillExtrusionTranslation, functionExpression, @"fillExtrusionTranslation should round-trip camera expressions."); - layer.fillExtrusionTranslation = nil; XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(), @@ -443,13 +437,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue, @"Setting fillExtrusionTranslationAnchor to a camera expression should update fill-extrusion-translate-anchor."); XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, functionExpression, @"fillExtrusionTranslationAnchor should round-trip camera expressions."); - layer.fillExtrusionTranslationAnchor = nil; XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(), diff --git a/platform/darwin/test/MGLFillStyleLayerTests.mm b/platform/darwin/test/MGLFillStyleLayerTests.mm index a5019f1032..45bae07f43 100644 --- a/platform/darwin/test/MGLFillStyleLayerTests.mm +++ b/platform/darwin/test/MGLFillStyleLayerTests.mm @@ -71,13 +71,12 @@ { 18, false }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillAntialias(), propertyValue, @"Setting fillAntialiased to a camera expression should update fill-antialias."); XCTAssertEqualObjects(layer.fillAntialiased, functionExpression, @"fillAntialiased should round-trip camera expressions."); - layer.fillAntialiased = nil; XCTAssertTrue(rawLayer->getFillAntialias().isUndefined(), @@ -115,7 +114,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillColor(), propertyValue, @"Setting fillColor to a camera expression should update fill-color."); XCTAssertEqualObjects(layer.fillColor, functionExpression, @@ -146,7 +145,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillColor, pedanticFunctionExpression, @"fillColor should round-trip camera-data expressions."); - layer.fillColor = nil; XCTAssertTrue(rawLayer->getFillColor().isUndefined(), @@ -187,7 +185,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue, @"Setting fillOpacity to a camera expression should update fill-opacity."); XCTAssertEqualObjects(layer.fillOpacity, functionExpression, @@ -218,7 +216,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillOpacity, pedanticFunctionExpression, @"fillOpacity should round-trip camera-data expressions."); - layer.fillOpacity = nil; XCTAssertTrue(rawLayer->getFillOpacity().isUndefined(), @@ -259,7 +256,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue, @"Setting fillOutlineColor to a camera expression should update fill-outline-color."); XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression, @@ -290,7 +287,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillOutlineColor, pedanticFunctionExpression, @"fillOutlineColor should round-trip camera-data expressions."); - layer.fillOutlineColor = nil; XCTAssertTrue(rawLayer->getFillOutlineColor().isUndefined(), @@ -331,13 +327,12 @@ { 18, "Fill Pattern" }, }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillPattern(), propertyValue, @"Setting fillPattern to a camera expression should update fill-pattern."); XCTAssertEqualObjects(layer.fillPattern, functionExpression, @"fillPattern should round-trip camera expressions."); - layer.fillPattern = nil; XCTAssertTrue(rawLayer->getFillPattern().isUndefined(), @@ -390,13 +385,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillTranslate(), propertyValue, @"Setting fillTranslation to a camera expression should update fill-translate."); XCTAssertEqualObjects(layer.fillTranslation, functionExpression, @"fillTranslation should round-trip camera expressions."); - layer.fillTranslation = nil; XCTAssertTrue(rawLayer->getFillTranslate().isUndefined(), @@ -434,13 +428,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillTranslateAnchor(), propertyValue, @"Setting fillTranslationAnchor to a camera expression should update fill-translate-anchor."); XCTAssertEqualObjects(layer.fillTranslationAnchor, functionExpression, @"fillTranslationAnchor should round-trip camera expressions."); - layer.fillTranslationAnchor = nil; XCTAssertTrue(rawLayer->getFillTranslateAnchor().isUndefined(), diff --git a/platform/darwin/test/MGLGeometryTests.mm b/platform/darwin/test/MGLGeometryTests.mm index a0ddecf77e..1489fefea9 100644 --- a/platform/darwin/test/MGLGeometryTests.mm +++ b/platform/darwin/test/MGLGeometryTests.mm @@ -172,4 +172,40 @@ XCTAssertEqual(point.y, roundTrippedPoint.y); XCTAssertEqual(point.zoomLevel, roundTrippedPoint.zoomLevel); } + +- (void)testMGLLocationCoordinate2DIsValid { + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(37.936, -71.516); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(46.816368, 5.844469); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(-21.512680, 23.334703); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(-44.947936, -73.081313); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(19.333630, 203.555405); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(23.254696, -240.795323); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(91, 361); + XCTAssertFalse(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(-91, -361); + XCTAssertFalse(MGLLocationCoordinate2DIsValid(coordinate)); + } +} + @end diff --git a/platform/darwin/test/MGLHeatmapStyleLayerTests.mm b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm index e4b1917257..db15778872 100644 --- a/platform/darwin/test/MGLHeatmapStyleLayerTests.mm +++ b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm @@ -71,13 +71,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getHeatmapIntensity(), propertyValue, @"Setting heatmapIntensity to a camera expression should update heatmap-intensity."); XCTAssertEqualObjects(layer.heatmapIntensity, functionExpression, @"heatmapIntensity should round-trip camera expressions."); - layer.heatmapIntensity = nil; XCTAssertTrue(rawLayer->getHeatmapIntensity().isUndefined(), @@ -124,13 +123,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getHeatmapOpacity(), propertyValue, @"Setting heatmapOpacity to a camera expression should update heatmap-opacity."); XCTAssertEqualObjects(layer.heatmapOpacity, functionExpression, @"heatmapOpacity should round-trip camera expressions."); - layer.heatmapOpacity = nil; XCTAssertTrue(rawLayer->getHeatmapOpacity().isUndefined(), @@ -177,7 +175,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue, @"Setting heatmapRadius to a camera expression should update heatmap-radius."); XCTAssertEqualObjects(layer.heatmapRadius, functionExpression, @@ -208,7 +206,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.heatmapRadius, pedanticFunctionExpression, @"heatmapRadius should round-trip camera-data expressions."); - layer.heatmapRadius = nil; XCTAssertTrue(rawLayer->getHeatmapRadius().isUndefined(), @@ -249,7 +246,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue, @"Setting heatmapWeight to a camera expression should update heatmap-weight."); XCTAssertEqualObjects(layer.heatmapWeight, functionExpression, @@ -280,7 +277,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.heatmapWeight, pedanticFunctionExpression, @"heatmapWeight should round-trip camera-data expressions."); - layer.heatmapWeight = nil; XCTAssertTrue(rawLayer->getHeatmapWeight().isUndefined(), diff --git a/platform/darwin/test/MGLHillshadeStyleLayerTests.mm b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm index 34937d1674..2495b32d97 100644 --- a/platform/darwin/test/MGLHillshadeStyleLayerTests.mm +++ b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm @@ -53,13 +53,12 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeAccentColor(), propertyValue, @"Setting hillshadeAccentColor to a camera expression should update hillshade-accent-color."); XCTAssertEqualObjects(layer.hillshadeAccentColor, functionExpression, @"hillshadeAccentColor should round-trip camera expressions."); - layer.hillshadeAccentColor = nil; XCTAssertTrue(rawLayer->getHillshadeAccentColor().isUndefined(), @@ -106,13 +105,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeExaggeration(), propertyValue, @"Setting hillshadeExaggeration to a camera expression should update hillshade-exaggeration."); XCTAssertEqualObjects(layer.hillshadeExaggeration, functionExpression, @"hillshadeExaggeration should round-trip camera expressions."); - layer.hillshadeExaggeration = nil; XCTAssertTrue(rawLayer->getHillshadeExaggeration().isUndefined(), @@ -159,13 +157,12 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeHighlightColor(), propertyValue, @"Setting hillshadeHighlightColor to a camera expression should update hillshade-highlight-color."); XCTAssertEqualObjects(layer.hillshadeHighlightColor, functionExpression, @"hillshadeHighlightColor should round-trip camera expressions."); - layer.hillshadeHighlightColor = nil; XCTAssertTrue(rawLayer->getHillshadeHighlightColor().isUndefined(), @@ -212,13 +209,12 @@ { 18, mbgl::style::HillshadeIlluminationAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::HillshadeIlluminationAnchorType> { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeIlluminationAnchor(), propertyValue, @"Setting hillshadeIlluminationAnchor to a camera expression should update hillshade-illumination-anchor."); XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, functionExpression, @"hillshadeIlluminationAnchor should round-trip camera expressions."); - layer.hillshadeIlluminationAnchor = nil; XCTAssertTrue(rawLayer->getHillshadeIlluminationAnchor().isUndefined(), @@ -256,13 +252,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeIlluminationDirection(), propertyValue, @"Setting hillshadeIlluminationDirection to a camera expression should update hillshade-illumination-direction."); XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, functionExpression, @"hillshadeIlluminationDirection should round-trip camera expressions."); - layer.hillshadeIlluminationDirection = nil; XCTAssertTrue(rawLayer->getHillshadeIlluminationDirection().isUndefined(), @@ -300,13 +295,12 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeShadowColor(), propertyValue, @"Setting hillshadeShadowColor to a camera expression should update hillshade-shadow-color."); XCTAssertEqualObjects(layer.hillshadeShadowColor, functionExpression, @"hillshadeShadowColor should round-trip camera expressions."); - layer.hillshadeShadowColor = nil; XCTAssertTrue(rawLayer->getHillshadeShadowColor().isUndefined(), diff --git a/platform/darwin/test/MGLLineStyleLayerTests.mm b/platform/darwin/test/MGLLineStyleLayerTests.mm index 5490278e98..aa24bc8c15 100644 --- a/platform/darwin/test/MGLLineStyleLayerTests.mm +++ b/platform/darwin/test/MGLLineStyleLayerTests.mm @@ -71,13 +71,12 @@ { 18, mbgl::style::LineCapType::Square }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::LineCapType> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineCap(), propertyValue, @"Setting lineCap to a camera expression should update line-cap."); XCTAssertEqualObjects(layer.lineCap, functionExpression, @"lineCap should round-trip camera expressions."); - layer.lineCap = nil; XCTAssertTrue(rawLayer->getLineCap().isUndefined(), @@ -115,13 +114,12 @@ { 18, mbgl::style::LineJoinType::Miter }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::LineJoinType> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineJoin(), propertyValue, @"Setting lineJoin to a camera expression should update line-join."); XCTAssertEqualObjects(layer.lineJoin, functionExpression, @"lineJoin should round-trip camera expressions."); - layer.lineJoin = nil; XCTAssertTrue(rawLayer->getLineJoin().isUndefined(), @@ -153,13 +151,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineMiterLimit(), propertyValue, @"Setting lineMiterLimit to a camera expression should update line-miter-limit."); XCTAssertEqualObjects(layer.lineMiterLimit, functionExpression, @"lineMiterLimit should round-trip camera expressions."); - layer.lineMiterLimit = nil; XCTAssertTrue(rawLayer->getLineMiterLimit().isUndefined(), @@ -197,13 +194,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineRoundLimit(), propertyValue, @"Setting lineRoundLimit to a camera expression should update line-round-limit."); XCTAssertEqualObjects(layer.lineRoundLimit, functionExpression, @"lineRoundLimit should round-trip camera expressions."); - layer.lineRoundLimit = nil; XCTAssertTrue(rawLayer->getLineRoundLimit().isUndefined(), @@ -241,7 +237,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineBlur(), propertyValue, @"Setting lineBlur to a camera expression should update line-blur."); XCTAssertEqualObjects(layer.lineBlur, functionExpression, @@ -272,7 +268,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineBlur, pedanticFunctionExpression, @"lineBlur should round-trip camera-data expressions."); - layer.lineBlur = nil; XCTAssertTrue(rawLayer->getLineBlur().isUndefined(), @@ -313,7 +308,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineColor(), propertyValue, @"Setting lineColor to a camera expression should update line-color."); XCTAssertEqualObjects(layer.lineColor, functionExpression, @@ -344,7 +339,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineColor, pedanticFunctionExpression, @"lineColor should round-trip camera-data expressions."); - layer.lineColor = nil; XCTAssertTrue(rawLayer->getLineColor().isUndefined(), @@ -385,13 +379,12 @@ { 18, {1, 2} }, }}; propertyValue = mbgl::style::CameraFunction<std::vector<float>> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineDasharray(), propertyValue, @"Setting lineDashPattern to a camera expression should update line-dasharray."); XCTAssertEqualObjects(layer.lineDashPattern, functionExpression, @"lineDashPattern should round-trip camera expressions."); - layer.lineDashPattern = nil; XCTAssertTrue(rawLayer->getLineDasharray().isUndefined(), @@ -429,7 +422,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue, @"Setting lineGapWidth to a camera expression should update line-gap-width."); XCTAssertEqualObjects(layer.lineGapWidth, functionExpression, @@ -460,7 +453,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineGapWidth, pedanticFunctionExpression, @"lineGapWidth should round-trip camera-data expressions."); - layer.lineGapWidth = nil; XCTAssertTrue(rawLayer->getLineGapWidth().isUndefined(), @@ -501,7 +493,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineOffset(), propertyValue, @"Setting lineOffset to a camera expression should update line-offset."); XCTAssertEqualObjects(layer.lineOffset, functionExpression, @@ -532,7 +524,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineOffset, pedanticFunctionExpression, @"lineOffset should round-trip camera-data expressions."); - layer.lineOffset = nil; XCTAssertTrue(rawLayer->getLineOffset().isUndefined(), @@ -573,7 +564,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue, @"Setting lineOpacity to a camera expression should update line-opacity."); XCTAssertEqualObjects(layer.lineOpacity, functionExpression, @@ -604,7 +595,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineOpacity, pedanticFunctionExpression, @"lineOpacity should round-trip camera-data expressions."); - layer.lineOpacity = nil; XCTAssertTrue(rawLayer->getLineOpacity().isUndefined(), @@ -645,13 +635,12 @@ { 18, "Line Pattern" }, }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; - + XCTAssertEqual(rawLayer->getLinePattern(), propertyValue, @"Setting linePattern to a camera expression should update line-pattern."); XCTAssertEqualObjects(layer.linePattern, functionExpression, @"linePattern should round-trip camera expressions."); - layer.linePattern = nil; XCTAssertTrue(rawLayer->getLinePattern().isUndefined(), @@ -704,13 +693,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineTranslate(), propertyValue, @"Setting lineTranslation to a camera expression should update line-translate."); XCTAssertEqualObjects(layer.lineTranslation, functionExpression, @"lineTranslation should round-trip camera expressions."); - layer.lineTranslation = nil; XCTAssertTrue(rawLayer->getLineTranslate().isUndefined(), @@ -748,13 +736,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineTranslateAnchor(), propertyValue, @"Setting lineTranslationAnchor to a camera expression should update line-translate-anchor."); XCTAssertEqualObjects(layer.lineTranslationAnchor, functionExpression, @"lineTranslationAnchor should round-trip camera expressions."); - layer.lineTranslationAnchor = nil; XCTAssertTrue(rawLayer->getLineTranslateAnchor().isUndefined(), @@ -792,7 +779,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineWidth(), propertyValue, @"Setting lineWidth to a camera expression should update line-width."); XCTAssertEqualObjects(layer.lineWidth, functionExpression, @@ -823,7 +810,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineWidth, pedanticFunctionExpression, @"lineWidth should round-trip camera-data expressions."); - layer.lineWidth = nil; XCTAssertTrue(rawLayer->getLineWidth().isUndefined(), diff --git a/platform/darwin/test/MGLNSStringAdditionsTests.m b/platform/darwin/test/MGLNSStringAdditionsTests.m index 2a8715d991..a3ee7e3433 100644 --- a/platform/darwin/test/MGLNSStringAdditionsTests.m +++ b/platform/darwin/test/MGLNSStringAdditionsTests.m @@ -40,37 +40,29 @@ } - (void)testTransliteratedString { - if (@available(iOS 9.0, *)) { - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Portland"); - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Portland"); - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Портланд"); - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Arab"], @"پُرتلَند"); - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Portland"); - - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Latn"], @"běi jīng"); - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Hans"], @"北京"); - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"бе̌и йӣнг"); - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Arab"], @"بِِ̌ جِينگ"); - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Fake"], @"北京"); - - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Moskva"); - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Mосква"); - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Москва"); - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Arab"], @"مُسكڤَ"); - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Mосква"); - - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Latn"], @"rondon"); - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Hans"], @"ロンドン"); - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"рондон"); - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Arab"], @"رُندُن"); - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Fake"], @"ロンドン"); - } else { - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Made-up Place"); - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Made-up Place"); - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Made-up Place"); - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Arab"], @"Made-up Place"); - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Made-up Place"); - } + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Portland"); + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Portland"); + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Портланд"); + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Arab"], @"پُرتلَند"); + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Portland"); + + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Latn"], @"běi jīng"); + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Hans"], @"北京"); + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"бе̌и йӣнг"); + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Arab"], @"بِِ̌ جِينگ"); + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Fake"], @"北京"); + + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Moskva"); + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Mосква"); + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Москва"); + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Arab"], @"مُسكڤَ"); + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Mосква"); + + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Latn"], @"rondon"); + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Hans"], @"ロンドン"); + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"рондон"); + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Arab"], @"رُندُن"); + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Fake"], @"ロンドン"); } @end diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index ab4a7e2d88..41c2d56868 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -1,229 +1,15 @@ #import <XCTest/XCTest.h> #import <Mapbox/Mapbox.h> -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "MGLValueEvaluator.h" -namespace mbgl { - namespace style { - bool operator!=(const Filter &a, const Filter &b) { - return !(a == b); - } - } -} - -#define MGLAssertEqualFilters(actual, expected, ...) \ - XCTAssertTrue(actual.is<__typeof__(expected)>()); \ - if (actual.is<__typeof__(expected)>()) { \ - XCTAssertEqual(actual.get<__typeof__(expected)>(), expected, __VA_ARGS__); \ - } - @interface MGLPredicateTests : XCTestCase @end @implementation MGLPredicateTests -- (void)testPredication { - XCTAssertNil([NSPredicate mgl_predicateWithFilter:mbgl::style::NullFilter()]); - - { - mbgl::style::EqualsFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = 'b'"]); - } - - { - mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Point }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'Point'", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::LineString }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'LineString'", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Polygon }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'Polygon'", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::IdentifierEqualsFilter filter = { .value = UINT64_C(67086180) }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 67086180", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::NotHasIdentifierFilter filter; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = nil", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Unknown }; - XCTAssertThrowsSpecificNamed([NSPredicate mgl_predicateWithFilter:filter], NSException, NSInternalInconsistencyException); - } - - { - mbgl::style::NotHasFilter filter = { .key = "a" }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = nil"]); - } - - { - mbgl::style::NotEqualsFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != 'b'"]); - } - - { - mbgl::style::TypeNotEqualsFilter filter = { .value = mbgl::FeatureType::Point }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != 'Point'", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::IdentifierNotEqualsFilter filter = { .value = UINT64_C(67086180) }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != 67086180", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::HasIdentifierFilter filter; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::HasFilter filter = { .key = "a" }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != nil"]); - } - - { - mbgl::style::LessThanFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a < 'b'"]); - } - - { - mbgl::style::LessThanEqualsFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a <= 'b'"]); - } - - { - mbgl::style::GreaterThanFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a > 'b'"]); - } - - { - mbgl::style::GreaterThanEqualsFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a >= 'b'"]); - } - - { - mbgl::style::AllFilter filter = { - .filters = { - mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") }, - mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]); - } - - { - mbgl::style::AllFilter filter = { - .filters = { - mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") }, - mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]); - } - - { - mbgl::style::InFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].predicateFormat); - } - - { - mbgl::style::TypeInFilter filter = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {'LineString', 'Polygon'}", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, expected.predicateFormat); - } - - { - mbgl::style::IdentifierInFilter filter = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::NotInFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].predicateFormat); - } - - { - mbgl::style::TypeNotInFilter filter = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {'LineString', 'Polygon'}", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, expected.predicateFormat); - } - - { - mbgl::style::IdentifierNotInFilter filter = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::AllFilter filter; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]); - } - - { - mbgl::style::AllFilter filter = { - .filters = { - mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") }, - mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"]); - } - - { - mbgl::style::AnyFilter filter; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:NO]); - } - - { - mbgl::style::AnyFilter filter = { - .filters = { - mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") }, - mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"]); - } - - { - mbgl::style::NoneFilter filter; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]); - } - - { - mbgl::style::NoneFilter filter = { - .filters = { - mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") }, - mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"]); - } -} - - (void)testUnsupportedFilterPredicates { - XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 2"].mgl_filter, NSException, NSInvalidArgumentException); - XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 1"].mgl_filter, NSException, NSInvalidArgumentException); - XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:YES].mgl_filter, NSException, NSInvalidArgumentException); - XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:NO].mgl_filter, NSException, NSInvalidArgumentException); XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException); XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException); XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException); @@ -242,48 +28,48 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x == YES"]; NSArray *jsonExpression = @[@"==", @[@"get", @"x"], @YES]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') < 5"]; NSArray *jsonExpression = @[@"<", @[@"to-number", @[@"get", @"x"]], @5]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') > 5"]; NSArray *jsonExpression = @[@">", @[@"to-number", @[@"get", @"x"]], @5]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') <= 5"]; NSArray *jsonExpression = @[@"<=", @[@"to-number", @[@"get", @"x"]], @5]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') >= 5"]; NSArray *jsonExpression = @[@">=", @[@"to-number", @[@"get", @"x"]], @5]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSString') > 'value'"]; NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"x"]], @"value"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { @@ -360,48 +146,48 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') < 'b'"]; NSArray *jsonExpression = @[@"<", @[@"to-string", @[@"get", @"a"]], @"b"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <= 'b'"]; NSArray *jsonExpression = @[@"<=", @[@"to-string", @[@"get", @"a"]], @"b"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') > 'b'"]; NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"a"]], @"b"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >= 'b'"]; NSArray *jsonExpression = @[@">=", @[@"to-string", @[@"get", @"a"]], @"b"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') BETWEEN {'b', 'z'}"]; NSArray *jsonExpression =@[@"all", @[@"<=", @"b", @[@"to-string", @[@"get", @"a"]]], @[@"<=", @[@"to-string", @[@"get", @"a"]], @"z"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits]; NSArray *jsonExpression = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { @@ -409,86 +195,86 @@ namespace mbgl { NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected] mustRoundTrip:NO]; } { NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]]; NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected] mustRoundTrip:NO]; } { NSArray *expected = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]]; NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected] mustRoundTrip:NO]; } { - NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; + NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($featureIdentifier, { 3, 4, 5, 6 }, YES, NO) == YES"]; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]]; + NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(x, { 3, 4, 5, 6 }, YES, NO) == YES"]; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"get", @"a"], @"b", @YES, @"c", @YES, @NO]; + NSArray *expected = @[@"match", @[@"get", @"a"], @[@"b", @"c"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), 'b', YES, 'c', YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(a, { 'b', 'c' }, YES, NO) == YES"]; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO]; + NSArray *expected = @[@"match", @[@"geometry-type"], @[@"LineString", @"Polygon"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"], @[@"LineString", @"Polygon"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, { 'LineString', 'Polygon' }, YES, NO) == YES"]; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS x"]; + NSArray *expected = @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3 } CONTAINS x"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(x, { 3, 4, 5, 6 }, YES, NO) == YES"]; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO]; + NSArray *expected = @[@"match", @[@"geometry-type"], @[@"LineString", @"Polygon"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"LineString", @"Polygon"], [NSExpression expressionForVariable:@"geometryType"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, { 'LineString', 'Polygon' }, YES, NO) == YES"]; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; + NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($featureIdentifier, { 3, 4, 5, 6 }, YES, NO) == YES"]; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } @@ -499,32 +285,32 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"]; NSArray *jsonExpression = @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"]; NSArray *jsonExpression = @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"]; NSArray *jsonExpression = @[@"!", @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"]; NSArray *jsonExpression = @[@"!", @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { diff --git a/platform/darwin/test/MGLRasterStyleLayerTests.mm b/platform/darwin/test/MGLRasterStyleLayerTests.mm index c8e454743e..cf5175812e 100644 --- a/platform/darwin/test/MGLRasterStyleLayerTests.mm +++ b/platform/darwin/test/MGLRasterStyleLayerTests.mm @@ -53,13 +53,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterBrightnessMax(), propertyValue, @"Setting maximumRasterBrightness to a camera expression should update raster-brightness-max."); XCTAssertEqualObjects(layer.maximumRasterBrightness, functionExpression, @"maximumRasterBrightness should round-trip camera expressions."); - layer.maximumRasterBrightness = nil; XCTAssertTrue(rawLayer->getRasterBrightnessMax().isUndefined(), @@ -97,13 +96,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterBrightnessMin(), propertyValue, @"Setting minimumRasterBrightness to a camera expression should update raster-brightness-min."); XCTAssertEqualObjects(layer.minimumRasterBrightness, functionExpression, @"minimumRasterBrightness should round-trip camera expressions."); - layer.minimumRasterBrightness = nil; XCTAssertTrue(rawLayer->getRasterBrightnessMin().isUndefined(), @@ -141,13 +139,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterContrast(), propertyValue, @"Setting rasterContrast to a camera expression should update raster-contrast."); XCTAssertEqualObjects(layer.rasterContrast, functionExpression, @"rasterContrast should round-trip camera expressions."); - layer.rasterContrast = nil; XCTAssertTrue(rawLayer->getRasterContrast().isUndefined(), @@ -194,13 +191,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterFadeDuration(), propertyValue, @"Setting rasterFadeDuration to a camera expression should update raster-fade-duration."); XCTAssertEqualObjects(layer.rasterFadeDuration, functionExpression, @"rasterFadeDuration should round-trip camera expressions."); - layer.rasterFadeDuration = nil; XCTAssertTrue(rawLayer->getRasterFadeDuration().isUndefined(), @@ -238,13 +234,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterHueRotate(), propertyValue, @"Setting rasterHueRotation to a camera expression should update raster-hue-rotate."); XCTAssertEqualObjects(layer.rasterHueRotation, functionExpression, @"rasterHueRotation should round-trip camera expressions."); - layer.rasterHueRotation = nil; XCTAssertTrue(rawLayer->getRasterHueRotate().isUndefined(), @@ -282,13 +277,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterOpacity(), propertyValue, @"Setting rasterOpacity to a camera expression should update raster-opacity."); XCTAssertEqualObjects(layer.rasterOpacity, functionExpression, @"rasterOpacity should round-trip camera expressions."); - layer.rasterOpacity = nil; XCTAssertTrue(rawLayer->getRasterOpacity().isUndefined(), @@ -335,13 +329,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterSaturation(), propertyValue, @"Setting rasterSaturation to a camera expression should update raster-saturation."); XCTAssertEqualObjects(layer.rasterSaturation, functionExpression, @"rasterSaturation should round-trip camera expressions."); - layer.rasterSaturation = nil; XCTAssertTrue(rawLayer->getRasterSaturation().isUndefined(), diff --git a/platform/darwin/test/MGLStyleLayerTests.h b/platform/darwin/test/MGLStyleLayerTests.h index 28c316c8ba..c7577819b8 100644 --- a/platform/darwin/test/MGLStyleLayerTests.h +++ b/platform/darwin/test/MGLStyleLayerTests.h @@ -14,7 +14,7 @@ @interface NSString (MGLStyleLayerTestAdditions) -@property (nonatomic, readonly, copy) NS_ARRAY_OF(NSString *) *lexicalClasses; +@property (nonatomic, readonly, copy) NSArray<NSString *> *lexicalClasses; @property (nonatomic, readonly, copy) NSString *lemma; @end diff --git a/platform/darwin/test/MGLStyleLayerTests.m b/platform/darwin/test/MGLStyleLayerTests.m index b51fa02af4..52b36dba00 100644 --- a/platform/darwin/test/MGLStyleLayerTests.m +++ b/platform/darwin/test/MGLStyleLayerTests.m @@ -33,7 +33,7 @@ } - (void)testPropertyName:(NSString *)name isBoolean:(BOOL)isBoolean { - NS_MUTABLE_ARRAY_OF(NSString *) *components = [name componentsSeparatedByString:@"-"].mutableCopy; + NSMutableArray<NSString *> *components = [name componentsSeparatedByString:@"-"].mutableCopy; if (isBoolean) { if ([components.firstObject isEqualToString:@"is"]) { [components removeObjectAtIndex:0]; @@ -67,7 +67,7 @@ @implementation NSString (MGLStyleLayerTestAdditions) -- (NS_ARRAY_OF(NSString *) *)lexicalClasses { +- (NSArray<NSString *> *)lexicalClasses { NSOrthography *orthography = [NSOrthography orthographyWithDominantScript:@"Latn" languageMap:@{@"Latn": @[@"en"]}]; NSLinguisticTaggerOptions options = (NSLinguisticTaggerOmitPunctuation diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs index f70f0bba23..57e5fce533 100644 --- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs +++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs @@ -59,7 +59,7 @@ MGLTransition transitionTest = MGLTransitionMake(5, 4); <% for (const property of properties) { -%> -<% if (property.name === 'heatmap-color') continue; -%> +<% if (property['property-type'] === 'color-ramp') continue; -%> // <%- originalPropertyName(property) %> { @@ -69,7 +69,7 @@ NSExpression *constantExpression = [NSExpression expressionWithFormat:<%- objCTestValue(property, type, true, 3) %>]; layer.<%- objCName(property) %> = constantExpression; -<% if (property["property-function"]) { -%> +<% if (isDataDriven(property)) { -%> mbgl::style::DataDrivenPropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> }; <% } else { -%> mbgl::style::PropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> }; @@ -88,13 +88,13 @@ { 18, <%- mbglTestValue(property, type) %> }, }}; propertyValue = mbgl::style::CameraFunction<<%- mbglType(property) %>> { intervalStops }; - + XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue, @"Setting <%- objCName(property) %> to a camera expression should update <%- originalPropertyName(property) %>."); XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression, @"<%- objCName(property) %> should round-trip camera expressions."); -<% if (property["property-function"] && isInterpolatable(property)) { -%> +<% if (isDataDriven(property) && isInterpolatable(property)) { -%> functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}]; layer.<%- objCName(property) %> = functionExpression; @@ -120,7 +120,7 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.<%- objCName(property) %>, pedanticFunctionExpression, @"<%- objCName(property) %> should round-trip camera-data expressions."); -<% } -%> +<% } -%> <% if (!property.required) { -%> layer.<%- objCName(property) %> = nil; @@ -129,7 +129,7 @@ XCTAssertEqualObjects(layer.<%- objCName(property) %>, defaultExpression, @"<%- objCName(property) %> should return the default value after being unset."); <% } -%> -<% if (!property["property-function"]) { -%> +<% if (!isDataDriven(property)) { -%> functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionExpression, NSException, NSInvalidArgumentException, @"MGL<%- camelize(type) %>Layer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); @@ -154,7 +154,7 @@ - (void)testPropertyNames { <% for (const property of properties) { -%> -<% if (property.name === 'heatmap-color') continue; -%> +<% if (property['property-type'] === 'color-ramp') continue; -%> [self testPropertyName:@"<%- property.getter || property.name %>" isBoolean:<%- property.type === 'boolean' ? 'YES' : 'NO' %>]; <% } -%> } diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index 6048f39ea3..32243c1bec 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -448,6 +448,10 @@ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]); } { + NSArray *preferences = @[@"en", @"fr", @"el"]; + XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en"); + } + { NSArray *preferences = @[@"tlh"]; XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]); } diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm index cf2f80125a..e1a7ee4e0e 100644 --- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm +++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm @@ -71,13 +71,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconAllowOverlap(), propertyValue, @"Setting iconAllowsOverlap to a camera expression should update icon-allow-overlap."); XCTAssertEqualObjects(layer.iconAllowsOverlap, functionExpression, @"iconAllowsOverlap should round-trip camera expressions."); - layer.iconAllowsOverlap = nil; XCTAssertTrue(rawLayer->getIconAllowOverlap().isUndefined(), @@ -115,13 +114,12 @@ { 18, mbgl::style::SymbolAnchorType::BottomRight }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolAnchorType> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconAnchor(), propertyValue, @"Setting iconAnchor to a camera expression should update icon-anchor."); XCTAssertEqualObjects(layer.iconAnchor, functionExpression, @"iconAnchor should round-trip camera expressions."); - layer.iconAnchor = nil; XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(), @@ -153,13 +151,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconIgnorePlacement(), propertyValue, @"Setting iconIgnoresPlacement to a camera expression should update icon-ignore-placement."); XCTAssertEqualObjects(layer.iconIgnoresPlacement, functionExpression, @"iconIgnoresPlacement should round-trip camera expressions."); - layer.iconIgnoresPlacement = nil; XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(), @@ -197,13 +194,12 @@ { 18, "Icon Image" }, }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconImage(), propertyValue, @"Setting iconImageName to a camera expression should update icon-image."); XCTAssertEqualObjects(layer.iconImageName, functionExpression, @"iconImageName should round-trip camera expressions."); - layer.iconImageName = nil; XCTAssertTrue(rawLayer->getIconImage().isUndefined(), @@ -241,7 +237,7 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconOffset(), propertyValue, @"Setting iconOffset to a camera expression should update icon-offset."); XCTAssertEqualObjects(layer.iconOffset, functionExpression, @@ -272,7 +268,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconOffset, pedanticFunctionExpression, @"iconOffset should round-trip camera-data expressions."); - layer.iconOffset = nil; XCTAssertTrue(rawLayer->getIconOffset().isUndefined(), @@ -304,13 +299,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconOptional(), propertyValue, @"Setting iconOptional to a camera expression should update icon-optional."); XCTAssertEqualObjects(layer.iconOptional, functionExpression, @"iconOptional should round-trip camera expressions."); - layer.iconOptional = nil; XCTAssertTrue(rawLayer->getIconOptional().isUndefined(), @@ -348,13 +342,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconPadding(), propertyValue, @"Setting iconPadding to a camera expression should update icon-padding."); XCTAssertEqualObjects(layer.iconPadding, functionExpression, @"iconPadding should round-trip camera expressions."); - layer.iconPadding = nil; XCTAssertTrue(rawLayer->getIconPadding().isUndefined(), @@ -392,13 +385,12 @@ { 18, mbgl::style::AlignmentType::Auto }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconPitchAlignment(), propertyValue, @"Setting iconPitchAlignment to a camera expression should update icon-pitch-alignment."); XCTAssertEqualObjects(layer.iconPitchAlignment, functionExpression, @"iconPitchAlignment should round-trip camera expressions."); - layer.iconPitchAlignment = nil; XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(), @@ -436,7 +428,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconRotate(), propertyValue, @"Setting iconRotation to a camera expression should update icon-rotate."); XCTAssertEqualObjects(layer.iconRotation, functionExpression, @@ -467,7 +459,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconRotation, pedanticFunctionExpression, @"iconRotation should round-trip camera-data expressions."); - layer.iconRotation = nil; XCTAssertTrue(rawLayer->getIconRotate().isUndefined(), @@ -499,13 +490,12 @@ { 18, mbgl::style::AlignmentType::Auto }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconRotationAlignment(), propertyValue, @"Setting iconRotationAlignment to a camera expression should update icon-rotation-alignment."); XCTAssertEqualObjects(layer.iconRotationAlignment, functionExpression, @"iconRotationAlignment should round-trip camera expressions."); - layer.iconRotationAlignment = nil; XCTAssertTrue(rawLayer->getIconRotationAlignment().isUndefined(), @@ -543,7 +533,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconSize(), propertyValue, @"Setting iconScale to a camera expression should update icon-size."); XCTAssertEqualObjects(layer.iconScale, functionExpression, @@ -574,7 +564,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconScale, pedanticFunctionExpression, @"iconScale should round-trip camera-data expressions."); - layer.iconScale = nil; XCTAssertTrue(rawLayer->getIconSize().isUndefined(), @@ -606,13 +595,12 @@ { 18, mbgl::style::IconTextFitType::Both }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::IconTextFitType> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconTextFit(), propertyValue, @"Setting iconTextFit to a camera expression should update icon-text-fit."); XCTAssertEqualObjects(layer.iconTextFit, functionExpression, @"iconTextFit should round-trip camera expressions."); - layer.iconTextFit = nil; XCTAssertTrue(rawLayer->getIconTextFit().isUndefined(), @@ -656,13 +644,12 @@ { 18, { 1, 1, 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 4>> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconTextFitPadding(), propertyValue, @"Setting iconTextFitPadding to a camera expression should update icon-text-fit-padding."); XCTAssertEqualObjects(layer.iconTextFitPadding, functionExpression, @"iconTextFitPadding should round-trip camera expressions."); - layer.iconTextFitPadding = nil; XCTAssertTrue(rawLayer->getIconTextFitPadding().isUndefined(), @@ -700,13 +687,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconKeepUpright(), propertyValue, @"Setting keepsIconUpright to a camera expression should update icon-keep-upright."); XCTAssertEqualObjects(layer.keepsIconUpright, functionExpression, @"keepsIconUpright should round-trip camera expressions."); - layer.keepsIconUpright = nil; XCTAssertTrue(rawLayer->getIconKeepUpright().isUndefined(), @@ -744,13 +730,12 @@ { 18, false }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextKeepUpright(), propertyValue, @"Setting keepsTextUpright to a camera expression should update text-keep-upright."); XCTAssertEqualObjects(layer.keepsTextUpright, functionExpression, @"keepsTextUpright should round-trip camera expressions."); - layer.keepsTextUpright = nil; XCTAssertTrue(rawLayer->getTextKeepUpright().isUndefined(), @@ -788,13 +773,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextMaxAngle(), propertyValue, @"Setting maximumTextAngle to a camera expression should update text-max-angle."); XCTAssertEqualObjects(layer.maximumTextAngle, functionExpression, @"maximumTextAngle should round-trip camera expressions."); - layer.maximumTextAngle = nil; XCTAssertTrue(rawLayer->getTextMaxAngle().isUndefined(), @@ -832,7 +816,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue, @"Setting maximumTextWidth to a camera expression should update text-max-width."); XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression, @@ -863,7 +847,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.maximumTextWidth, pedanticFunctionExpression, @"maximumTextWidth should round-trip camera-data expressions."); - layer.maximumTextWidth = nil; XCTAssertTrue(rawLayer->getTextMaxWidth().isUndefined(), @@ -895,13 +878,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getSymbolAvoidEdges(), propertyValue, @"Setting symbolAvoidsEdges to a camera expression should update symbol-avoid-edges."); XCTAssertEqualObjects(layer.symbolAvoidsEdges, functionExpression, @"symbolAvoidsEdges should round-trip camera expressions."); - layer.symbolAvoidsEdges = nil; XCTAssertTrue(rawLayer->getSymbolAvoidEdges().isUndefined(), @@ -939,13 +921,12 @@ { 18, mbgl::style::SymbolPlacementType::Line }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolPlacementType> { intervalStops }; - + XCTAssertEqual(rawLayer->getSymbolPlacement(), propertyValue, @"Setting symbolPlacement to a camera expression should update symbol-placement."); XCTAssertEqualObjects(layer.symbolPlacement, functionExpression, @"symbolPlacement should round-trip camera expressions."); - layer.symbolPlacement = nil; XCTAssertTrue(rawLayer->getSymbolPlacement().isUndefined(), @@ -983,13 +964,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getSymbolSpacing(), propertyValue, @"Setting symbolSpacing to a camera expression should update symbol-spacing."); XCTAssertEqualObjects(layer.symbolSpacing, functionExpression, @"symbolSpacing should round-trip camera expressions."); - layer.symbolSpacing = nil; XCTAssertTrue(rawLayer->getSymbolSpacing().isUndefined(), @@ -1027,13 +1007,12 @@ { 18, "Text Field" }, }}; propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextField(), propertyValue, @"Setting text to a camera expression should update text-field."); XCTAssertEqualObjects(layer.text, functionExpression, @"text should round-trip camera expressions."); - layer.text = nil; XCTAssertTrue(rawLayer->getTextField().isUndefined(), @@ -1065,13 +1044,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextAllowOverlap(), propertyValue, @"Setting textAllowsOverlap to a camera expression should update text-allow-overlap."); XCTAssertEqualObjects(layer.textAllowsOverlap, functionExpression, @"textAllowsOverlap should round-trip camera expressions."); - layer.textAllowsOverlap = nil; XCTAssertTrue(rawLayer->getTextAllowOverlap().isUndefined(), @@ -1109,13 +1087,12 @@ { 18, mbgl::style::SymbolAnchorType::BottomRight }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::SymbolAnchorType> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue, @"Setting textAnchor to a camera expression should update text-anchor."); XCTAssertEqualObjects(layer.textAnchor, functionExpression, @"textAnchor should round-trip camera expressions."); - layer.textAnchor = nil; XCTAssertTrue(rawLayer->getTextAnchor().isUndefined(), @@ -1147,13 +1124,12 @@ { 18, { "Text Font", "Tnof Txet" } }, }}; propertyValue = mbgl::style::CameraFunction<std::vector<std::string>> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextFont(), propertyValue, @"Setting textFontNames to a camera expression should update text-font."); XCTAssertEqualObjects(layer.textFontNames, functionExpression, @"textFontNames should round-trip camera expressions."); - layer.textFontNames = nil; XCTAssertTrue(rawLayer->getTextFont().isUndefined(), @@ -1185,7 +1161,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextSize(), propertyValue, @"Setting textFontSize to a camera expression should update text-size."); XCTAssertEqualObjects(layer.textFontSize, functionExpression, @@ -1216,7 +1192,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textFontSize, pedanticFunctionExpression, @"textFontSize should round-trip camera-data expressions."); - layer.textFontSize = nil; XCTAssertTrue(rawLayer->getTextSize().isUndefined(), @@ -1248,13 +1223,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextIgnorePlacement(), propertyValue, @"Setting textIgnoresPlacement to a camera expression should update text-ignore-placement."); XCTAssertEqualObjects(layer.textIgnoresPlacement, functionExpression, @"textIgnoresPlacement should round-trip camera expressions."); - layer.textIgnoresPlacement = nil; XCTAssertTrue(rawLayer->getTextIgnorePlacement().isUndefined(), @@ -1292,13 +1266,12 @@ { 18, mbgl::style::TextJustifyType::Right }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TextJustifyType> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextJustify(), propertyValue, @"Setting textJustification to a camera expression should update text-justify."); XCTAssertEqualObjects(layer.textJustification, functionExpression, @"textJustification should round-trip camera expressions."); - layer.textJustification = nil; XCTAssertTrue(rawLayer->getTextJustify().isUndefined(), @@ -1330,7 +1303,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue, @"Setting textLetterSpacing to a camera expression should update text-letter-spacing."); XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression, @@ -1361,7 +1334,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textLetterSpacing, pedanticFunctionExpression, @"textLetterSpacing should round-trip camera-data expressions."); - layer.textLetterSpacing = nil; XCTAssertTrue(rawLayer->getTextLetterSpacing().isUndefined(), @@ -1393,13 +1365,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextLineHeight(), propertyValue, @"Setting textLineHeight to a camera expression should update text-line-height."); XCTAssertEqualObjects(layer.textLineHeight, functionExpression, @"textLineHeight should round-trip camera expressions."); - layer.textLineHeight = nil; XCTAssertTrue(rawLayer->getTextLineHeight().isUndefined(), @@ -1443,7 +1414,7 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextOffset(), propertyValue, @"Setting textOffset to a camera expression should update text-offset."); XCTAssertEqualObjects(layer.textOffset, functionExpression, @@ -1474,7 +1445,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textOffset, pedanticFunctionExpression, @"textOffset should round-trip camera-data expressions."); - layer.textOffset = nil; XCTAssertTrue(rawLayer->getTextOffset().isUndefined(), @@ -1506,13 +1476,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction<bool> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextOptional(), propertyValue, @"Setting textOptional to a camera expression should update text-optional."); XCTAssertEqualObjects(layer.textOptional, functionExpression, @"textOptional should round-trip camera expressions."); - layer.textOptional = nil; XCTAssertTrue(rawLayer->getTextOptional().isUndefined(), @@ -1550,13 +1519,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextPadding(), propertyValue, @"Setting textPadding to a camera expression should update text-padding."); XCTAssertEqualObjects(layer.textPadding, functionExpression, @"textPadding should round-trip camera expressions."); - layer.textPadding = nil; XCTAssertTrue(rawLayer->getTextPadding().isUndefined(), @@ -1594,13 +1562,12 @@ { 18, mbgl::style::AlignmentType::Auto }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextPitchAlignment(), propertyValue, @"Setting textPitchAlignment to a camera expression should update text-pitch-alignment."); XCTAssertEqualObjects(layer.textPitchAlignment, functionExpression, @"textPitchAlignment should round-trip camera expressions."); - layer.textPitchAlignment = nil; XCTAssertTrue(rawLayer->getTextPitchAlignment().isUndefined(), @@ -1638,7 +1605,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextRotate(), propertyValue, @"Setting textRotation to a camera expression should update text-rotate."); XCTAssertEqualObjects(layer.textRotation, functionExpression, @@ -1669,7 +1636,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textRotation, pedanticFunctionExpression, @"textRotation should round-trip camera-data expressions."); - layer.textRotation = nil; XCTAssertTrue(rawLayer->getTextRotate().isUndefined(), @@ -1701,13 +1667,12 @@ { 18, mbgl::style::AlignmentType::Auto }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextRotationAlignment(), propertyValue, @"Setting textRotationAlignment to a camera expression should update text-rotation-alignment."); XCTAssertEqualObjects(layer.textRotationAlignment, functionExpression, @"textRotationAlignment should round-trip camera expressions."); - layer.textRotationAlignment = nil; XCTAssertTrue(rawLayer->getTextRotationAlignment().isUndefined(), @@ -1745,13 +1710,12 @@ { 18, mbgl::style::TextTransformType::Lowercase }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TextTransformType> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextTransform(), propertyValue, @"Setting textTransform to a camera expression should update text-transform."); XCTAssertEqualObjects(layer.textTransform, functionExpression, @"textTransform should round-trip camera expressions."); - layer.textTransform = nil; XCTAssertTrue(rawLayer->getTextTransform().isUndefined(), @@ -1783,7 +1747,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconColor(), propertyValue, @"Setting iconColor to a camera expression should update icon-color."); XCTAssertEqualObjects(layer.iconColor, functionExpression, @@ -1814,7 +1778,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconColor, pedanticFunctionExpression, @"iconColor should round-trip camera-data expressions."); - layer.iconColor = nil; XCTAssertTrue(rawLayer->getIconColor().isUndefined(), @@ -1855,7 +1818,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, @"Setting iconHaloBlur to a camera expression should update icon-halo-blur."); XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression, @@ -1886,7 +1849,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconHaloBlur, pedanticFunctionExpression, @"iconHaloBlur should round-trip camera-data expressions."); - layer.iconHaloBlur = nil; XCTAssertTrue(rawLayer->getIconHaloBlur().isUndefined(), @@ -1927,7 +1889,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, @"Setting iconHaloColor to a camera expression should update icon-halo-color."); XCTAssertEqualObjects(layer.iconHaloColor, functionExpression, @@ -1958,7 +1920,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconHaloColor, pedanticFunctionExpression, @"iconHaloColor should round-trip camera-data expressions."); - layer.iconHaloColor = nil; XCTAssertTrue(rawLayer->getIconHaloColor().isUndefined(), @@ -1999,7 +1960,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, @"Setting iconHaloWidth to a camera expression should update icon-halo-width."); XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression, @@ -2030,7 +1991,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconHaloWidth, pedanticFunctionExpression, @"iconHaloWidth should round-trip camera-data expressions."); - layer.iconHaloWidth = nil; XCTAssertTrue(rawLayer->getIconHaloWidth().isUndefined(), @@ -2071,7 +2031,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, @"Setting iconOpacity to a camera expression should update icon-opacity."); XCTAssertEqualObjects(layer.iconOpacity, functionExpression, @@ -2102,7 +2062,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconOpacity, pedanticFunctionExpression, @"iconOpacity should round-trip camera-data expressions."); - layer.iconOpacity = nil; XCTAssertTrue(rawLayer->getIconOpacity().isUndefined(), @@ -2149,13 +2108,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconTranslate(), propertyValue, @"Setting iconTranslation to a camera expression should update icon-translate."); XCTAssertEqualObjects(layer.iconTranslation, functionExpression, @"iconTranslation should round-trip camera expressions."); - layer.iconTranslation = nil; XCTAssertTrue(rawLayer->getIconTranslate().isUndefined(), @@ -2193,13 +2151,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconTranslateAnchor(), propertyValue, @"Setting iconTranslationAnchor to a camera expression should update icon-translate-anchor."); XCTAssertEqualObjects(layer.iconTranslationAnchor, functionExpression, @"iconTranslationAnchor should round-trip camera expressions."); - layer.iconTranslationAnchor = nil; XCTAssertTrue(rawLayer->getIconTranslateAnchor().isUndefined(), @@ -2237,7 +2194,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextColor(), propertyValue, @"Setting textColor to a camera expression should update text-color."); XCTAssertEqualObjects(layer.textColor, functionExpression, @@ -2268,7 +2225,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textColor, pedanticFunctionExpression, @"textColor should round-trip camera-data expressions."); - layer.textColor = nil; XCTAssertTrue(rawLayer->getTextColor().isUndefined(), @@ -2309,7 +2265,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, @"Setting textHaloBlur to a camera expression should update text-halo-blur."); XCTAssertEqualObjects(layer.textHaloBlur, functionExpression, @@ -2340,7 +2296,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textHaloBlur, pedanticFunctionExpression, @"textHaloBlur should round-trip camera-data expressions."); - layer.textHaloBlur = nil; XCTAssertTrue(rawLayer->getTextHaloBlur().isUndefined(), @@ -2381,7 +2336,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, @"Setting textHaloColor to a camera expression should update text-halo-color."); XCTAssertEqualObjects(layer.textHaloColor, functionExpression, @@ -2412,7 +2367,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textHaloColor, pedanticFunctionExpression, @"textHaloColor should round-trip camera-data expressions."); - layer.textHaloColor = nil; XCTAssertTrue(rawLayer->getTextHaloColor().isUndefined(), @@ -2453,7 +2407,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, @"Setting textHaloWidth to a camera expression should update text-halo-width."); XCTAssertEqualObjects(layer.textHaloWidth, functionExpression, @@ -2484,7 +2438,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textHaloWidth, pedanticFunctionExpression, @"textHaloWidth should round-trip camera-data expressions."); - layer.textHaloWidth = nil; XCTAssertTrue(rawLayer->getTextHaloWidth().isUndefined(), @@ -2525,7 +2478,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction<float> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, @"Setting textOpacity to a camera expression should update text-opacity."); XCTAssertEqualObjects(layer.textOpacity, functionExpression, @@ -2556,7 +2509,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textOpacity, pedanticFunctionExpression, @"textOpacity should round-trip camera-data expressions."); - layer.textOpacity = nil; XCTAssertTrue(rawLayer->getTextOpacity().isUndefined(), @@ -2603,13 +2555,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextTranslate(), propertyValue, @"Setting textTranslation to a camera expression should update text-translate."); XCTAssertEqualObjects(layer.textTranslation, functionExpression, @"textTranslation should round-trip camera expressions."); - layer.textTranslation = nil; XCTAssertTrue(rawLayer->getTextTranslate().isUndefined(), @@ -2647,13 +2598,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextTranslateAnchor(), propertyValue, @"Setting textTranslationAnchor to a camera expression should update text-translate-anchor."); XCTAssertEqualObjects(layer.textTranslationAnchor, functionExpression, @"textTranslationAnchor should round-trip camera expressions."); - layer.textTranslationAnchor = nil; XCTAssertTrue(rawLayer->getTextTranslateAnchor().isUndefined(), diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp index 7670790be9..598a0b182b 100644 --- a/platform/default/mbgl/storage/offline.cpp +++ b/platform/default/mbgl/storage/offline.cpp @@ -43,7 +43,7 @@ uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, u const Range<uint8_t> clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); unsigned long result = 0;; for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { - result += util::tileCount(bounds, z, tileSize); + result += util::tileCount(bounds, z); } return result; diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index 65c2097182..2b872ab87b 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -10,11 +10,6 @@ namespace mbgl { -OfflineDatabase::Statement::~Statement() { - stmt.reset(); - stmt.clearBindings(); -} - OfflineDatabase::OfflineDatabase(std::string path_, uint64_t maximumCacheSize_) : path(std::move(path_)), maximumCacheSize(maximumCacheSize_) { @@ -28,48 +23,62 @@ OfflineDatabase::~OfflineDatabase() { statements.clear(); db.reset(); } catch (mapbox::sqlite::Exception& ex) { - Log::Error(Event::Database, ex.code, ex.what()); + Log::Error(Event::Database, (int)ex.code, ex.what()); } } -void OfflineDatabase::connect(int flags) { - db = std::make_unique<mapbox::sqlite::Database>(path.c_str(), flags); - db->setBusyTimeout(Milliseconds::max()); - db->exec("PRAGMA foreign_keys = ON"); -} - void OfflineDatabase::ensureSchema() { if (path != ":memory:") { - try { - connect(mapbox::sqlite::ReadWrite); - - switch (userVersion()) { - case 0: break; // cache-only database; ok to delete - case 1: break; // cache-only database; ok to delete - case 2: migrateToVersion3(); // fall through - case 3: // no-op and fall through - case 4: migrateToVersion5(); // fall through - case 5: migrateToVersion6(); // fall through - case 6: return; - default: break; // downgrade, delete the database - } - - removeExisting(); - connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); - } catch (mapbox::sqlite::Exception& ex) { - if (ex.code != mapbox::sqlite::Exception::Code::CANTOPEN && ex.code != mapbox::sqlite::Exception::Code::NOTADB) { + auto result = mapbox::sqlite::Database::tryOpen(path, mapbox::sqlite::ReadWrite); + 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(); + } else if (ex.code == mapbox::sqlite::ResultCode::CantOpen) { + // Doesn't exist yet. + } else { Log::Error(Event::Database, "Unexpected error connecting to database: %s", ex.what()); - throw; + throw ex; } - + } else { try { - if (ex.code == mapbox::sqlite::Exception::Code::NOTADB) { + 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: + // cache-only database; ok to delete + removeExisting(); + 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) { removeExisting(); + } else { + throw; } - connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); - } catch (...) { - Log::Error(Event::Database, "Unexpected error creating database: %s", util::toString(std::current_exception()).c_str()); - throw; } } } @@ -77,9 +86,9 @@ void OfflineDatabase::ensureSchema() { try { #include "offline_schema.cpp.include" - connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); - - // If you change the schema you must write a migration from the previous version. + db = std::make_unique<mapbox::sqlite::Database>(mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadWrite | mapbox::sqlite::Create)); + db->setBusyTimeout(Milliseconds::max()); + db->exec("PRAGMA foreign_keys = ON"); db->exec("PRAGMA auto_vacuum = INCREMENTAL"); db->exec("PRAGMA journal_mode = DELETE"); db->exec("PRAGMA synchronous = FULL"); @@ -92,14 +101,13 @@ void OfflineDatabase::ensureSchema() { } int OfflineDatabase::userVersion() { - auto stmt = db->prepare("PRAGMA user_version"); - stmt.run(); - return stmt.get<int>(0); + return static_cast<int>(getPragma<int64_t>("PRAGMA user_version")); } void OfflineDatabase::removeExisting() { Log::Warning(Event::Database, "Removing existing incompatible offline database"); + statements.clear(); db.reset(); try { @@ -135,14 +143,12 @@ void OfflineDatabase::migrateToVersion6() { transaction.commit(); } -OfflineDatabase::Statement OfflineDatabase::getStatement(const char * sql) { +mapbox::sqlite::Statement& OfflineDatabase::getStatement(const char* sql) { auto it = statements.find(sql); - - if (it != statements.end()) { - return Statement(*it->second); + if (it == statements.end()) { + it = statements.emplace(sql, std::make_unique<mapbox::sqlite::Statement>(*db, sql)).first; } - - return Statement(*statements.emplace(sql, std::make_unique<mapbox::sqlite::Statement>(db->prepare(sql))).first->second); + return *it->second; } optional<Response> OfflineDatabase::get(const Resource& resource) { @@ -209,41 +215,40 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource, } optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resource& resource) { - // clang-format off - Statement accessedStmt = getStatement( - "UPDATE resources SET accessed = ?1 WHERE url = ?2"); - // clang-format on - - accessedStmt->bind(1, util::now()); - accessedStmt->bind(2, resource.url); - accessedStmt->run(); + // Update accessed timestamp used for LRU eviction. + { + 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(); + } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( // 0 1 2 3 4 5 "SELECT etag, expires, must_revalidate, modified, data, compressed " "FROM resources " - "WHERE url = ?"); + "WHERE url = ?") }; // clang-format on - stmt->bind(1, resource.url); + query.bind(1, resource.url); - if (!stmt->run()) { + if (!query.run()) { return {}; } Response response; uint64_t size = 0; - response.etag = stmt->get<optional<std::string>>(0); - response.expires = stmt->get<optional<Timestamp>>(1); - response.mustRevalidate = stmt->get<bool>(2); - response.modified = stmt->get<optional<Timestamp>>(3); + response.etag = query.get<optional<std::string>>(0); + response.expires = query.get<optional<Timestamp>>(1); + response.mustRevalidate = query.get<bool>(2); + response.modified = query.get<optional<Timestamp>>(3); - optional<std::string> data = stmt->get<optional<std::string>>(4); + auto data = query.get<optional<std::string>>(4); if (!data) { response.noContent = true; - } else if (stmt->get<bool>(5)) { + } else if (query.get<bool>(5)) { response.data = std::make_shared<std::string>(util::decompress(*data)); size = data->length(); } else { @@ -255,16 +260,13 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou } optional<int64_t> OfflineDatabase::hasResource(const Resource& resource) { - // clang-format off - Statement stmt = getStatement("SELECT length(data) FROM resources WHERE url = ?"); - // clang-format on - - stmt->bind(1, resource.url); - if (!stmt->run()) { + mapbox::sqlite::Query query{ getStatement("SELECT length(data) FROM resources WHERE url = ?") }; + query.bind(1, resource.url); + if (!query.run()) { return {}; } - return stmt->get<optional<int64_t>>(0); + return query.get<optional<int64_t>>(0); } bool OfflineDatabase::putResource(const Resource& resource, @@ -273,19 +275,19 @@ bool OfflineDatabase::putResource(const Resource& resource, bool compressed) { if (response.notModified) { // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query notModifiedQuery{ getStatement( "UPDATE resources " "SET accessed = ?1, " " expires = ?2, " " must_revalidate = ?3 " - "WHERE url = ?4 "); + "WHERE url = ?4 ") }; // clang-format on - update->bind(1, util::now()); - update->bind(2, response.expires); - update->bind(3, response.mustRevalidate); - update->bind(4, resource.url); - update->run(); + notModifiedQuery.bind(1, util::now()); + notModifiedQuery.bind(2, response.expires); + notModifiedQuery.bind(3, response.mustRevalidate); + notModifiedQuery.bind(4, resource.url); + notModifiedQuery.run(); return false; } @@ -296,7 +298,7 @@ bool OfflineDatabase::putResource(const Resource& resource, mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate); // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query updateQuery{ getStatement( "UPDATE resources " "SET kind = ?1, " " etag = ?2, " @@ -306,81 +308,83 @@ bool OfflineDatabase::putResource(const Resource& resource, " accessed = ?6, " " data = ?7, " " compressed = ?8 " - "WHERE url = ?9 "); + "WHERE url = ?9 ") }; // clang-format on - update->bind(1, int(resource.kind)); - update->bind(2, response.etag); - update->bind(3, response.expires); - update->bind(4, response.mustRevalidate); - update->bind(5, response.modified); - update->bind(6, util::now()); - update->bind(9, resource.url); + updateQuery.bind(1, int(resource.kind)); + updateQuery.bind(2, response.etag); + updateQuery.bind(3, response.expires); + updateQuery.bind(4, response.mustRevalidate); + updateQuery.bind(5, response.modified); + updateQuery.bind(6, util::now()); + updateQuery.bind(9, resource.url); if (response.noContent) { - update->bind(7, nullptr); - update->bind(8, false); + updateQuery.bind(7, nullptr); + updateQuery.bind(8, false); } else { - update->bindBlob(7, data.data(), data.size(), false); - update->bind(8, compressed); + updateQuery.bindBlob(7, data.data(), data.size(), false); + updateQuery.bind(8, compressed); } - update->run(); - if (update->changes() != 0) { + updateQuery.run(); + if (updateQuery.changes() != 0) { transaction.commit(); return false; } // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT INTO resources (url, kind, etag, expires, must_revalidate, modified, accessed, data, compressed) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) "); + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) ") }; // clang-format on - insert->bind(1, resource.url); - insert->bind(2, int(resource.kind)); - insert->bind(3, response.etag); - insert->bind(4, response.expires); - insert->bind(5, response.mustRevalidate); - insert->bind(6, response.modified); - insert->bind(7, util::now()); + insertQuery.bind(1, resource.url); + insertQuery.bind(2, int(resource.kind)); + insertQuery.bind(3, response.etag); + insertQuery.bind(4, response.expires); + insertQuery.bind(5, response.mustRevalidate); + insertQuery.bind(6, response.modified); + insertQuery.bind(7, util::now()); if (response.noContent) { - insert->bind(8, nullptr); - insert->bind(9, false); + insertQuery.bind(8, nullptr); + insertQuery.bind(9, false); } else { - insert->bindBlob(8, data.data(), data.size(), false); - insert->bind(9, compressed); + insertQuery.bindBlob(8, data.data(), data.size(), false); + insertQuery.bind(9, compressed); } - insert->run(); + insertQuery.run(); transaction.commit(); return true; } optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource::TileData& tile) { - // clang-format off - Statement accessedStmt = getStatement( - "UPDATE tiles " - "SET accessed = ?1 " - "WHERE url_template = ?2 " - " AND pixel_ratio = ?3 " - " AND x = ?4 " - " AND y = ?5 " - " AND z = ?6 "); - // clang-format on + { + // clang-format off + mapbox::sqlite::Query accessedQuery{ getStatement( + "UPDATE tiles " + "SET accessed = ?1 " + "WHERE url_template = ?2 " + " AND pixel_ratio = ?3 " + " AND x = ?4 " + " AND y = ?5 " + " AND z = ?6 ") }; + // clang-format on - accessedStmt->bind(1, util::now()); - accessedStmt->bind(2, tile.urlTemplate); - accessedStmt->bind(3, tile.pixelRatio); - accessedStmt->bind(4, tile.x); - accessedStmt->bind(5, tile.y); - accessedStmt->bind(6, tile.z); - accessedStmt->run(); + accessedQuery.bind(1, util::now()); + accessedQuery.bind(2, tile.urlTemplate); + accessedQuery.bind(3, tile.pixelRatio); + accessedQuery.bind(4, tile.x); + accessedQuery.bind(5, tile.y); + accessedQuery.bind(6, tile.z); + accessedQuery.run(); + } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( // 0 1 2, 3, 4, 5 "SELECT etag, expires, must_revalidate, modified, data, compressed " "FROM tiles " @@ -388,31 +392,31 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource: " AND pixel_ratio = ?2 " " AND x = ?3 " " AND y = ?4 " - " AND z = ?5 "); + " AND z = ?5 ") }; // clang-format on - stmt->bind(1, tile.urlTemplate); - stmt->bind(2, tile.pixelRatio); - stmt->bind(3, tile.x); - stmt->bind(4, tile.y); - stmt->bind(5, tile.z); + query.bind(1, tile.urlTemplate); + query.bind(2, tile.pixelRatio); + query.bind(3, tile.x); + query.bind(4, tile.y); + query.bind(5, tile.z); - if (!stmt->run()) { + if (!query.run()) { return {}; } Response response; uint64_t size = 0; - response.etag = stmt->get<optional<std::string>>(0); - response.expires = stmt->get<optional<Timestamp>>(1); - response.mustRevalidate = stmt->get<bool>(2); - response.modified = stmt->get<optional<Timestamp>>(3); + response.etag = query.get<optional<std::string>>(0); + response.expires = query.get<optional<Timestamp>>(1); + response.mustRevalidate = query.get<bool>(2); + response.modified = query.get<optional<Timestamp>>(3); - optional<std::string> data = stmt->get<optional<std::string>>(4); + optional<std::string> data = query.get<optional<std::string>>(4); if (!data) { response.noContent = true; - } else if (stmt->get<bool>(5)) { + } else if (query.get<bool>(5)) { response.data = std::make_shared<std::string>(util::decompress(*data)); size = data->length(); } else { @@ -425,27 +429,27 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource: optional<int64_t> OfflineDatabase::hasTile(const Resource::TileData& tile) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query size{ getStatement( "SELECT length(data) " "FROM tiles " "WHERE url_template = ?1 " " AND pixel_ratio = ?2 " " AND x = ?3 " " AND y = ?4 " - " AND z = ?5 "); + " AND z = ?5 ") }; // clang-format on - stmt->bind(1, tile.urlTemplate); - stmt->bind(2, tile.pixelRatio); - stmt->bind(3, tile.x); - stmt->bind(4, tile.y); - stmt->bind(5, tile.z); + size.bind(1, tile.urlTemplate); + size.bind(2, tile.pixelRatio); + size.bind(3, tile.x); + size.bind(4, tile.y); + size.bind(5, tile.z); - if (!stmt->run()) { + if (!size.run()) { return {}; } - return stmt->get<optional<int64_t>>(0); + return size.get<optional<int64_t>>(0); } bool OfflineDatabase::putTile(const Resource::TileData& tile, @@ -454,7 +458,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, bool compressed) { if (response.notModified) { // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query notModifiedQuery{ getStatement( "UPDATE tiles " "SET accessed = ?1, " " expires = ?2, " @@ -463,18 +467,18 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, " AND pixel_ratio = ?5 " " AND x = ?6 " " AND y = ?7 " - " AND z = ?8 "); + " AND z = ?8 ") }; // clang-format on - update->bind(1, util::now()); - update->bind(2, response.expires); - update->bind(3, response.mustRevalidate); - update->bind(4, tile.urlTemplate); - update->bind(5, tile.pixelRatio); - update->bind(6, tile.x); - update->bind(7, tile.y); - update->bind(8, tile.z); - update->run(); + notModifiedQuery.bind(1, util::now()); + notModifiedQuery.bind(2, response.expires); + notModifiedQuery.bind(3, response.mustRevalidate); + notModifiedQuery.bind(4, tile.urlTemplate); + notModifiedQuery.bind(5, tile.pixelRatio); + notModifiedQuery.bind(6, tile.x); + notModifiedQuery.bind(7, tile.y); + notModifiedQuery.bind(8, tile.z); + notModifiedQuery.run(); return false; } @@ -485,7 +489,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate); // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query updateQuery{ getStatement( "UPDATE tiles " "SET modified = ?1, " " etag = ?2, " @@ -498,78 +502,75 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, " AND pixel_ratio = ?9 " " AND x = ?10 " " AND y = ?11 " - " AND z = ?12 "); + " AND z = ?12 ") }; // clang-format on - update->bind(1, response.modified); - update->bind(2, response.etag); - update->bind(3, response.expires); - update->bind(4, response.mustRevalidate); - update->bind(5, util::now()); - update->bind(8, tile.urlTemplate); - update->bind(9, tile.pixelRatio); - update->bind(10, tile.x); - update->bind(11, tile.y); - update->bind(12, tile.z); + updateQuery.bind(1, response.modified); + updateQuery.bind(2, response.etag); + updateQuery.bind(3, response.expires); + updateQuery.bind(4, response.mustRevalidate); + updateQuery.bind(5, util::now()); + updateQuery.bind(8, tile.urlTemplate); + updateQuery.bind(9, tile.pixelRatio); + updateQuery.bind(10, tile.x); + updateQuery.bind(11, tile.y); + updateQuery.bind(12, tile.z); if (response.noContent) { - update->bind(6, nullptr); - update->bind(7, false); + updateQuery.bind(6, nullptr); + updateQuery.bind(7, false); } else { - update->bindBlob(6, data.data(), data.size(), false); - update->bind(7, compressed); + updateQuery.bindBlob(6, data.data(), data.size(), false); + updateQuery.bind(7, compressed); } - update->run(); - if (update->changes() != 0) { + updateQuery.run(); + if (updateQuery.changes() != 0) { transaction.commit(); return false; } // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, must_revalidate, etag, expires, accessed, data, compressed) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)"); + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)") }; // clang-format on - insert->bind(1, tile.urlTemplate); - insert->bind(2, tile.pixelRatio); - insert->bind(3, tile.x); - insert->bind(4, tile.y); - insert->bind(5, tile.z); - insert->bind(6, response.modified); - insert->bind(7, response.mustRevalidate); - insert->bind(8, response.etag); - insert->bind(9, response.expires); - insert->bind(10, util::now()); + insertQuery.bind(1, tile.urlTemplate); + insertQuery.bind(2, tile.pixelRatio); + insertQuery.bind(3, tile.x); + insertQuery.bind(4, tile.y); + insertQuery.bind(5, tile.z); + insertQuery.bind(6, response.modified); + insertQuery.bind(7, response.mustRevalidate); + insertQuery.bind(8, response.etag); + insertQuery.bind(9, response.expires); + insertQuery.bind(10, util::now()); if (response.noContent) { - insert->bind(11, nullptr); - insert->bind(12, false); + insertQuery.bind(11, nullptr); + insertQuery.bind(12, false); } else { - insert->bindBlob(11, data.data(), data.size(), false); - insert->bind(12, compressed); + insertQuery.bindBlob(11, data.data(), data.size(), false); + insertQuery.bind(12, compressed); } - insert->run(); + insertQuery.run(); transaction.commit(); return true; } std::vector<OfflineRegion> OfflineDatabase::listRegions() { - // clang-format off - Statement stmt = getStatement( - "SELECT id, definition, description FROM regions"); - // clang-format on + mapbox::sqlite::Query query{ getStatement("SELECT id, definition, description FROM regions") }; std::vector<OfflineRegion> result; - while (stmt->run()) { + while (query.run()) { result.push_back(OfflineRegion( - stmt->get<int64_t>(0), - decodeOfflineRegionDefinition(stmt->get<std::string>(1)), - stmt->get<std::vector<uint8_t>>(2))); + query.get<int64_t>(0), + decodeOfflineRegionDefinition(query.get<std::string>(1)), + query.get<std::vector<uint8_t>>(2))); } return result; @@ -578,39 +579,37 @@ std::vector<OfflineRegion> OfflineDatabase::listRegions() { OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "INSERT INTO regions (definition, description) " - "VALUES (?1, ?2) "); + "VALUES (?1, ?2) ") }; // clang-format on - stmt->bind(1, encodeOfflineRegionDefinition(definition)); - stmt->bindBlob(2, metadata); - stmt->run(); + query.bind(1, encodeOfflineRegionDefinition(definition)); + query.bindBlob(2, metadata); + query.run(); - return OfflineRegion(stmt->lastInsertRowId(), definition, metadata); + return OfflineRegion(query.lastInsertRowId(), definition, metadata); } OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) { // clang-format off - Statement stmt = getStatement( - "UPDATE regions SET description = ?1" - "WHERE id = ?2"); + mapbox::sqlite::Query query{ getStatement( + "UPDATE regions SET description = ?1 " + "WHERE id = ?2") }; // clang-format on - stmt->bindBlob(1, metadata); - stmt->bind(2, regionID); - stmt->run(); + query.bindBlob(1, metadata); + query.bind(2, regionID); + query.run(); return metadata; } void OfflineDatabase::deleteRegion(OfflineRegion&& region) { - // clang-format off - Statement stmt = getStatement( - "DELETE FROM regions WHERE id = ?"); - // clang-format on - - stmt->bind(1, region.getID()); - stmt->run(); + { + mapbox::sqlite::Query query{ getStatement("DELETE FROM regions WHERE id = ?") }; + query.bind(1, region.getID()); + query.run(); + } evict(0); db->exec("PRAGMA incremental_vacuum"); @@ -656,7 +655,7 @@ uint64_t OfflineDatabase::putRegionResource(int64_t regionID, const Resource& re bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { if (resource.kind == Resource::Kind::Tile) { // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT OR IGNORE INTO region_tiles (region_id, tile_id) " "SELECT ?1, tiles.id " "FROM tiles " @@ -664,24 +663,24 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { " AND pixel_ratio = ?3 " " AND x = ?4 " " AND y = ?5 " - " AND z = ?6 "); + " AND z = ?6 ") }; // clang-format on const Resource::TileData& tile = *resource.tileData; - insert->bind(1, regionID); - insert->bind(2, tile.urlTemplate); - insert->bind(3, tile.pixelRatio); - insert->bind(4, tile.x); - insert->bind(5, tile.y); - insert->bind(6, tile.z); - insert->run(); - - if (insert->changes() == 0) { + insertQuery.bind(1, regionID); + insertQuery.bind(2, tile.urlTemplate); + insertQuery.bind(3, tile.pixelRatio); + insertQuery.bind(4, tile.x); + insertQuery.bind(5, tile.y); + insertQuery.bind(6, tile.z); + insertQuery.run(); + + if (insertQuery.changes() == 0) { return false; } // clang-format off - Statement select = getStatement( + mapbox::sqlite::Query selectQuery{ getStatement( "SELECT region_id " "FROM region_tiles, tiles " "WHERE region_id != ?1 " @@ -690,58 +689,54 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { " AND x = ?4 " " AND y = ?5 " " AND z = ?6 " - "LIMIT 1 "); + "LIMIT 1 ") }; // clang-format on - select->bind(1, regionID); - select->bind(2, tile.urlTemplate); - select->bind(3, tile.pixelRatio); - select->bind(4, tile.x); - select->bind(5, tile.y); - select->bind(6, tile.z); - return !select->run(); + selectQuery.bind(1, regionID); + selectQuery.bind(2, tile.urlTemplate); + selectQuery.bind(3, tile.pixelRatio); + selectQuery.bind(4, tile.x); + selectQuery.bind(5, tile.y); + selectQuery.bind(6, tile.z); + return !selectQuery.run(); } else { // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT OR IGNORE INTO region_resources (region_id, resource_id) " "SELECT ?1, resources.id " "FROM resources " - "WHERE resources.url = ?2 "); + "WHERE resources.url = ?2 ") }; // clang-format on - insert->bind(1, regionID); - insert->bind(2, resource.url); - insert->run(); + insertQuery.bind(1, regionID); + insertQuery.bind(2, resource.url); + insertQuery.run(); - if (insert->changes() == 0) { + if (insertQuery.changes() == 0) { return false; } // clang-format off - Statement select = getStatement( + mapbox::sqlite::Query selectQuery{ getStatement( "SELECT region_id " "FROM region_resources, resources " "WHERE region_id != ?1 " " AND resources.url = ?2 " - "LIMIT 1 "); + "LIMIT 1 ") }; // clang-format on - select->bind(1, regionID); - select->bind(2, resource.url); - return !select->run(); + selectQuery.bind(1, regionID); + selectQuery.bind(2, resource.url); + return !selectQuery.run(); } } OfflineRegionDefinition OfflineDatabase::getRegionDefinition(int64_t regionID) { - // clang-format off - Statement stmt = getStatement( - "SELECT definition FROM regions WHERE id = ?1"); - // clang-format on - - stmt->bind(1, regionID); - stmt->run(); + mapbox::sqlite::Query query{ getStatement("SELECT definition FROM regions WHERE id = ?1") }; + query.bind(1, regionID); + query.run(); - return decodeOfflineRegionDefinition(stmt->get<std::string>(0)); + return decodeOfflineRegionDefinition(query.get<std::string>(0)); } OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) { @@ -760,35 +755,35 @@ OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) std::pair<int64_t, int64_t> OfflineDatabase::getCompletedResourceCountAndSize(int64_t regionID) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(*), SUM(LENGTH(data)) " "FROM region_resources, resources " "WHERE region_id = ?1 " - "AND resource_id = resources.id "); + "AND resource_id = resources.id ") }; // clang-format on - stmt->bind(1, regionID); - stmt->run(); - return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) }; + query.bind(1, regionID); + query.run(); + return { query.get<int64_t>(0), query.get<int64_t>(1) }; } std::pair<int64_t, int64_t> OfflineDatabase::getCompletedTileCountAndSize(int64_t regionID) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(*), SUM(LENGTH(data)) " "FROM region_tiles, tiles " "WHERE region_id = ?1 " - "AND tile_id = tiles.id "); + "AND tile_id = tiles.id ") }; // clang-format on - stmt->bind(1, regionID); - stmt->run(); - return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) }; + query.bind(1, regionID); + query.run(); + return { query.get<int64_t>(0), query.get<int64_t>(1) }; } template <class T> -T OfflineDatabase::getPragma(const char * sql) { - Statement stmt = getStatement(sql); - stmt->run(); - return stmt->get<T>(0); +T OfflineDatabase::getPragma(const char* sql) { + mapbox::sqlite::Query query{ getStatement(sql) }; + query.run(); + return query.get<T>(0); } // Remove least-recently used resources and tiles until the used database size, @@ -813,7 +808,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { // size, and because pages can get fragmented on the database. while (usedSize() + neededFreeSize + pageSize > maximumCacheSize) { // clang-format off - Statement accessedStmt = getStatement( + mapbox::sqlite::Query accessedQuery{ getStatement( "SELECT max(accessed) " "FROM ( " " SELECT accessed " @@ -829,16 +824,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " WHERE tile_id IS NULL " " ORDER BY accessed ASC LIMIT ?1 " ") " - ); - accessedStmt->bind(1, 50); + ) }; + accessedQuery.bind(1, 50); // clang-format on - if (!accessedStmt->run()) { + if (!accessedQuery.run()) { return false; } - Timestamp accessed = accessedStmt->get<Timestamp>(0); + Timestamp accessed = accessedQuery.get<Timestamp>(0); // clang-format off - Statement stmt1 = getStatement( + mapbox::sqlite::Query resourceQuery{ getStatement( "DELETE FROM resources " "WHERE id IN ( " " SELECT id FROM resources " @@ -846,14 +841,14 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " ON resource_id = resources.id " " WHERE resource_id IS NULL " " AND accessed <= ?1 " - ") "); + ") ") }; // clang-format on - stmt1->bind(1, accessed); - stmt1->run(); - uint64_t changes1 = stmt1->changes(); + resourceQuery.bind(1, accessed); + resourceQuery.run(); + const uint64_t resourceChanges = resourceQuery.changes(); // clang-format off - Statement stmt2 = getStatement( + mapbox::sqlite::Query tileQuery{ getStatement( "DELETE FROM tiles " "WHERE id IN ( " " SELECT id FROM tiles " @@ -861,16 +856,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " ON tile_id = tiles.id " " WHERE tile_id IS NULL " " AND accessed <= ?1 " - ") "); + ") ") }; // clang-format on - stmt2->bind(1, accessed); - stmt2->run(); - uint64_t changes2 = stmt2->changes(); + tileQuery.bind(1, accessed); + tileQuery.run(); + const uint64_t tileChanges = tileQuery.changes(); // The cached value of offlineTileCount does not need to be updated // here because only non-offline tiles can be removed by eviction. - if (changes1 == 0 && changes2 == 0) { + if (resourceChanges == 0 && tileChanges == 0) { return false; } } @@ -901,16 +896,16 @@ uint64_t OfflineDatabase::getOfflineMapboxTileCount() { } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(DISTINCT id) " "FROM region_tiles, tiles " "WHERE tile_id = tiles.id " - "AND url_template LIKE 'mapbox://%' "); + "AND url_template LIKE 'mapbox://%' ") }; // clang-format on - stmt->run(); + query.run(); - offlineMapboxTileCount = stmt->get<int64_t>(0); + offlineMapboxTileCount = query.get<int64_t>(0); return *offlineMapboxTileCount; } diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp index 91b544a9e0..e0d90a9a15 100644 --- a/platform/default/mbgl/storage/offline_database.hpp +++ b/platform/default/mbgl/storage/offline_database.hpp @@ -15,6 +15,7 @@ namespace mapbox { namespace sqlite { class Database; class Statement; +class Query; } // namespace sqlite } // namespace mapbox @@ -58,7 +59,6 @@ public: uint64_t getOfflineMapboxTileCount(); private: - void connect(int flags); int userVersion(); void ensureSchema(); void removeExisting(); @@ -66,20 +66,7 @@ private: void migrateToVersion5(); void migrateToVersion6(); - class Statement { - public: - explicit Statement(mapbox::sqlite::Statement& stmt_) : stmt(stmt_) {} - Statement(Statement&&) = default; - Statement(const Statement&) = delete; - ~Statement(); - - mapbox::sqlite::Statement* operator->() { return &stmt; }; - - private: - mapbox::sqlite::Statement& stmt; - }; - - Statement getStatement(const char *); + mapbox::sqlite::Statement& getStatement(const char *); optional<std::pair<Response, uint64_t>> getTile(const Resource::TileData&); optional<int64_t> hasTile(const Resource::TileData&); @@ -102,8 +89,8 @@ private: std::pair<int64_t, int64_t> getCompletedTileCountAndSize(int64_t regionID); const std::string path; - std::unique_ptr<::mapbox::sqlite::Database> db; - std::unordered_map<const char *, std::unique_ptr<::mapbox::sqlite::Statement>> statements; + std::unique_ptr<mapbox::sqlite::Database> db; + std::unordered_map<const char *, const std::unique_ptr<mapbox::sqlite::Statement>> statements; template <class T> T getPragma(const char *); diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 2e08354fdf..776adc8f3a 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -14,27 +14,20 @@ namespace sqlite { class DatabaseImpl { public: - DatabaseImpl(const char* filename, int flags) + DatabaseImpl(sqlite3* db_) + : db(db_) { - const int error = sqlite3_open_v2(filename, &db, flags, nullptr); - if (error != SQLITE_OK) { - const auto message = sqlite3_errmsg(db); - db = nullptr; - throw Exception { error, message }; - } } ~DatabaseImpl() { - if (!db) return; - const int error = sqlite3_close(db); if (error != SQLITE_OK) { mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", sqlite3_errmsg(db), error); } } - sqlite3* db = nullptr; + sqlite3* db; }; class StatementImpl { @@ -69,14 +62,84 @@ public: template <typename T> using optional = std::experimental::optional<T>; +static const char* codeToString(const int err) { + switch (err) { + case SQLITE_OK: return "SQLITE_OK"; + case SQLITE_ERROR: return "SQLITE_ERROR"; + case SQLITE_INTERNAL: return "SQLITE_INTERNAL"; + case SQLITE_PERM: return "SQLITE_PERM"; + case SQLITE_ABORT: return "SQLITE_ABORT"; + case SQLITE_BUSY: return "SQLITE_BUSY"; + case SQLITE_LOCKED: return "SQLITE_LOCKED"; + case SQLITE_NOMEM: return "SQLITE_NOMEM"; + case SQLITE_READONLY: return "SQLITE_READONLY"; + case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT"; + case SQLITE_IOERR: return "SQLITE_IOERR"; + case SQLITE_CORRUPT: return "SQLITE_CORRUPT"; + case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND"; + case SQLITE_FULL: return "SQLITE_FULL"; + case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN"; + case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL"; + case SQLITE_EMPTY: return "SQLITE_EMPTY"; + case SQLITE_SCHEMA: return "SQLITE_SCHEMA"; + case SQLITE_TOOBIG: return "SQLITE_TOOBIG"; + case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT"; + case SQLITE_MISMATCH: return "SQLITE_MISMATCH"; + case SQLITE_MISUSE: return "SQLITE_MISUSE"; + case SQLITE_NOLFS: return "SQLITE_NOLFS"; + case SQLITE_AUTH: return "SQLITE_AUTH"; + case SQLITE_FORMAT: return "SQLITE_FORMAT"; + case SQLITE_RANGE: return "SQLITE_RANGE"; + case SQLITE_NOTADB: return "SQLITE_NOTADB"; + case SQLITE_NOTICE: return "SQLITE_NOTICE"; + case SQLITE_WARNING: return "SQLITE_WARNING"; + case SQLITE_ROW: return "SQLITE_ROW"; + case SQLITE_DONE: return "SQLITE_DONE"; + default: return "<unknown>"; + } +} + static void errorLogCallback(void *, const int err, const char *msg) { - if (err == SQLITE_ERROR) { - mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else if (err == SQLITE_WARNING) { - mbgl::Log::Warning(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else { - mbgl::Log::Info(mbgl::Event::Database, "%s (Code %i)", msg, err); + auto severity = mbgl::EventSeverity::Info; + + switch (err) { + case SQLITE_ERROR: // Generic error + case SQLITE_INTERNAL: // Internal logic error in SQLite + case SQLITE_PERM: // Access permission denied + case SQLITE_ABORT: // Callback routine requested an abort + case SQLITE_BUSY: // The database file is locked + case SQLITE_LOCKED: // A table in the database is locked + case SQLITE_NOMEM: // A malloc() failed + case SQLITE_READONLY: // Attempt to write a readonly database + case SQLITE_INTERRUPT: // Operation terminated by sqlite3_interrupt( + case SQLITE_IOERR: // Some kind of disk I/O error occurred + case SQLITE_CORRUPT: // The database disk image is malformed + case SQLITE_NOTFOUND: // Unknown opcode in sqlite3_file_control() + case SQLITE_FULL: // Insertion failed because database is full + case SQLITE_CANTOPEN: // Unable to open the database file + case SQLITE_PROTOCOL: // Database lock protocol error + case SQLITE_EMPTY: // Internal use only + case SQLITE_SCHEMA: // The database schema changed + case SQLITE_TOOBIG: // String or BLOB exceeds size limit + case SQLITE_CONSTRAINT: // Abort due to constraint violation + case SQLITE_MISMATCH: // Data type mismatch + case SQLITE_MISUSE: // Library used incorrectly + case SQLITE_NOLFS: // Uses OS features not supported on host + case SQLITE_AUTH: // Authorization denied + case SQLITE_FORMAT: // Not used + case SQLITE_RANGE: // 2nd parameter to sqlite3_bind out of range + case SQLITE_NOTADB: // File opened that is not a database file + severity = mbgl::EventSeverity::Error; + break; + case SQLITE_WARNING: // Warnings from sqlite3_log() + severity = mbgl::EventSeverity::Warning; + break; + case SQLITE_NOTICE: // Notifications from sqlite3_log() + default: + break; } + + mbgl::Log::Record(severity, mbgl::Event::Database, "%s (%s)", msg, codeToString(err)); } const static bool sqliteVersionCheck __attribute__((unused)) = []() { @@ -94,11 +157,29 @@ const static bool sqliteVersionCheck __attribute__((unused)) = []() { return true; }(); -Database::Database(const std::string &filename, int flags) - : impl(std::make_unique<DatabaseImpl>(filename.c_str(), flags)) -{ +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); + if (error != SQLITE_OK) { + const auto message = sqlite3_errmsg(db); + return Exception { error, message }; + } + return Database(std::make_unique<DatabaseImpl>(db)); } +Database Database::open(const std::string &filename, int flags) { + auto result = tryOpen(filename, flags); + if (result.is<Exception>()) { + throw result.get<Exception>(); + } else { + return std::move(result.get<Database>()); + } +} + +Database::Database(std::unique_ptr<DatabaseImpl> impl_) + : impl(std::move(impl_)) +{} + Database::Database(Database &&other) : impl(std::move(other.impl)) {} @@ -131,128 +212,137 @@ void Database::exec(const std::string &sql) { } } -Statement Database::prepare(const char *query) { - assert(impl); - return Statement(this, query); +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique<StatementImpl>(db.impl->db, sql)) { } -Statement::Statement(Database *db, const char *sql) - : impl(std::make_unique<StatementImpl>(db->impl->db, sql)) -{ +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif } -Statement::Statement(Statement &&other) { - *this = std::move(other); -} +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); -Statement &Statement::operator=(Statement &&other) { - std::swap(impl, other.impl); - return *this; +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() = default; +Query::~Query() { + reset(); + clearBindings(); -template <> void Statement::bind(int offset, std::nullptr_t) { - assert(impl); - impl->check(sqlite3_bind_null(impl->stmt, offset)); +#ifndef NDEBUG + stmt.used = false; +#endif } -template <> void Statement::bind(int offset, int8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_null(stmt.impl->stmt, offset)); } -template <> void Statement::bind(int offset, int16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int8_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, int32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int16_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, int64_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int32_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int64_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint8_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint16_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, float value) { - assert(impl); - impl->check(sqlite3_bind_double(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint32_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, double value) { - assert(impl); - impl->check(sqlite3_bind_double(impl->stmt, offset, value)); +template <> void Query::bind(int offset, float value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, bool value) { - assert(impl); - impl->check(sqlite3_bind_int(impl->stmt, offset, value)); +template <> void Query::bind(int offset, double value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, const char *value) { - assert(impl); - impl->check(sqlite3_bind_text(impl->stmt, offset, value, -1, SQLITE_STATIC)); +template <> void Query::bind(int offset, bool value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int(stmt.impl->stmt, offset, value)); } -// We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they -// was introduced in SQLite 3.8.7, and we need to support earlier versions: -// iOS 8.0: 3.7.13 -// iOS 8.2: 3.8.5 -// According to http://stackoverflow.com/questions/14288128/what-version-of-sqlite-does-ios-provide, -// the first iOS version with 3.8.7+ was 9.0, with 3.8.8. +template <> void Query::bind(int offset, const char *value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, -1, SQLITE_STATIC)); +} -void Statement::bind(int offset, const char * value, std::size_t length, bool retain) { - assert(impl); +// We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they +// were introduced in SQLite 3.8.7, and we need to support earlier versions: +// Android 11: 3.7 +// Android 21: 3.8 +// Android 24: 3.9 +// Per https://developer.android.com/reference/android/database/sqlite/package-summary. +// The first iOS version with 3.8.7+ was 9.0, with 3.8.8. + +void Query::bind(int offset, const char * value, std::size_t length, bool retain) { + assert(stmt.impl); if (length > std::numeric_limits<int>::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - impl->check(sqlite3_bind_text(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void * value, std::size_t length, bool retain) { - assert(impl); +void Query::bindBlob(int offset, const void * value, std::size_t length, bool retain) { + assert(stmt.impl); if (length > std::numeric_limits<int>::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - impl->check(sqlite3_bind_blob(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_blob(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { +void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } template <> -void Statement::bind( +void Query::bind( int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); } -template <> void Statement::bind(int offset, optional<std::string> value) { +template <> void Query::bind(int offset, optional<std::string> value) { if (!value) { bind(offset, nullptr); } else { @@ -261,7 +351,7 @@ template <> void Statement::bind(int offset, optional<std::string> value) { } template <> -void Statement::bind( +void Query::bind( int offset, optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> value) { if (!value) { @@ -271,86 +361,86 @@ void Statement::bind( } } -bool Statement::run() { - assert(impl); - const int err = sqlite3_step(impl->stmt); - impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(impl->stmt)); - impl->changes = sqlite3_changes(sqlite3_db_handle(impl->stmt)); +bool Query::run() { + assert(stmt.impl); + const int err = sqlite3_step(stmt.impl->stmt); + stmt.impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(stmt.impl->stmt)); + stmt.impl->changes = sqlite3_changes(sqlite3_db_handle(stmt.impl->stmt)); if (err == SQLITE_DONE) { return false; } else if (err == SQLITE_ROW) { return true; } else if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(impl->stmt)) }; + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt.impl->stmt)) }; } else { return false; } } -template <> bool Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> bool Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> int Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int64_t Statement::get(int offset) { - assert(impl); - return sqlite3_column_int64(impl->stmt, offset); +template <> int64_t Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int64(stmt.impl->stmt, offset); } -template <> double Statement::get(int offset) { - assert(impl); - return sqlite3_column_double(impl->stmt, offset); +template <> double Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_double(stmt.impl->stmt, offset); } -template <> std::string Statement::get(int offset) { - assert(impl); +template <> std::string Query::get(int offset) { + assert(stmt.impl); return { - reinterpret_cast<const char *>(sqlite3_column_blob(impl->stmt, offset)), - size_t(sqlite3_column_bytes(impl->stmt, offset)) + reinterpret_cast<const char *>(sqlite3_column_blob(stmt.impl->stmt, offset)), + size_t(sqlite3_column_bytes(stmt.impl->stmt, offset)) }; } -template <> std::vector<uint8_t> Statement::get(int offset) { - assert(impl); - const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(impl->stmt, offset)); - const uint8_t* end = begin + sqlite3_column_bytes(impl->stmt, offset); +template <> std::vector<uint8_t> Query::get(int offset) { + assert(stmt.impl); + const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(stmt.impl->stmt, offset)); + const uint8_t* end = begin + sqlite3_column_bytes(stmt.impl->stmt, offset); return { begin, end }; } template <> std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> -Statement::get(int offset) { - assert(impl); +Query::get(int offset) { + assert(stmt.impl); return std::chrono::time_point_cast<std::chrono::seconds>( - std::chrono::system_clock::from_time_t(sqlite3_column_int64(impl->stmt, offset))); + std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt.impl->stmt, offset))); } -template <> optional<int64_t> Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional<int64_t> Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional<int64_t>(); } else { return get<int64_t>(offset); } } -template <> optional<double> Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional<double> Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional<double>(); } else { return get<double>(offset); } } -template <> optional<std::string> Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional<std::string> Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional<std::string>(); } else { return get<std::string>(offset); @@ -359,9 +449,9 @@ template <> optional<std::string> Statement::get(int offset) { template <> optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> -Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return {}; } else { return get<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>( @@ -369,24 +459,24 @@ Statement::get(int offset) { } } -void Statement::reset() { - assert(impl); - sqlite3_reset(impl->stmt); +void Query::reset() { + assert(stmt.impl); + sqlite3_reset(stmt.impl->stmt); } -void Statement::clearBindings() { - assert(impl); - sqlite3_clear_bindings(impl->stmt); +void Query::clearBindings() { + assert(stmt.impl); + sqlite3_clear_bindings(stmt.impl->stmt); } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - auto changes_ = impl->changes; +uint64_t Query::changes() const { + assert(stmt.impl); + auto changes_ = stmt.impl->changes; return (changes_ < 0 ? 0 : changes_); } diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp index 82e3ceff6d..cdc94298fe 100644 --- a/platform/default/sqlite3.hpp +++ b/platform/default/sqlite3.hpp @@ -5,6 +5,7 @@ #include <stdexcept> #include <chrono> #include <memory> +#include <mapbox/variant.hpp> namespace mapbox { namespace sqlite { @@ -19,36 +20,72 @@ enum OpenFlag : int { PrivateCache = 0x00040000, }; -struct Exception : std::runtime_error { - enum Code : int { - OK = 0, - CANTOPEN = 14, - NOTADB = 26 - }; +enum class ResultCode : int { + OK = 0, + Error = 1, + Internal = 2, + Perm = 3, + Abort = 4, + Busy = 5, + Locked = 6, + NoMem = 7, + ReadOnly = 8, + Interrupt = 9, + IOErr = 10, + Corrupt = 11, + NotFound = 12, + Full = 13, + CantOpen = 14, + Protocol = 15, + Schema = 17, + TooBig = 18, + Constraint = 19, + Mismatch = 20, + Misuse = 21, + NoLFS = 22, + Auth = 23, + Range = 25, + NotADB = 26 +}; - Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {} - Exception(int err, const std::string& msg) : std::runtime_error(msg), code(err) {} - const int code = OK; +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(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) { + } + const ResultCode code = ResultCode::OK; }; class DatabaseImpl; class Statement; class StatementImpl; +class Query; class Database { private: + Database(std::unique_ptr<DatabaseImpl>); Database(const Database &) = delete; Database &operator=(const Database &) = delete; public: - Database(const std::string &filename, int flags = 0); + static mapbox::util::variant<Database, Exception> tryOpen(const std::string &filename, int flags = 0); + static Database open(const std::string &filename, int flags = 0); + Database(Database &&); ~Database(); Database &operator=(Database &&); void setBusyTimeout(std::chrono::milliseconds); void exec(const std::string &sql); - Statement prepare(const char *query); private: std::unique_ptr<DatabaseImpl> impl; @@ -56,28 +93,54 @@ private: friend class Statement; }; +// A Statement object represents a prepared statement that can be run repeatedly run with a Query object. class Statement { +public: + Statement(Database& db, const char* sql); + Statement(const Statement&) = delete; + Statement(Statement&&) = delete; + Statement& operator=(const Statement&) = delete; + Statement& operator=(Statement&&) = delete; + ~Statement(); + + friend class Query; + private: - Statement(const Statement &) = delete; - Statement &operator=(const Statement &) = delete; + std::unique_ptr<StatementImpl> impl; + +#ifndef NDEBUG + // This flag stores whether there exists a Query object that uses this prepared statement. + // There may only be one Query object at a time. Statement objects must outlive Query objects. + // While a Query object exists, a Statement object may not be moved or deleted. + bool used = false; +#endif +}; +// A Query object is used to run a database query with a prepared statement (stored in a Statement +// object). There may only exist one Query object per Statement object. Query objects are designed +// to be constructed and destroyed frequently. +class Query { public: - Statement(Database *db, const char *sql); - Statement(Statement &&); - ~Statement(); - Statement &operator=(Statement &&); + Query(Statement&); + Query(const Query&) = delete; + Query(Query&&) = delete; + Query& operator=(const Query&) = delete; + Query& operator=(Query&&) = delete; + ~Query(); - template <typename T> void bind(int offset, T value); + template <typename T> + void bind(int offset, T value); // Text - void bind(int offset, const char *, std::size_t length, bool retain = true); + void bind(int offset, const char*, std::size_t length, bool retain = true); void bind(int offset, const std::string&, bool retain = true); // Blob - void bindBlob(int offset, const void *, std::size_t length, bool retain = true); + void bindBlob(int offset, const void*, std::size_t length, bool retain = true); void bindBlob(int offset, const std::vector<uint8_t>&, bool retain = true); - template <typename T> T get(int offset); + template <typename T> + T get(int offset); bool run(); void reset(); @@ -87,7 +150,7 @@ public: uint64_t changes() const; private: - std::unique_ptr<StatementImpl> impl; + Statement& stmt; }; class Transaction { diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp index 3988419265..362269b8e4 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -7,6 +7,8 @@ #include <mbgl/style/image.hpp> #include <mbgl/style/transition_options.hpp> #include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <mbgl/style/expression/compound_expression.hpp> +#include <mbgl/style/expression/literal.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/platform.hpp> #include <mbgl/util/string.hpp> @@ -619,6 +621,9 @@ void GLFWView::onDidFinishLoadingStyle() { } void GLFWView::toggle3DExtrusions(bool visible) { + using namespace mbgl::style; + using namespace mbgl::style::expression; + show3DExtrusions = visible; // Satellite-only style does not contain building extrusions data. @@ -634,7 +639,9 @@ void GLFWView::toggle3DExtrusions(bool visible) { auto extrusionLayer = std::make_unique<mbgl::style::FillExtrusionLayer>("3d-buildings", "composite"); extrusionLayer->setSourceLayer("building"); extrusionLayer->setMinZoom(15.0f); - extrusionLayer->setFilter(mbgl::style::EqualsFilter { "extrude", { std::string("true") } }); + + ParsingContext parsingContext; + extrusionLayer->setFilter(Filter(createCompoundExpression("filter-==", createLiteral("extrude"), createLiteral("true"), parsingContext))); auto colorFn = mbgl::style::SourceFunction<mbgl::Color> { "height", mbgl::style::ExponentialStops<mbgl::Color> { diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 7e5b13b28c..7c9c625011 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,18 +2,58 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 4.0.1 +## master + +### Packaging + +* The minimum deployment target for this SDK is now iOS 9.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776)) ### Style layers -* Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Added support for aggregate expressions as input values to `MGL_MATCH` expressions. ([#11866](https://github.com/mapbox/mapbox-gl-native/pull/11866)) -### Other +### Other changes -* Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) -* Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) +* Unknown tokens in URLs are now preserved, rather than replaced with an empty string. ([#11787](https://github.com/mapbox/mapbox-gl-native/issues/11787)) +* Adjusted when and how the camera transition update and finish callbacks are called, fixing recursion bugs. ([#11614](https://github.com/mapbox/mapbox-gl-native/pull/11614)) * Improved application launch performance. +## 4.0.1 - May 14, 2018 + +### Packaging + +* Re-added support for 32-bit simulators (i386) to work around an issue in CocoaPods. ([#11891](https://github.com/mapbox/mapbox-gl-native/pull/11891)) +* Added a Korean localization. ([#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792)) + +### Style layers + +* Deprecated `+[NSExpression featurePropertiesVariableExpression]`; use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Added an `-[NSPredicate(MGLAdditions) predicateWithMGLJSONObject:]` method and `NSPredicate.mgl_jsonExpressionObject` property. ([#11810](https://github.com/mapbox/mapbox-gl-native/pull/11810)) +* Added `FIRST`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) +* Inside an expression, casting `nil` to a string turns it into the empty string instead of the string `"null"`. ([#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904)) +* Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) + +### Annotations + +* Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) +* Fixed an issue where annotation views could become distorted if `rotatesToMatchCamera` was enabled. ([#11817](https://github.com/mapbox/mapbox-gl-native/pull/11817)) +* Fixed `MGLAnnotationView.rotatesToMatchCamera` overriding other transforms that might be applied to annotation views that had this property enabled. ([#11842](https://github.com/mapbox/mapbox-gl-native/pull/11842)) +* Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) + +### Other changes + +* If English is the first language listed in the user’s Preferred Languages setting, `-[MGLStyle localizeLabelsIntoLocale:]` no longer prioritizes other languages over English. ([#11907](https://github.com/mapbox/mapbox-gl-native/pull/11907)) +* Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) +* Reduced per-frame render CPU time. ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) + +## 3.7.8 - May 7, 2018 + +* Improved compatibility with Mapbox China APIs. ([#11845](https://github.com/mapbox/mapbox-gl-native/pull/11845)) + +## 3.7.7 - May 3, 2018 + +* Fixed a crash when removing an `MGLOfflinePack`. ([#11821](https://github.com/mapbox/mapbox-gl-native/issues/11821)) + ## 4.0.0 - April 19, 2018 The 4.0._x_ series of releases will be the last to support iOS 8. The minimum iOS deployment version will increase to iOS 9.0 in a future release. diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md index 7a97074d38..34388c2589 100644 --- a/platform/ios/DEVELOPING.md +++ b/platform/ios/DEVELOPING.md @@ -4,14 +4,10 @@ This document explains how to build the Mapbox Maps SDK for iOS from source. It ## Requirements -The Mapbox Maps SDK for iOS and iosapp demo application require iOS 8.0 or above. _Note: Support for iOS 8 will be removed in a future release and the minimum iOS deployment version will increase to iOS 9.0._ - -The Mapbox Maps SDK for iOS requires Xcode 9.1 or above to compile from source. +See the "Requirements" section in [INSTALL.md](INSTALL.md). ## Building the SDK -Make sure that you have the [core dependencies](../../INSTALL.md) installed. - Create and open an Xcode workspace that includes both the SDK source and some Objective-C test applications by running: ```bash diff --git a/platform/ios/INSTALL.md b/platform/ios/INSTALL.md index 01f4621b1e..bfa7bf7ceb 100644 --- a/platform/ios/INSTALL.md +++ b/platform/ios/INSTALL.md @@ -4,31 +4,48 @@ This document explains how to build a development version of Mapbox Maps SDK for ### Requirements -The Mapbox Maps SDK for iOS is intended to run on iOS 8.0 and above on the following devices: +The Mapbox Maps SDK for iOS is intended to run on iOS 9.0 and above on the following devices: * iPhone 4s and above (5, 5c, 5s, 6, 6 Plus, 7, 7 Plus, 8, 8 Plus, X) * iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2, Pro) * iPod touch 5th generation and above -_Note: Support for iOS 8 will be removed in a future release and the minimum iOS deployment version will increase to iOS 9.0._ -Note that 32-bit simulators (such as the iPhone 5 or iPad 2) are not supported. +Note that debugging in 32-bit simulators (such as the iPhone 5 or iPad 2) is only partially supported. The Mapbox Maps SDK for iOS requires: * Xcode 9.1 or higher to compile from source * Xcode 8.0 or higher to integrate the compiled framework into an application -### Building the SDK - -1. [Install core dependencies](../../INSTALL.md). +Before building, follow these steps to install prerequisites: +1. Install [Xcode](https://developer.apple.com/xcode/) +1. Launch Xcode and install any updates +1. Install [Homebrew](http://brew.sh) +1. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org): + ``` + brew install node cmake ccache + ``` +1. Install [xcpretty](https://github.com/supermarin/xcpretty) (optional, used for prettifying command line builds): + ``` + [sudo] gem install xcpretty + ``` 1. Install [jazzy](https://github.com/realm/jazzy) for generating API documentation: - ``` [sudo] gem install jazzy ``` +### Building the SDK + +1. Clone the git repository: + ``` + git clone https://github.com/mapbox/mapbox-gl-native.git + cd mapbox-gl-native + ``` + Note that this repository uses Git submodules. They'll be automatically checked out when you first run a `make` command, + but are not updated automatically. We recommended that you run `git submodule update` after pulling down new commits to + this repository. 1. Run `make iframework BUILDTYPE=Release`. The packaging script will produce a `build/ios/pkg/` folder containing: - a `dynamic` folder containing a dynamically-linked fat framework with debug symbols for devices and the iOS Simulator - a `documentation` folder with HTML API documentation diff --git a/platform/ios/Integration Tests/MGLCameraTransitionTests.mm b/platform/ios/Integration Tests/MGLCameraTransitionTests.mm new file mode 100644 index 0000000000..d5c288fbb9 --- /dev/null +++ b/platform/ios/Integration Tests/MGLCameraTransitionTests.mm @@ -0,0 +1,398 @@ +#import "MGLMapViewIntegrationTest.h" +#import "../../darwin/src/MGLGeometry_Private.h" + +@interface MBCameraTransitionTests : MGLMapViewIntegrationTest +@end + +@implementation MBCameraTransitionTests + +- (void)testSetAndResetNorthWithDispatchAsyncInDelegateMethod { + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MBCameraTransitionTests *strongSelf = weakself; + + if (!strongSelf) return; + + [expectation fulfill]; + + MGLTestAssert(strongSelf, mapView.userTrackingMode != MGLUserTrackingModeFollowWithHeading); + if (mapView.direction != 0.0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [mapView resetNorth]; + }); + } + }; + + [self.mapView setDirection:90 animated:YES]; + + // loop, render, and wait + [self waitForExpectations:@[expectation] timeout:1.5]; +} + + +- (void)testSetAndResetNorthInDelegateMethod { + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MBCameraTransitionTests *strongSelf = weakself; + + if (!strongSelf) return; + + [expectation fulfill]; + + MGLTestAssert(strongSelf, mapView.userTrackingMode != MGLUserTrackingModeFollowWithHeading); + if (mapView.direction != 0.0) { + NSLog(@"Reset to north"); + [mapView resetNorth]; + } + }; + + [self.mapView setDirection:90 animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; +} + +- (void)testInterruptingAndResetNorthOnlyOnceInIsChanging { + + // Reset to non-zero, prior to testing + [self.mapView setDirection:45 animated:NO]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 1; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + __block BOOL startedReset = NO; + __block BOOL finishedReset = NO; + + self.regionIsChanging = ^(MGLMapView *mapView) { + MBCameraTransitionTests *strongSelf = weakself; + if (!strongSelf) return; + + if (!startedReset) { + NSLog(@"Reset to north, interrupting the previous transition"); + startedReset = YES; + [mapView resetNorth]; + finishedReset = YES; + } + }; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + MBCameraTransitionTests *strongSelf = weakself; + if (!strongSelf) return; + + MGLTestAssert(strongSelf, startedReset); + + if (finishedReset) { + MGLTestAssert(strongSelf, !(reason & MGLCameraChangeReasonTransitionCancelled)); + [expectation fulfill]; + } + else { + MGLTestAssert(strongSelf, reason & MGLCameraChangeReasonTransitionCancelled); + } + }; + + [self.mapView setDirection:90 animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; + + XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction); +} + +- (void)testSetCenterCancelsTransitions { + XCTestExpectation *cameraIsInDCExpectation = [self expectationWithDescription:@"camera reset to DC"]; + + CLLocationCoordinate2D dc = CLLocationCoordinate2DMake(38.894368, -77.036487); + CLLocationCoordinate2D dc_west = CLLocationCoordinate2DMake(38.894368, -77.076487); + + double zoomLevel = 15.0; + + [self.mapView setCenterCoordinate:dc zoomLevel:zoomLevel animated:NO]; + [self.mapView setCenterCoordinate:dc_west zoomLevel:zoomLevel animated:YES]; + + __weak typeof(self) weakself = self; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.15 * NSEC_PER_SEC), + dispatch_get_main_queue(), + ^{ + MBCameraTransitionTests *strongSelf = weakself; + + [strongSelf.mapView setCenterCoordinate:dc zoomLevel:zoomLevel animated:NO]; + MGLTestAssertEqualWithAccuracy(strongSelf, + dc.latitude, + strongSelf.mapView.centerCoordinate.latitude, + 0.0005, + @"setting center coordinate should cancel transitions"); + MGLTestAssertEqualWithAccuracy(strongSelf, + dc.longitude, + strongSelf.mapView.centerCoordinate.longitude, + 0.0005, + @"setting center coordinate should cancel transitions"); + [cameraIsInDCExpectation fulfill]; + }); + + [self waitForExpectations:@[cameraIsInDCExpectation] timeout:10.0]; +} + +- (void)testSetCenterCoordinateInDelegateMethod { + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + __block NSInteger delegateCallCount = 0; + + CLLocationCoordinate2D target = CLLocationCoordinate2DMake(40.0, 40.0); + CLLocationCoordinate2D target2 = CLLocationCoordinate2DMake(-40.0, -40.0); + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MBCameraTransitionTests *strongSelf = weakself; + + if (!strongSelf) return; + + MGLTestAssert(strongSelf, mapView.userTrackingMode != MGLUserTrackingModeFollowWithHeading); + + CLLocationCoordinate2D center = mapView.centerCoordinate; + + switch(delegateCallCount) { + case 0: + { + // Our center coordinate should match our target (assuming we're not + // constrained by zoom level) + MGLTestAssertEqualWithAccuracy(strongSelf, + target.longitude, + center.longitude, + 0.0005, + @"center coordinate longitude should be at target"); + + MGLTestAssertEqualWithAccuracy(strongSelf, + target.latitude, + center.latitude, + 0.0005, + @"center coordinate latitude should be at target"); + + // Now set another coordinate. + // Should take MGLAnimationDuration seconds (0.3s) + [mapView setCenterCoordinate:target2 animated:YES]; + break; + } + + case 1: + { + // Our center coordinate should match our target (assuming we're not + // constrained by zoom level) + MGLTestAssertEqualWithAccuracy(strongSelf, + target2.longitude, + center.longitude, + 0.0005, + @"center coordinate longitude should be at target2"); + + MGLTestAssertEqualWithAccuracy(strongSelf, + target2.latitude, + center.latitude, + 0.0005, + @"center coordinate latitude should be at target2"); + break; + + } + + default: + MGLTestFail(strongSelf); + break; + } + + delegateCallCount++; + + [expectation fulfill]; + }; + + // Should take MGLAnimationDuration seconds (0.3) + [self.mapView setCenterCoordinate:target zoomLevel:15.0 animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; +} + +- (void)testFlyToCameraInDelegateMethod { + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + + __weak typeof(self) weakself = self; + __block NSInteger delegateCallCount = 0; + expectation.expectedFulfillmentCount = 3; + expectation.assertForOverFulfill = YES; + + CLLocationCoordinate2D target = CLLocationCoordinate2DMake(40.0, 40.0); + CLLocationCoordinate2D target2 = CLLocationCoordinate2DMake(30.0, 30.0); + + __block BOOL runloop = YES; + + NSTimeInterval stop0 = CACurrentMediaTime(); + __block NSTimeInterval stop1 = 0.0; + __block NSTimeInterval stop2 = 0.0; + + double zoomLevel = 5.0; + double altitude = MGLAltitudeForZoomLevel(zoomLevel, 0.0, target.latitude, self.mapView.frame.size); + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MBCameraTransitionTests *strongSelf = weakself; + + if (!strongSelf) return; + + MGLTestAssert(strongSelf, mapView.userTrackingMode != MGLUserTrackingModeFollowWithHeading); + + CLLocationCoordinate2D center = mapView.centerCoordinate; + + switch(delegateCallCount) { + case 0: + { + stop1 = CACurrentMediaTime(); + + // Our center coordinate should match our target (assuming we're not + // constrained by zoom level) + MGLTestAssertEqualWithAccuracy(strongSelf, + target.longitude, + center.longitude, + 0.0005, + @"center coordinate longitude should be at target"); + + MGLTestAssertEqualWithAccuracy(strongSelf, + target.latitude, + center.latitude, + 0.0005, + @"center coordinate latitude should be at target"); + + // Now set another coordinate. + MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:target2 + fromDistance:altitude + pitch:0.0 + heading:0.0]; + + // flyToCamera can take a while... + [mapView flyToCamera:camera completionHandler:^{ + MGLTestAssert(strongSelf, !runloop, @"Completion block should be called after delegate method"); + [expectation fulfill]; + stop2 = CACurrentMediaTime(); + }]; + break; + } + + case 1: + { + // Our center coordinate should match our target (assuming we're not + // constrained by zoom level) + MGLTestAssertEqualWithAccuracy(strongSelf, + target2.longitude, + center.longitude, + 0.0005, + @"center coordinate longitude should be at target2"); + + MGLTestAssertEqualWithAccuracy(strongSelf, + target2.latitude, + center.latitude, + 0.0005, + @"center coordinate latitude should be at target2"); + + runloop = NO; + break; + } + + default: + MGLTestFail(strongSelf); + break; + } + + delegateCallCount++; + + [expectation fulfill]; + }; + + // Should take MGLAnimationDuration + [self.mapView setCenterCoordinate:target zoomLevel:zoomLevel animated:YES]; + + // Run the loop, so the camera can fly to the new camera + while (runloop) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + [self waitForExpectations:@[expectation] timeout:0.5]; + + NSLog(@"setCenterCoordinate: %0.4fs", stop1 - stop0); + NSLog(@"flyToCamera: %0.4fs", stop2 - stop1); + + XCTAssert(delegateCallCount == 2, @"Expecting 2 regionDidChange callbacks, got %ld", delegateCallCount); // Once for the setDirection and once for the reset north +} + +#pragma mark - Pending tests + +- (void)disabled_testContinuallyResettingNorthInIsChangingPENDING { + + // See https://github.com/mapbox/mapbox-gl-native/pull/11614 + // This test currently fails, unsurprisingly, since we're continually + // setting the camera to the same parameters during its update. + // + // Possible solutions/expectations: + // - If you set camera parameters that match the *current* target parameters + // then the transition could be a no-op. We'd need to consider any completion + // block + // - Ideally we would detect this case and disallow it. + + // Reset to non-zero, prior to testing + [self.mapView setDirection:45 animated:NO]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + self.regionIsChanging = ^(MGLMapView *mapView) { + [mapView resetNorth]; + }; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + [expectation fulfill]; + }; + + [self.mapView setDirection:90 animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; + + XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction); +} + +- (void)disabled_testContinuallySettingCoordinateInIsChangingPENDING { + // See above comment in `-disabled_testContinuallyResettingNorthInIsChangingPENDING` + + // Reset to non-zero, prior to testing + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(0.0, 0.0) animated:NO]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + + self.regionIsChanging = ^(MGLMapView *mapView) { + [weakself.mapView setCenterCoordinate:CLLocationCoordinate2DMake(-40.0, -40.0) animated:YES]; + }; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + [expectation fulfill]; + }; + + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(40.0, 40.0) animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; + + XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction); +} + +@end diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h index ab5d2cc46f..6c04ed9f84 100644 --- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h +++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h @@ -1,18 +1,23 @@ #import <XCTest/XCTest.h> #import <Mapbox/Mapbox.h> -#define TestFailWithSelf(myself, ...) \ +#define MGLTestFail(myself, ...) \ _XCTPrimitiveFail(myself, __VA_ARGS__) +#define MGLTestAssert(myself, expression, ...) \ + _XCTPrimitiveAssertTrue(myself, expression, @#expression, __VA_ARGS__) + +#define MGLTestAssertEqualWithAccuracy(myself, expression1, expression2, accuracy, ...) \ + _XCTPrimitiveAssertEqualWithAccuracy(myself, expression1, @#expression1, expression2, @#expression2, accuracy, @#accuracy, __VA_ARGS__) + @interface MGLMapViewIntegrationTest : XCTestCase <MGLMapViewDelegate> @property (nonatomic) MGLMapView *mapView; @property (nonatomic) MGLStyle *style; @property (nonatomic) XCTestExpectation *styleLoadingExpectation; @property (nonatomic) XCTestExpectation *renderFinishedExpectation; -@property (nonatomic) void (^regionDidChange)(MGLMapView *mapView, BOOL animated); +@property (nonatomic) void (^regionWillChange)(MGLMapView *mapView, BOOL animated); @property (nonatomic) void (^regionIsChanging)(MGLMapView *mapView); - - +@property (nonatomic) void (^regionDidChange)(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated); // Utility methods - (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout; diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m index fc3229c83b..c42b8eef89 100644 --- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m +++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m @@ -45,9 +45,9 @@ self.renderFinishedExpectation = nil; } -- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated { - if (self.regionDidChange) { - self.regionDidChange(mapView, animated); +- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated { + if (self.regionWillChange) { + self.regionWillChange(mapView, animated); } } @@ -57,6 +57,12 @@ } } +- (void)mapView:(MGLMapView *)mapView regionDidChangeWithReason:(MGLCameraChangeReason)reason animated:(BOOL)animated { + if (self.regionDidChange) { + self.regionDidChange(mapView, reason, animated); + } +} + #pragma mark - Utilities - (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout { diff --git a/platform/ios/Integration Tests/MGLShapeSourceTests.m b/platform/ios/Integration Tests/MGLShapeSourceTests.m new file mode 100644 index 0000000000..088a9b011e --- /dev/null +++ b/platform/ios/Integration Tests/MGLShapeSourceTests.m @@ -0,0 +1,126 @@ +// +// MBShapeSourceTests.m +// integration +// +// Created by Julian Rex on 4/5/18. +// Copyright © 2018 Mapbox. All rights reserved. +// + +#import "MGLMapViewIntegrationTest.h" + +@interface MGLShapeSourceTests : MGLMapViewIntegrationTest +@end + +@implementation MGLShapeSourceTests + +- (void)testSettingShapeSourceToNilInRegionDidChange { + + NSMutableArray *features = [[NSMutableArray alloc] init]; + + for (NSUInteger i = 0; i <= 180; i+=5) { + CLLocationCoordinate2D coord[4] = { + CLLocationCoordinate2DMake(round(0), round(i)), + CLLocationCoordinate2DMake(round(20), round(i)), + CLLocationCoordinate2DMake(round(0), round(i / 2 )), + CLLocationCoordinate2DMake(round(20), round(i / 2))}; + + MGLPolygonFeature *feature = [MGLPolygonFeature polygonWithCoordinates:coord count:4]; + [features addObject:feature]; + } + + MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"source" features:features options:nil]; + [self.style addSource:shapeSource]; + + MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"layer" source:shapeSource]; + layer.fillOpacity = [NSExpression expressionForConstantValue:@0.5]; + [self.style addLayer:layer]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 1; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + __block NSInteger delegateCallCount = 0; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MGLShapeSourceTests *strongSelf = weakself; + + if (!strongSelf) + return; + + delegateCallCount++; + + // Setting the shapeSource.shape = nil, was causing an infinite loop, so here + // we check for a runaway call. 10 here is arbitrary. We could argue that this + // should check that the call count is only 1, however in this case we particularly + // want to check for the infinite loop. + // See https://github.com/mapbox/mapbox-gl-native/issues/11180 + + if (delegateCallCount > 10) { + MGLTestFail(strongSelf); + } + else { + shapeSource.shape = nil; + } + + [expectation fulfill]; + }; + + // setCenterCoordinate is NOT animated here. + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)]; + [self waitForExpectations:@[expectation] timeout:5.0]; +} + +- (void)testSettingShapeSourceToNilInRegionIsChanging { + + NSMutableArray *features = [[NSMutableArray alloc] init]; + + for (NSUInteger i = 0; i <= 180; i+=5) { + CLLocationCoordinate2D coord[4] = { + CLLocationCoordinate2DMake(round(0), round(i)), + CLLocationCoordinate2DMake(round(20), round(i)), + CLLocationCoordinate2DMake(round(0), round(i / 2 )), + CLLocationCoordinate2DMake(round(20), round(i / 2))}; + + MGLPolygonFeature *feature = [MGLPolygonFeature polygonWithCoordinates:coord count:4]; + [features addObject:feature]; + } + + MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"source" features:features options:nil]; + [self.style addSource:shapeSource]; + + MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"layer" source:shapeSource]; + layer.fillOpacity = [NSExpression expressionForConstantValue:@0.5]; + [self.style addLayer:layer]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 1; + expectation.assertForOverFulfill = YES; + + __block NSInteger delegateCallCount = 0; + __weak typeof(self) weakself = self; + + self.regionIsChanging = ^(MGLMapView *mapView) { + // See https://github.com/mapbox/mapbox-gl-native/issues/11180 + shapeSource.shape = nil; + }; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + delegateCallCount++; + + if (delegateCallCount > 1) { + MGLTestFail(weakself); + } + + [expectation fulfill]; + }; + + // Should take MGLAnimationDuration seconds (0.3) + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0) animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.0]; +} + + +@end diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index 7ea1b993ef..f2d8cee025 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.0.0' + version = '4.1.0-alpha.1' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :ios - m.ios.deployment_target = '8.0' + m.ios.deployment_target = '9.0' m.requires_arc = true diff --git a/platform/ios/Mapbox-iOS-SDK-static-part.podspec b/platform/ios/Mapbox-iOS-SDK-static-part.podspec index b2a114f9d2..bd98bb7272 100644 --- a/platform/ios/Mapbox-iOS-SDK-static-part.podspec +++ b/platform/ios/Mapbox-iOS-SDK-static-part.podspec @@ -1,4 +1,4 @@ - m.ios.deployment_target = '8.0' + m.ios.deployment_target = '9.0' m.requires_arc = true diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index 47a021598f..7f4f27301e 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.0.0' + version = '4.1.0-alpha.1' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :ios - m.ios.deployment_target = '8.0' + m.ios.deployment_target = '9.0' m.requires_arc = true diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 73412890c8..79229c5e8e 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.0.0' + version = '4.1.0-alpha.1' m.name = 'Mapbox-iOS-SDK' m.version = version @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :ios - m.ios.deployment_target = '8.0' + m.ios.deployment_target = '9.0' m.requires_arc = true diff --git a/platform/ios/app/MBXOfflinePacksTableViewController.m b/platform/ios/app/MBXOfflinePacksTableViewController.m index 26a15a0b95..959ae57548 100644 --- a/platform/ios/app/MBXOfflinePacksTableViewController.m +++ b/platform/ios/app/MBXOfflinePacksTableViewController.m @@ -45,7 +45,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS_DICTIONARY_OF(NSString *, id) *)change context:(void *)context { +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context { if ([keyPath isEqualToString:@"packs"]) { NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue]; NSIndexSet *indices = change[NSKeyValueChangeIndexesKey]; @@ -119,9 +119,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac }]; }]; [alertController addAction:downloadAction]; - if ([alertController respondsToSelector:@selector(setPreferredAction:)]) { - alertController.preferredAction = downloadAction; - } + alertController.preferredAction = downloadAction; [self presentViewController:alertController animated:YES completion:nil]; } diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 391af5ea05..0cb6c73d31 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -5,6 +5,7 @@ #import "MBXOfflinePacksTableViewController.h" #import "MBXAnnotationView.h" #import "MBXUserLocationAnnotationView.h" +#import "LimeGreenStyleLayer.h" #import "MBXEmbeddedMapViewController.h" #import <Mapbox/Mapbox.h> @@ -82,6 +83,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) { MBXSettingsRuntimeStylingRasterTileSource, MBXSettingsRuntimeStylingImageSource, MBXSettingsRuntimeStylingRouteLine, + MBXSettingsRuntimeStylingAddLimeGreenTriangleLayer, MBXSettingsRuntimeStylingDDSPolygon, MBXSettingsRuntimeStylingCustomLatLonGrid, }; @@ -175,9 +177,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { self.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]; self.mapView.showsScale = YES; self.mapView.showsUserHeadingIndicator = YES; - if ([UIFont respondsToSelector:@selector(monospacedDigitSystemFontOfSize:weight:)]) { - self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular]; - } + self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular]; if ([MGLAccountManager accessToken].length) { @@ -207,11 +207,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView reloadStyle:self]; }]; [alertController addAction:OKAction]; + alertController.preferredAction = OKAction; - if ([alertController respondsToSelector:@selector(setPreferredAction:)]) - { - alertController.preferredAction = OKAction; - } [self presentViewController:alertController animated:YES completion:nil]; } } @@ -371,6 +368,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Style Raster Tile Source", @"Style Image Source", @"Add Route Line", + @"Add Lime Green Triangle Layer", @"Dynamically Style Polygon", @"Add Custom Lat/Lon Grid", ]]; @@ -559,6 +557,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { case MBXSettingsRuntimeStylingRouteLine: [self styleRouteLine]; break; + case MBXSettingsRuntimeStylingAddLimeGreenTriangleLayer: + [self styleAddLimeGreenTriangleLayer]; + break; case MBXSettingsRuntimeStylingDDSPolygon: [self stylePolygonWithDDS]; break; @@ -1443,6 +1444,12 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addLayer:routeLayer]; } +- (void)styleAddLimeGreenTriangleLayer +{ + LimeGreenStyleLayer *layer = [[LimeGreenStyleLayer alloc] initWithIdentifier:@"mbx-custom"]; + [self.mapView.style addLayer:layer]; +} + - (void)stylePolygonWithDDS { CLLocationCoordinate2D leftCoords[] = { {37.73081027834234, -122.49412536621094}, @@ -1532,7 +1539,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self continueWorldTourWithRemainingAnnotations:annotations]; } -- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations +- (void)continueWorldTourWithRemainingAnnotations:(NSMutableArray<MGLPointAnnotation *> *)annotations { MGLPointAnnotation *nextAnnotation = annotations.firstObject; if (!nextAnnotation || !_isTouringWorld) diff --git a/platform/ios/app/ko.lproj/Localizable.strings b/platform/ios/app/ko.lproj/Localizable.strings new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/platform/ios/app/ko.lproj/Localizable.strings diff --git a/platform/ios/benchmark/MBXBenchViewController.mm b/platform/ios/benchmark/MBXBenchViewController.mm index d4629e2521..84c2790d50 100644 --- a/platform/ios/benchmark/MBXBenchViewController.mm +++ b/platform/ios/benchmark/MBXBenchViewController.mm @@ -76,10 +76,8 @@ [self startBenchmarkIteration]; }]; [alertController addAction:OKAction]; + alertController.preferredAction = OKAction; - if ([alertController respondsToSelector:@selector(setPreferredAction:)]) { - alertController.preferredAction = OKAction; - } [self presentViewController:alertController animated:YES completion:nil]; } } diff --git a/platform/ios/docs/doc-README.md b/platform/ios/docs/doc-README.md index 7cd0376d07..3a95aa96de 100644 --- a/platform/ios/docs/doc-README.md +++ b/platform/ios/docs/doc-README.md @@ -1,6 +1,6 @@ # [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/) -The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 8.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. +The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 9.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. ![Mapbox Maps SDK for iOS screenshots](img/screenshot.png) diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index b3beea8540..d4fb17eb6a 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -280,7 +280,7 @@ Each property representing a layout or paint attribute is set to an but you create the former using a very different syntax. `NSExpression`’s format string syntax is reminiscent of a spreadsheet formula or an expression in a database query. See the -“[Predicates and Expressions](Predicates and Expressions.md)” guide for an +“[Predicates and Expressions](predicates-and-expressions.html)” guide for an overview of the expression support in this SDK. This SDK no longer supports style functions; use expressions instead. @@ -325,7 +325,7 @@ In style specification | Method, function, or predicate type | Format string syn `number` | | `string` | | `to-boolean` | `boolValue` | -`to-color` | | +`to-color` | | `CAST(var, 'UIColor')` `to-number` | `mgl_numberWithFallbackValues:` | `CAST(zipCode, 'NSNumber')` `to-string` | `stringValue` | `CAST(ele, 'NSString')` `typeof` | | @@ -357,7 +357,7 @@ In style specification | Method, function, or predicate type | Format string syn `upcase` | `uppercase:` | `uppercase('Elysian Fields')` `rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` | `rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` | -`to-rgba` | | +`to-rgba` | | `CAST(noindex(var), 'NSArray')` `-` | `from:subtract:` | `2 - 1` `*` | `multiply:by:` | `1 * 2` `/` | `divide:by:` | `1 / 2` @@ -412,5 +412,5 @@ In style JSON | In the format string `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` -See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for +See the “[Predicates and Expressions](predicates-and-expressions.html)” guide for a full description of the supported operators and operand types. diff --git a/platform/ios/docs/pod-README.md b/platform/ios/docs/pod-README.md index f94073bd9f..8a3080055d 100644 --- a/platform/ios/docs/pod-README.md +++ b/platform/ios/docs/pod-README.md @@ -1,6 +1,6 @@ # [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/) -The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 8.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. +The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 9.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. For more information, check out the [Mapbox Maps SDK for iOS homepage](https://www.mapbox.com/ios-sdk/) and the [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) online. @@ -10,7 +10,7 @@ For more information, check out the [Mapbox Maps SDK for iOS homepage](https://w The Mapbox Maps SDK for iOS may be installed as either a dynamic framework or a static framework. (To reduce the download size, the static framework is omitted from some distributions; you may need to download the full package from the [release page](https://github.com/mapbox/mapbox-gl-native/releases/).) -Integrating the Mapbox Maps SDK for iOS requires Xcode 8.0 or higher. To use this SDK with Xcode 7.3.1, download and use a symbols build from the [releases](https://github.com/mapbox/mapbox-gl-native/releases) page. +Integrating the Mapbox Maps SDK for iOS requires Xcode 8.0 or higher. {{DYNAMIC}} diff --git a/platform/ios/framework/Settings.bundle/ko.lproj/Root.strings b/platform/ios/framework/Settings.bundle/ko.lproj/Root.strings new file mode 100644 index 0000000000..1fc9f9ce51 --- /dev/null +++ b/platform/ios/framework/Settings.bundle/ko.lproj/Root.strings @@ -0,0 +1,3 @@ +"TELEMETRY_GROUP_TITLE" = "개인 정보 설정"; +"TELEMETRY_SWITCH_TITLE" = "맵박스 텔레메트리"; +"TELEMETRY_GROUP_FOOTER" = "이 설정은 어플리케이션이 익명화된 장소와 사용데이터를 맵박스와 공유하는것을 허용합니다."; diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 6892760f59..aef2552b0b 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -36,6 +36,8 @@ 1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454941ECD450D00021D39 /* MGLLight_Private.h */; }; 1F7454A91ED08AB400021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */; }; 1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */; }; + 1FC4817D2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */; }; + 1FC4817F2098CD80000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */; }; 30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; 30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; 30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */; }; @@ -53,7 +55,6 @@ 3510FFEC1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; }; 3510FFED1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; }; 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; }; 3510FFF31D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; }; 3510FFF91D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFF71D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h */; }; @@ -131,8 +132,8 @@ 357FE2E01E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */; }; 3598544D1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */; }; 359F57461D2FDDA6005217F1 /* MGLUserLocationAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */; }; - 35B82BF81D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; }; - 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; }; + 35B82BF81D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */; }; 35B82BFB1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */; }; 35B8E08C1D6C8B5100E768D2 /* MGLPredicateTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B8E08B1D6C8B5100E768D2 /* MGLPredicateTests.mm */; }; @@ -159,6 +160,9 @@ 35E79F201D41266300957B9E /* MGLStyleLayer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */; }; 35E79F211D41266300957B9E /* MGLStyleLayer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */; }; 36F1153D1D46080700878E1A /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 36F1153B1D46080700878E1A /* libmbgl-core.a */; }; + 3E6465D62065767A00685536 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E6465D42065767A00685536 /* LimeGreenStyleLayer.m */; }; + 3E8770612074297100B7E842 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; + 3E8E82F120744B4100E7BE97 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E8E82F020744B4100E7BE97 /* libmbgl-core.a */; }; 3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */; }; 3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */; }; 3EA9363147E77DD29FA06063 /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */; }; @@ -267,9 +271,6 @@ 40F887701D7A1E58008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; }; 40F887711D7A1E59008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; }; 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; }; - 5549A0381EF1D86B00073113 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5549A0371EF1D86B00073113 /* libmbgl-core.a */; }; - 5549A0391EF2877100073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; - 5549A03A1EF2877500073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; 556660CA1E1BF3A900E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 556660D81E1D085500E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D71E1D085500E2C41B /* MGLVersionNumber.m */; }; 556660DB1E1D8E8D00E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -307,6 +308,7 @@ 9620BB391E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; }; 9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; }; 9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; }; + 9621F2502091020E005B3800 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9654C1261FFC1AB900DB6A19 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */; }; 9654C1291FFC1CCD00DB6A19 /* MGLPolygon_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */; }; 9658C155204761FC00D8A674 /* MGLMapViewScaleBarTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */; }; @@ -361,8 +363,10 @@ AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; }; AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; AC518E04201BB56100EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; + CA0C27922076C804001CE5B7 /* MGLShapeSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0C27912076C804001CE5B7 /* MGLShapeSourceTests.m */; }; CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */; }; CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */; }; + CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */; }; CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; CAA69DA4206DCD0E007279CD /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA4A26961CB6E795000B7809 /* Mapbox.framework */; }; @@ -511,8 +515,6 @@ DA8963381CC549A100684375 /* sprites in Resources */ = {isa = PBXBuildFile; fileRef = DA8963341CC549A100684375 /* sprites */; }; DA8963391CC549A100684375 /* styles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963351CC549A100684375 /* styles */; }; DA89633A1CC549A100684375 /* tiles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963361CC549A100684375 /* tiles */; }; - DA9EA82B201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - DA9EA82C201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */; }; DAA32CC31E4C6B65006F8D24 /* MGLDistanceFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3557F7AF1E1D27D300CCA5E6 /* MGLDistanceFormatter.m */; }; DAA4E4081CBB6C9500178DFB /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; }; DAA4E4091CBB6C9500178DFB /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -755,6 +757,7 @@ 1F7454941ECD450D00021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = "<group>"; }; 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLightTest.mm; path = ../../darwin/test/MGLLightTest.mm; sourceTree = "<group>"; }; 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = "<group>"; }; + 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLPrivateAdditions.h"; sourceTree = "<group>"; }; 20DABE861DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Foundation.strings"; sourceTree = "<group>"; }; 20DABE881DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; }; 20DABE8A1DF78149007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Root.strings"; sourceTree = "<group>"; }; @@ -831,6 +834,9 @@ 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleLayer_Private.h; sourceTree = "<group>"; }; 36F1153B1D46080700878E1A /* libmbgl-core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-core.a"; path = "build/Debug-iphoneos/libmbgl-core.a"; sourceTree = "<group>"; }; 36F1153C1D46080700878E1A /* libmbgl-platform-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-platform-ios.a"; path = "build/Debug-iphoneos/libmbgl-platform-ios.a"; sourceTree = "<group>"; }; + 3E6465D42065767A00685536 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LimeGreenStyleLayer.m; path = ../../darwin/app/LimeGreenStyleLayer.m; sourceTree = "<group>"; }; + 3E6465D52065767A00685536 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LimeGreenStyleLayer.h; path = ../../darwin/app/LimeGreenStyleLayer.h; sourceTree = "<group>"; }; + 3E8E82F020744B4100E7BE97 /* libmbgl-core.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-core.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRendererConfiguration.mm; sourceTree = "<group>"; }; 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererConfiguration.h; sourceTree = "<group>"; }; 400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+MGLAdditions.h"; sourceTree = "<group>"; }; @@ -992,9 +998,11 @@ 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>"; }; + CA0C27912076C804001CE5B7 /* MGLShapeSourceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLShapeSourceTests.m; sourceTree = "<group>"; }; CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewIntegrationTest.m; sourceTree = "<group>"; }; CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLMapViewIntegrationTest.h; sourceTree = "<group>"; }; CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleLayerIntegrationTests.m; sourceTree = "<group>"; }; + CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCameraTransitionTests.mm; sourceTree = "<group>"; }; CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChangeReason.h; sourceTree = "<group>"; }; DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo.h; sourceTree = "<group>"; }; DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAttributionInfo.mm; sourceTree = "<group>"; }; @@ -1188,7 +1196,6 @@ DA9C012C1E4C7ADB00C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Foundation.stringsdict"; sourceTree = "<group>"; }; DA9C012D1E4C7B1F00C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; }; DA9C012E1E4C7B6100C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Root.strings"; sourceTree = "<group>"; }; - DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = "<group>"; }; DAA32CA11E4C44DB006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Foundation.stringsdict; sourceTree = "<group>"; }; DAA32CA21E4C44DD006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; }; DAA32CA31E4C44F1006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Foundation.strings; sourceTree = "<group>"; }; @@ -1259,6 +1266,12 @@ DAFBD0D21E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = "<group>"; }; DAFBD0D31E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; }; DAFBD0D41E3FA7A2000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Root.strings"; sourceTree = "<group>"; }; + DAFEB3742093AE3700A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; }; + DAFEB3752093AE4800A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Foundation.strings; sourceTree = "<group>"; }; + DAFEB3762093AE6800A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ko; path = ko.lproj/Foundation.stringsdict; sourceTree = "<group>"; }; + DAFEB3772093AE7900A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; }; + DAFEB3782093AE9200A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ko; path = ko.lproj/Localizable.stringsdict; sourceTree = "<group>"; }; + DAFEB3792093AEA100A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Root.strings; sourceTree = "<group>"; }; DD0902A21DB18DE700C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = "<group>"; }; DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = "<group>"; }; DD4823721D94AE6C00EB71B7 /* fill_filter_style.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = fill_filter_style.json; sourceTree = "<group>"; }; @@ -1294,7 +1307,6 @@ buildActionMask = 2147483647; files = ( DA8847D91CBAF91600AB86E3 /* Mapbox.framework in Frameworks */, - 5549A0391EF2877100073113 /* OpenGLES.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1302,7 +1314,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5549A0381EF1D86B00073113 /* libmbgl-core.a in Frameworks */, + 3E8E82F120744B4100E7BE97 /* libmbgl-core.a in Frameworks */, DA2E88561CC036F400F24E7B /* Mapbox.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1333,7 +1345,7 @@ buildActionMask = 2147483647; files = ( DAA4E4081CBB6C9500178DFB /* Mapbox.framework in Frameworks */, - 5549A03A1EF2877500073113 /* OpenGLES.framework in Frameworks */, + 3E8770612074297100B7E842 /* OpenGLES.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1345,6 +1357,8 @@ children = ( 16376B091FFD9DAF0000563E /* MBGLIntegrationTests.m */, 16376B0B1FFD9DAF0000563E /* Info.plist */, + CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */, + CA0C27912076C804001CE5B7 /* MGLShapeSourceTests.m */, CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */, CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */, CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */, @@ -1727,6 +1741,8 @@ DA1DC94C1CB6C1C2006E619F /* Demo App */ = { isa = PBXGroup; children = ( + 3E6465D52065767A00685536 /* LimeGreenStyleLayer.h */, + 3E6465D42065767A00685536 /* LimeGreenStyleLayer.m */, DA1DC9501CB6C1C2006E619F /* MBXAppDelegate.h */, DA1DC9981CB6E054006E619F /* MBXAppDelegate.m */, 40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */, @@ -1766,6 +1782,7 @@ DA1DC9921CB6DF24006E619F /* Frameworks */ = { isa = PBXGroup; children = ( + 3E8E82F020744B4100E7BE97 /* libmbgl-core.a */, 55D120AD1F791018004B6D81 /* libmbgl-loop-darwin.a */, 55D120AB1F791015004B6D81 /* libmbgl-filesource.a */, 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */, @@ -2099,11 +2116,11 @@ 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */, DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */, 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */, - DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */, DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */, 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */, 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */, 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */, + 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */, DA8848151CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h */, DA8848161CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m */, DA8848171CBAFA6200AB86E3 /* NSString+MGLAdditions.h */, @@ -2192,6 +2209,7 @@ DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */, 35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */, 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */, + 1FC4817D2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */, 353AFA141D65AB17005A69F4 /* NSDate+MGLAdditions.h in Headers */, DA8848531CBAFB9800AB86E3 /* MGLCompactCalloutView.h in Headers */, DA8847FB1CBAFA5100AB86E3 /* MGLShape.h in Headers */, @@ -2289,7 +2307,6 @@ DA8848841CBB033F00AB86E3 /* FABAttributes.h in Headers */, DA8847FD1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h in Headers */, DA88482F1CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h in Headers */, - DA9EA82B201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */, DA8848601CBAFC2E00AB86E3 /* Mapbox.h in Headers */, DAF0D8101DFE0EA000B28378 /* MGLRasterTileSource_Private.h in Headers */, 350098BB1D480108004B2AF0 /* MGLVectorTileSource.h in Headers */, @@ -2319,7 +2336,6 @@ DABFB8641CBE99E500D62B32 /* MGLOfflineStorage.h in Headers */, 96E516E32000552A00A02306 /* MGLAccountManager_Private.h in Headers */, 96E5170420005A6B00A02306 /* SMCalloutView.h in Headers */, - DA9EA82C201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */, 96036A02200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */, DAD165791CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */, 4049C29E1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */, @@ -2329,7 +2345,6 @@ 96E516F6200059EC00A02306 /* MGLRendererFrontend.h in Headers */, 071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */, DABFB8611CBE99E500D62B32 /* MGLMultiPoint.h in Headers */, - 3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */, 35D3A1E71E9BE7EC002B38EE /* MGLScaleBar.h in Headers */, 96E516EF2000594F00A02306 /* NSArray+MGLAdditions.h in Headers */, 96E516F12000596800A02306 /* NSString+MGLAdditions.h in Headers */, @@ -2374,6 +2389,7 @@ 96E516F920005A3500A02306 /* MGLFaux3DUserLocationAnnotationView.h in Headers */, 96E516F22000596D00A02306 /* NSException+MGLAdditions.h in Headers */, 96E516EC2000560B00A02306 /* MGLUserLocationAnnotationView_Private.h in Headers */, + 1FC4817F2098CD80000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */, DABFB8671CBE99E500D62B32 /* MGLPolygon.h in Headers */, 404C26E81D89C55D000AA13D /* MGLTileSource_Private.h in Headers */, 1F7454931ECBB43F00021D39 /* MGLLight.h in Headers */, @@ -2434,6 +2450,7 @@ 96E516E12000551100A02306 /* MGLMultiPoint_Private.h in Headers */, 3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */, DACA86272019218600E9693A /* MGLRasterDEMSource.h in Headers */, + 9621F2502091020E005B3800 /* NSExpression+MGLAdditions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2609,7 +2626,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = MBX; - LastUpgradeCheck = 0910; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = Mapbox; TargetAttributes = { 16376B061FFD9DAF0000563E = { @@ -2676,6 +2693,7 @@ he, da, "pt-PT", + ko, ); mainGroup = DA1DC9411CB6C1C2006E619F; productRefGroup = DA1DC94B1CB6C1C2006E619F /* Products */; @@ -2797,8 +2815,10 @@ buildActionMask = 2147483647; files = ( CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */, + CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */, 16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.m in Sources */, CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */, + CA0C27922076C804001CE5B7 /* MGLShapeSourceTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2823,6 +2843,7 @@ 927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */, DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */, 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */, + 3E6465D62065767A00685536 /* LimeGreenStyleLayer.m in Sources */, 632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3220,6 +3241,7 @@ DACBC60B20118ABE00C4D7E2 /* he */, DAD88E07202ACFE800AAA536 /* da */, DA93409B208562EB0059919A /* pt-PT */, + DAFEB3742093AE3700A86A83 /* ko */, ); name = Localizable.strings; sourceTree = "<group>"; @@ -3248,6 +3270,7 @@ DACBC60E20118AFE00C4D7E2 /* he */, DAD88E0C202AD06500AAA536 /* da */, DA93409F208563440059919A /* pt-PT */, + DAFEB3792093AEA100A86A83 /* ko */, ); name = Root.strings; sourceTree = "<group>"; @@ -3274,6 +3297,7 @@ DACBC60D20118ADE00C4D7E2 /* he */, DAD88E0A202AD03C00AAA536 /* da */, DA93409D208563220059919A /* pt-PT */, + DAFEB3772093AE7900A86A83 /* ko */, ); name = Localizable.strings; sourceTree = "<group>"; @@ -3297,6 +3321,7 @@ DA3389661FA3EE28001EA329 /* bg */, DACBC60C20118AD000C4D7E2 /* he */, DAD88E08202AD01300AAA536 /* da */, + DAFEB3752093AE4800A86A83 /* ko */, ); name = Foundation.strings; sourceTree = "<group>"; @@ -3321,6 +3346,7 @@ DA80E9611FE84AEF0065FC9B /* ar */, DAD88E09202AD01F00AAA536 /* da */, DA93409C2085630C0059919A /* pt-PT */, + DAFEB3762093AE6800A86A83 /* ko */, ); name = Foundation.stringsdict; sourceTree = "<group>"; @@ -3351,6 +3377,7 @@ DA3389691FA3EE50001EA329 /* bg */, DAD88E0B202AD04D00AAA536 /* da */, DA93409E208563360059919A /* pt-PT */, + DAFEB3782093AE9200A86A83 /* ko */, ); name = Localizable.stringsdict; sourceTree = "<group>"; @@ -3360,6 +3387,7 @@ /* Begin XCBuildConfiguration section */ 16376B0E1FFD9DAF0000563E /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -3374,8 +3402,8 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; + HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = "Integration Tests/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.integration-tests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3386,6 +3414,7 @@ }; 16376B0F1FFD9DAF0000563E /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -3400,8 +3429,8 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; + HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = "Integration Tests/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.integration-tests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3427,7 +3456,6 @@ CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Integration Test Harness/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.Integration-Test-Harness"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3452,7 +3480,6 @@ CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Integration Test Harness/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.Integration-Test-Harness"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3474,6 +3501,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -3481,6 +3509,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -3510,7 +3539,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -3533,6 +3562,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -3540,6 +3570,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -3563,7 +3594,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -3579,7 +3610,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL; PRODUCT_NAME = "Mapbox GL"; @@ -3593,7 +3623,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL; PRODUCT_NAME = "Mapbox GL"; @@ -3628,7 +3657,6 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = test/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( @@ -3653,7 +3681,6 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = test/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( @@ -3688,7 +3715,6 @@ ); INFOPLIST_FILE = framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( @@ -3731,7 +3757,6 @@ ); INFOPLIST_FILE = framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( @@ -3845,7 +3870,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/benchmark/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.bench; PRODUCT_NAME = "Bench GL"; @@ -3857,7 +3881,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/benchmark/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.bench; PRODUCT_NAME = "Bench GL"; diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme index afba168676..01b565d5d9 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0910" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/Integration Test Harness.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/Integration Test Harness.xcscheme index 283c58ef7f..1638592557 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/Integration Test Harness.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/Integration Test Harness.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0920" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -26,7 +26,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference @@ -56,7 +55,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme index d1a152bce7..68e1c54ead 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0910" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -26,7 +26,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> </Testables> @@ -46,7 +45,6 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme index e5f17124f2..325b58d690 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0910" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -54,7 +54,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference @@ -84,7 +83,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme index 3816aa5c91..6c7bff4527 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0910" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -40,7 +40,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference @@ -70,7 +69,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme index 7d5f2cd6d2..1f8969faf7 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0910" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -26,7 +26,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> </Testables> @@ -46,7 +45,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme index fc385d3763..f88ec1a04c 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0910" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -26,7 +26,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> </Testables> @@ -46,7 +45,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index 34dcc1f50b..d3aae51b77 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -19,10 +19,10 @@ custom_categories: children: - Adding Markers to a Map - Runtime Styling - - Migrating to Expressions - Working with Mapbox Studio - Working with GeoJSON Data - Predicates and Expressions + - Migrating to Expressions - For Style Authors - Tile URL Templates - Info.plist Keys diff --git a/platform/ios/resources/ko.lproj/Localizable.strings b/platform/ios/resources/ko.lproj/Localizable.strings new file mode 100644 index 0000000000..40136803d1 --- /dev/null +++ b/platform/ios/resources/ko.lproj/Localizable.strings @@ -0,0 +1,117 @@ +/* Accessibility hint */ +"ANNOTATION_A11Y_HINT" = "추가 정보 보기"; + +/* No comment provided by engineer. */ +"API_CLIENT_400_DESC" = "세션 데이타 작업 실패. 초기 요청: %@"; + +/* No comment provided by engineer. */ +"API_CLIENT_400_REASON" = "상태 코드 %ld"; + +/* No comment provided by engineer. */ +"CANCEL" = "취소"; + +/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */ +"CLOSE_CALLOUT_A11Y_HINT" = "지도로 복귀"; + +/* Accessibility hint */ +"COMPASS_A11Y_HINT" = "정북쪽으로 지도 회전"; + +/* Accessibility label */ +"COMPASS_A11Y_LABEL" = "나침반"; + +/* Compass abbreviation for north */ +"COMPASS_NORTH" = "북"; + +/* Instructions in Interface Builder designable; {key}, {plist file name} */ +"DESIGNABLE" = "맵박스에의한 지도를 출력하기 위해, %1$@ 를 %2$@에 당신의 접근 토큰으로 으로 설정하십시오. \n\n자세한 정보를 찾으시는 경우, 확인:"; + +/* Setup documentation URL display string; keep as short as possible */ +"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk"; + +/* Accessibility hint */ +"INFO_A11Y_HINT" = "크레딧, 피드백 폼, 기타 보기"; + +/* Accessibility label */ +"INFO_A11Y_LABEL" = "이 맵에 관하여"; + +/* List separator */ +"LIST_SEPARATOR" = ", "; + +/* User-friendly error description */ +"LOAD_MAP_FAILED_DESC" = "알수 없는 에러로 인해 맵 로드에 실패하였습니다."; + +/* User-friendly error description */ +"LOAD_STYLE_FAILED_DESC" = "형식이 로드되지 않아, 맵 로드에 실패하였습니다."; + +/* Accessibility label */ +"LOGO_A11Y_LABEL" = "맵박스"; + +/* Accessibility label */ +"MAP_A11Y_LABEL" = "지도"; + +/* Map accessibility value; {number of visible annotations} */ +"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld 주석 볼 수 있는."; + +/* Map accessibility value; {list of visible places} */ +"MAP_A11Y_VALUE_PLACES" = "가시거리 범위: %@."; + +/* Map accessibility value; {number of visible roads} */ +"MAP_A11Y_VALUE_ROADS" = "%ld 도로 볼 수 있는."; + +/* Map accessibility value; {zoom level} */ +"MAP_A11Y_VALUE_ZOOM" = "확대 %dx."; + +/* User-friendly error description */ +"PARSE_STYLE_FAILED_DESC" = "형식에 오류가 발생하여 맵 로드에 실패하였습니다."; + +/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */ +"ROAD_DIRECTION_A11Y_FMT" = "%1$@ to %2$@"; + +/* Accessibility value indicating that a road is a divided road (dual carriageway) */ +"ROAD_DIVIDED_A11Y_VALUE" = "분리 도로"; + +/* Accessibility value indicating that a road is a one-way road */ +"ROAD_ONEWAY_A11Y_VALUE" = "일방 통행"; + +/* String format for accessibility value for road feature; {route number} */ +"ROAD_REF_A11Y_FMT" = "경로 %@"; + +/* Action sheet title */ +"SDK_NAME" = "iOS를 위한 맵박스 지도 SDK"; + +/* Developer-only SDK update notification; {latest version, in format x.x.x} */ +"SDK_UPDATE_AVAILABLE" = "iOS버전 맵박스 지도 %@가 사용 가능합니다:"; + +/* User-friendly error description */ +"STYLE_NOT_FOUND_DESC" = "형식을 찾을 수 없거나 호환이 되지 않아, 맵 로드에 실패하였습니다."; + +/* Telemetry prompt message */ +"TELEMETRY_DISABLED_MSG" = "당신은 익명의 사용 데이타를 제공함으로써, 오픈스트리트맵과 맵박스의 향상에 기여하고 있습니다."; + +/* Telemetry prompt button */ +"TELEMETRY_DISABLED_OFF" = "참여하지 마십시오"; + +/* Telemetry prompt button */ +"TELEMETRY_DISABLED_ON" = "참여"; + +/* Telemetry prompt message */ +"TELEMETRY_ENABLED_MSG" = "당신은 익명의 사용 데이타를 제공함으로써, 오픈스트리트맵과 맵박스의 향상에 기여하고 있습니다."; + +/* Telemetry prompt button */ +"TELEMETRY_ENABLED_OFF" = "참여 중지"; + +/* Telemetry prompt button */ +"TELEMETRY_ENABLED_ON" = "참여 계속"; + +/* Telemetry prompt button */ +"TELEMETRY_MORE" = "좀 더 말해보세요"; + +/* Action in attribution sheet */ +"TELEMETRY_NAME" = "맵박스 텔레메트리"; + +/* Telemetry prompt title */ +"TELEMETRY_TITLE" = "더 나은 맵박스 지도 만들기"; + +/* Default user location annotation title */ +"USER_DOT_TITLE" = "당신의 위치"; + diff --git a/platform/ios/resources/ko.lproj/Localizable.stringsdict b/platform/ios/resources/ko.lproj/Localizable.stringsdict new file mode 100644 index 0000000000..fdde2639c6 --- /dev/null +++ b/platform/ios/resources/ko.lproj/Localizable.stringsdict @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>MAP_A11Y_VALUE_ANNOTATIONS</key> + <dict> + <key>NSStringLocalizedFormatKey</key> + <string>%#@count@</string> + <key>count</key> + <dict> + <key>NSStringFormatSpecTypeKey</key> + <string>NSStringPluralRuleType</string> + <key>NSStringFormatValueTypeKey</key> + <string>ld</string> + <key>other</key> + <string>%d 주석 보이기</string> + </dict> + </dict> + <key>MAP_A11Y_VALUE_ROADS</key> + <dict> + <key>NSStringLocalizedFormatKey</key> + <string>%#@count@</string> + <key>count</key> + <dict> + <key>NSStringFormatSpecTypeKey</key> + <string>NSStringPluralRuleType</string> + <key>NSStringFormatValueTypeKey</key> + <string>ld</string> + <key>other</key> + <string>%d 도로 보이기</string> + </dict> + </dict> + <key>MAP_A11Y_VALUE_ZOOM</key> + <dict> + <key>NSStringLocalizedFormatKey</key> + <string>%#@level@</string> + <key>level</key> + <dict> + <key>NSStringFormatSpecTypeKey</key> + <string>NSStringPluralRuleType</string> + <key>NSStringFormatValueTypeKey</key> + <string>d</string> + <key>other</key> + <string>확대 %dx</string> + </dict> + </dict> +</dict> +</plist> diff --git a/platform/ios/scripts/package.sh b/platform/ios/scripts/package.sh index 6df1b687b4..438fce240f 100755 --- a/platform/ios/scripts/package.sh +++ b/platform/ios/scripts/package.sh @@ -70,7 +70,7 @@ xcodebuild \ CURRENT_SHORT_VERSION=${SHORT_VERSION} \ CURRENT_SEMANTIC_VERSION=${SEM_VERSION} \ CURRENT_COMMIT_HASH=${HASH} \ - ARCHS="x86_64" \ + ONLY_ACTIVE_ARCH=NO \ -derivedDataPath ${DERIVED_DATA} \ -workspace ./platform/ios/ios.xcworkspace \ -scheme ${SCHEME} \ @@ -194,6 +194,10 @@ if [[ ${BUILD_DYNAMIC} == true && ${BUILDTYPE} == Release ]]; then validate_dsym \ "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" \ "${OUTPUT}/dynamic/${NAME}.framework/${NAME}" + + step "Removing i386 slice from dSYM" + lipo -remove i386 "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" -o "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" + lipo -info "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" fi function create_podspec { diff --git a/platform/ios/src/MGLAnnotationContainerView.h b/platform/ios/src/MGLAnnotationContainerView.h index 90d2964831..ccec3052a6 100644 --- a/platform/ios/src/MGLAnnotationContainerView.h +++ b/platform/ios/src/MGLAnnotationContainerView.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)annotationContainerViewWithAnnotationContainerView:(MGLAnnotationContainerView *)annotationContainerView; -- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews; +- (void)addSubviews:(NSArray<MGLAnnotationView *> *)subviews; @end diff --git a/platform/ios/src/MGLAnnotationContainerView.m b/platform/ios/src/MGLAnnotationContainerView.m index 9a823c839c..6c82a1836d 100644 --- a/platform/ios/src/MGLAnnotationContainerView.m +++ b/platform/ios/src/MGLAnnotationContainerView.m @@ -3,7 +3,7 @@ @interface MGLAnnotationContainerView () -@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *annotationViews; +@property (nonatomic) NSMutableArray<MGLAnnotationView *> *annotationViews; @end @@ -26,7 +26,7 @@ return newAnnotationContainerView; } -- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews +- (void)addSubviews:(NSArray<MGLAnnotationView *> *)subviews { for (MGLAnnotationView *view in subviews) { diff --git a/platform/ios/src/MGLAnnotationContainerView_Private.h b/platform/ios/src/MGLAnnotationContainerView_Private.h index 007b03550b..9dce54842d 100644 --- a/platform/ios/src/MGLAnnotationContainerView_Private.h +++ b/platform/ios/src/MGLAnnotationContainerView_Private.h @@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN @interface MGLAnnotationContainerView (Private) -@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *annotationViews; +@property (nonatomic) NSMutableArray<MGLAnnotationView *> *annotationViews; @end diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 1c53ba507a..46b0f56a79 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -11,8 +11,9 @@ @property (nonatomic, readwrite, nullable) NSString *reuseIdentifier; @property (nonatomic, readwrite) CATransform3D lastAppliedScaleTransform; -@property (nonatomic, readwrite) CATransform3D lastAppliedRotateTransform; @property (nonatomic, readwrite) CGFloat lastPitch; +@property (nonatomic, readwrite) CATransform3D lastAppliedRotationTransform; +@property (nonatomic, readwrite) CGFloat lastDirection; @property (nonatomic, weak) UIPanGestureRecognizer *panGestureRecognizer; @property (nonatomic, weak) UILongPressGestureRecognizer *longPressRecognizer; @property (nonatomic, weak) MGLMapView *mapView; @@ -43,9 +44,9 @@ - (void)commonInitWithAnnotation:(nullable id<MGLAnnotation>)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { _lastAppliedScaleTransform = CATransform3DIdentity; + _lastAppliedRotationTransform = CATransform3DIdentity; _annotation = annotation; _reuseIdentifier = [reuseIdentifier copy]; - _scalesWithViewingDistance = NO; _enabled = YES; } @@ -159,7 +160,7 @@ // We keep track of each viewing distance scale transform that we apply. Each iteration, // we can account for it so that we don't get cumulative scaling every time we move. - // We also avoid clobbering any existing transform passed in by the client, too. + // We also avoid clobbering any existing transform passed in by the client or this SDK. CATransform3D undoOfLastScaleTransform = CATransform3DInvert(_lastAppliedScaleTransform); CATransform3D newScaleTransform = CATransform3DMakeScale(pitchAdjustedScale, pitchAdjustedScale, 1); CATransform3D effectiveTransform = CATransform3DConcat(undoOfLastScaleTransform, newScaleTransform); @@ -181,11 +182,20 @@ { if (self.rotatesToMatchCamera == NO) return; - CGFloat directionRad = self.mapView.direction * M_PI / 180.0; - CATransform3D newRotateTransform = CATransform3DMakeRotation(-directionRad, 0, 0, 1); - self.layer.transform = CATransform3DConcat(CATransform3DIdentity, newRotateTransform); - - _lastAppliedRotateTransform = newRotateTransform; + CGFloat direction = -MGLRadiansFromDegrees(self.mapView.direction); + + // Return early if the map view has the same rotation as the already-applied transform. + if (direction == _lastDirection) return; + _lastDirection = direction; + + // We keep track of each rotation transform that we apply. Each iteration, + // we can account for it so that we don't get cumulative rotation every time we move. + // We also avoid clobbering any existing transform passed in by the client or this SDK. + CATransform3D undoOfLastRotationTransform = CATransform3DInvert(_lastAppliedRotationTransform); + CATransform3D newRotationTransform = CATransform3DMakeRotation(direction, 0, 0, 1); + CATransform3D effectiveTransform = CATransform3DConcat(undoOfLastRotationTransform, newRotationTransform); + self.layer.transform = CATransform3DConcat(self.layer.transform, effectiveTransform); + _lastAppliedRotationTransform = newRotationTransform; } #pragma mark - Draggable diff --git a/platform/ios/src/MGLCameraChangeReason.h b/platform/ios/src/MGLCameraChangeReason.h index 6c6b3636ba..f439d3e7ea 100644 --- a/platform/ios/src/MGLCameraChangeReason.h +++ b/platform/ios/src/MGLCameraChangeReason.h @@ -57,5 +57,9 @@ typedef NS_OPTIONS(NSUInteger, MGLCameraChangeReason) MGLCameraChangeReasonGestureOneFingerZoom = 1 << 7, // :nodoc: The user panned with two fingers to tilt the map (two finger drag). - MGLCameraChangeReasonGestureTilt = 1 << 8 + MGLCameraChangeReasonGestureTilt = 1 << 8, + + // :nodoc: Cancelled + MGLCameraChangeReasonTransitionCancelled = 1 << 16 + }; diff --git a/platform/ios/src/MGLMapAccessibilityElement.mm b/platform/ios/src/MGLMapAccessibilityElement.mm index 8bce38a145..c1cc5304d7 100644 --- a/platform/ios/src/MGLMapAccessibilityElement.mm +++ b/platform/ios/src/MGLMapAccessibilityElement.mm @@ -56,10 +56,8 @@ // may be in the local language, which may be written in another script. // Attempt to transform to the script of the preferred language, keeping // the original string if no transform exists or if transformation fails. - if (@available(iOS 9.0, *)) { - NSString *dominantScript = [NSOrthography mgl_dominantScriptForMapboxStreetsLanguage:languageCode]; - name = [name mgl_stringByTransliteratingIntoScript:dominantScript]; - } + NSString *dominantScript = [NSOrthography mgl_dominantScriptForMapboxStreetsLanguage:languageCode]; + name = [name mgl_stringByTransliteratingIntoScript:dominantScript]; self.accessibilityLabel = name; } diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 020dc5e830..fd0ca19b26 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -189,7 +189,7 @@ MGL_EXPORT IB_DESIGNABLE */ @property (nonatomic, readonly, nullable) MGLStyle *style; -@property (nonatomic, readonly) NS_ARRAY_OF(NSURL *) *bundledStyleURLs __attribute__((unavailable("Call the relevant class method of MGLStyle for the URL of a particular default style."))); +@property (nonatomic, readonly) NSArray<NSURL *> *bundledStyleURLs __attribute__((unavailable("Call the relevant class method of MGLStyle for the URL of a particular default style."))); /** URL of the style currently displayed in the receiver. @@ -286,7 +286,7 @@ MGL_EXPORT IB_DESIGNABLE */ - (IBAction)showAttribution:(id)sender; -@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((unavailable("Support for style classes has been removed."))); +@property (nonatomic) NSArray<NSString *> *styleClasses __attribute__((unavailable("Support for style classes has been removed."))); - (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed."))); @@ -773,7 +773,7 @@ MGL_EXPORT IB_DESIGNABLE @param animated `YES` if you want the map region change to be animated, or `NO` if you want the map to display the new region immediately without animations. */ -- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated; +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations animated:(BOOL)animated; /** Sets the visible region so that the map displays the specified annotations with @@ -788,7 +788,7 @@ MGL_EXPORT IB_DESIGNABLE @param animated `YES` if you want the map region change to be animated, or `NO` if you want the map to display the new region immediately without animations. */ -- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated; +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated; /** A camera representing the current viewpoint of the map. @@ -1076,7 +1076,7 @@ MGL_EXPORT IB_DESIGNABLE annotations are associated with the map view, the value of this property is `nil`. */ -@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations; +@property (nonatomic, readonly, nullable) NSArray<id <MGLAnnotation>> *annotations; /** Adds an annotation to the map view. @@ -1104,7 +1104,7 @@ MGL_EXPORT IB_DESIGNABLE must conform to the `MGLAnnotation` protocol. The map view retains each individual annotation object. */ -- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations; +- (void)addAnnotations:(NSArray<id <MGLAnnotation>> *)annotations; /** Removes an annotation from the map view, deselecting it if it is selected. @@ -1129,7 +1129,7 @@ MGL_EXPORT IB_DESIGNABLE @param annotations The array of annotation objects to remove. Objects in the array must conform to the `MGLAnnotation` protocol. */ -- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations; +- (void)removeAnnotations:(NSArray<id <MGLAnnotation>> *)annotations; /** Returns an `MGLAnnotationView` if the given annotation is currently associated @@ -1180,7 +1180,7 @@ MGL_EXPORT IB_DESIGNABLE annotations are associated with the map view or if no annotations associated with the map view are currently visible, the value of this property is `nil`. */ -@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *visibleAnnotations; +@property (nonatomic, readonly, nullable) NSArray<id <MGLAnnotation>> *visibleAnnotations; /** Returns the list of annotations associated with the receiver that intersect with @@ -1191,7 +1191,7 @@ MGL_EXPORT IB_DESIGNABLE no annotations associated with the map view are currently visible in the rectangle. */ -- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotationsInRect:(CGRect)rect; +- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotationsInRect:(CGRect)rect; #pragma mark Managing Annotation Selections @@ -1208,7 +1208,7 @@ MGL_EXPORT IB_DESIGNABLE @note In versions prior to `4.0.0` if the annotation was offscreen it was not selected. */ -@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations; +@property (nonatomic, copy) NSArray<id <MGLAnnotation>> *selectedAnnotations; /** Selects an annotation and displays its callout view. @@ -1246,7 +1246,7 @@ MGL_EXPORT IB_DESIGNABLE overlays are associated with the map view, the value of this property is empty array. */ -@property (nonatomic, readonly, nonnull) NS_ARRAY_OF(id <MGLOverlay>) *overlays; +@property (nonatomic, readonly, nonnull) NSArray<id <MGLOverlay>> *overlays; /** Adds a single overlay object to the map. @@ -1265,7 +1265,7 @@ MGL_EXPORT IB_DESIGNABLE @param overlays An array of objects, each of which must conform to the `MGLOverlay` protocol. */ -- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays; +- (void)addOverlays:(NSArray<id <MGLOverlay>> *)overlays; /** Removes a single overlay object from the map. @@ -1285,7 +1285,7 @@ MGL_EXPORT IB_DESIGNABLE @param overlays An array of objects, each of which conforms to the `MGLOverlay` protocol. */ -- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays; +- (void)removeOverlays:(NSArray<id <MGLOverlay>> *)overlays; #pragma mark Accessing the Underlying Map Data @@ -1301,7 +1301,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point NS_SWIFT_NAME(visibleFeatures(at:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point NS_SWIFT_NAME(visibleFeatures(at:)); /** Returns an array of rendered map features that intersect with a given point, @@ -1320,7 +1320,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); /** Returns an array of rendered map features that intersect with a given point, @@ -1387,7 +1387,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:)); /** Returns an array of rendered map features that intersect with the given @@ -1402,7 +1402,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect NS_SWIFT_NAME(visibleFeatures(in:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect NS_SWIFT_NAME(visibleFeatures(in:)); /** Returns an array of rendered map features that intersect with the given @@ -1421,7 +1421,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:)); /** Returns an array of rendered map features that intersect with the given @@ -1493,7 +1493,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:)); #pragma mark Debugging the Map diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index c679979d37..e16411f2c5 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -49,7 +49,7 @@ #import "NSBundle+MGLAdditions.h" #import "NSDate+MGLAdditions.h" #import "NSException+MGLAdditions.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSProcessInfo+MGLAdditions.h" #import "NSString+MGLAdditions.h" #import "NSURL+MGLAdditions.h" @@ -194,14 +194,14 @@ public: @property (nonatomic) GLKView *glView; @property (nonatomic) UIImageView *glSnapshotView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *scaleBarConstraints; +@property (nonatomic) NSMutableArray<NSLayoutConstraint *> *scaleBarConstraints; @property (nonatomic, readwrite) MGLScaleBar *scaleBar; @property (nonatomic, readwrite) UIImageView *compassView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *compassViewConstraints; +@property (nonatomic) NSMutableArray<NSLayoutConstraint *> *compassViewConstraints; @property (nonatomic, readwrite) UIImageView *logoView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints; +@property (nonatomic) NSMutableArray<NSLayoutConstraint *> *logoViewConstraints; @property (nonatomic, readwrite) UIButton *attributionButton; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints; +@property (nonatomic) NSMutableArray<NSLayoutConstraint *> *attributionButtonConstraints; @property (nonatomic, readwrite) MGLStyle *style; @@ -217,7 +217,7 @@ public: @property (nonatomic) MGLCameraChangeReason cameraChangeReasonBitmask; /// Mapping from reusable identifiers to annotation images. -@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier; +@property (nonatomic) NSMutableDictionary<NSString *, MGLAnnotationImage *> *annotationImagesByIdentifier; /// Currently shown popover representing the selected annotation. @property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation; @@ -235,7 +235,7 @@ public: @property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement; @property (nonatomic) MGLAnnotationContainerView *annotationContainerView; @property (nonatomic) MGLUserLocation *userLocation; -@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *) *annotationViewReuseQueueByIdentifier; +@property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<MGLAnnotationView *> *> *annotationViewReuseQueueByIdentifier; @end @@ -287,9 +287,9 @@ public: BOOL _delegateHasLineWidthsForShapeAnnotations; MGLCompassDirectionFormatter *_accessibilityCompassFormatter; - NS_ARRAY_OF(id <MGLFeature>) *_visiblePlaceFeatures; - NS_ARRAY_OF(id <MGLFeature>) *_visibleRoadFeatures; - NS_MUTABLE_SET_OF(MGLFeatureAccessibilityElement *) *_featureAccessibilityElements; + NSArray<id <MGLFeature>> *_visiblePlaceFeatures; + NSArray<id <MGLFeature>> *_visibleRoadFeatures; + NSMutableSet<MGLFeatureAccessibilityElement *> *_featureAccessibilityElements; BOOL _accessibilityValueAnnouncementIsPending; MGLReachability *_reachability; @@ -335,12 +335,12 @@ public: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle ++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyle { return [NSSet setWithObject:@"styleURL"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL ++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyleURL { return [NSSet setWithObjects:@"styleURL__", nil]; } @@ -624,18 +624,8 @@ public: UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, [UIScreen mainScreen].scale); [scaleImage drawInRect:{ CGPointZero, scaleImage.size }]; - CGFloat northSize = 11; - UIFont *northFont; - if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) - { - northFont = [UIFont systemFontOfSize:northSize weight:UIFontWeightUltraLight]; - } - else - { - northFont = [UIFont systemFontOfSize:northSize]; - } NSAttributedString *north = [[NSAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COMPASS_NORTH", nil, nil, @"N", @"Compass abbreviation for north") attributes:@{ - NSFontAttributeName: northFont, + NSFontAttributeName: [UIFont systemFontOfSize:11 weight:UIFontWeightUltraLight], NSForegroundColorAttributeName: [UIColor whiteColor], }]; CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2, @@ -1273,7 +1263,7 @@ public: } } -- (void)touchesBegan:(__unused NS_SET_OF(UITouch *) *)touches withEvent:(__unused UIEvent *)event +- (void)touchesBegan:(__unused NSSet<UITouch *> *)touches withEvent:(__unused UIEvent *)event { _changeDelimiterSuppressionDepth = 0; _mbglMap->setGestureInProgress(false); @@ -1281,7 +1271,7 @@ public: { [self setUserTrackingMode:MGLUserTrackingModeNone animated:NO]; } - _mbglMap->cancelTransitions(); + [self cancelTransitions]; } - (void)notifyGestureDidBegin { @@ -1331,7 +1321,7 @@ public: { if ( ! self.isScrollEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; MGLMapCamera *oldCamera = self.camera; @@ -1400,7 +1390,7 @@ public: { if ( ! self.isZoomEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; CGPoint centerPoint = [self anchorPointForGesture:pinch]; MGLMapCamera *oldCamera = self.camera; @@ -1501,7 +1491,7 @@ public: { if ( ! self.isRotateEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; CGPoint centerPoint = [self anchorPointForGesture:rotate]; MGLMapCamera *oldCamera = self.camera; @@ -1706,7 +1696,7 @@ public: { if ( ! self.isZoomEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; if (doubleTap.state == UIGestureRecognizerStateEnded) { @@ -1747,7 +1737,7 @@ public: if (_mbglMap->getZoom() == _mbglMap->getMinZoom()) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureZoomOut; @@ -1786,7 +1776,7 @@ public: { if ( ! self.isZoomEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureOneFingerZoom; @@ -1831,7 +1821,7 @@ public: { if ( ! self.isPitchEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureTilt; @@ -2238,22 +2228,22 @@ public: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled ++ (NSSet<NSString *> *)keyPathsForValuesAffectingZoomEnabled { return [NSSet setWithObject:@"allowsZooming"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingScrollEnabled ++ (NSSet<NSString *> *)keyPathsForValuesAffectingScrollEnabled { return [NSSet setWithObject:@"allowsScrolling"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingRotateEnabled ++ (NSSet<NSString *> *)keyPathsForValuesAffectingRotateEnabled { return [NSSet setWithObject:@"allowsRotating"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPitchEnabled ++ (NSSet<NSString *> *)keyPathsForValuesAffectingPitchEnabled { return [NSSet setWithObject:@"allowsTilting"]; } @@ -2419,7 +2409,7 @@ public: return value; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visiblePlaceFeatures +- (NSArray<id <MGLFeature>> *)visiblePlaceFeatures { if (!_visiblePlaceFeatures) { @@ -2429,7 +2419,7 @@ public: return _visiblePlaceFeatures; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleRoadFeatures +- (NSArray<id <MGLFeature>> *)visibleRoadFeatures { if (!_visibleRoadFeatures) { @@ -2907,7 +2897,7 @@ public: #pragma mark - Geography - -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCenterCoordinate ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCenterCoordinate { return [NSSet setWithObjects:@"latitude", @"longitude", @"camera", nil]; } @@ -2991,14 +2981,14 @@ public: return; } - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; _mbglMap->easeTo(cameraOptions, animationOptions); } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomLevel ++ (NSSet<NSString *> *)keyPathsForValuesAffectingZoomLevel { return [NSSet setWithObject:@"camera"]; } @@ -3016,7 +3006,7 @@ public: - (void)setZoomLevel:(double)zoomLevel animated:(BOOL)animated { if (zoomLevel == self.zoomLevel) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; @@ -3159,7 +3149,7 @@ public: } [self willChangeValueForKey:@"visibleCoordinateBounds"]; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; @@ -3167,7 +3157,7 @@ public: [self didChangeValueForKey:@"visibleCoordinateBounds"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingDirection ++ (NSSet<NSString *> *)keyPathsForValuesAffectingDirection { return [NSSet setWithObject:@"camera"]; } @@ -3192,7 +3182,7 @@ public: - (void)_setDirection:(CLLocationDirection)direction animated:(BOOL)animated { if (direction == self.direction) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; CGFloat duration = animated ? MGLAnimationDuration : 0; @@ -3217,12 +3207,12 @@ public: [self setDirection:direction animated:NO]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPitch ++ (NSSet<NSString *> *)keyPathsForValuesAffectingPitch { return [NSSet setWithObject:@"camera"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCamera ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCamera { return [NSSet setWithObjects:@"longitude", @"latitude", @"centerCoordinate", @"zoomLevel", @"direction", nil]; } @@ -3281,7 +3271,7 @@ public: } [self willChangeValueForKey:@"camera"]; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; @@ -3340,7 +3330,7 @@ public: } [self willChangeValueForKey:@"camera"]; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; @@ -3349,6 +3339,12 @@ public: [self didChangeValueForKey:@"camera"]; } +- (void)cancelTransitions { + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonTransitionCancelled; + _mbglMap->cancelTransitions(); + self.cameraChangeReasonBitmask &= ~MGLCameraChangeReasonTransitionCancelled; +} + - (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds { return [self cameraThatFitsCoordinateBounds:bounds edgePadding:UIEdgeInsetsZero]; @@ -3496,7 +3492,7 @@ public: #pragma mark - Annotations - -- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)annotations +- (nullable NSArray<id <MGLAnnotation>> *)annotations { if (_annotationContextsByAnnotationTag.empty()) { @@ -3520,12 +3516,12 @@ public: return [NSArray arrayWithObjects:&annotations[0] count:annotations.size()]; } -- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotations +- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotations { return [self visibleAnnotationsInRect:self.bounds]; } -- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotationsInRect:(CGRect)rect +- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotationsInRect:(CGRect)rect { if (_annotationContextsByAnnotationTag.empty()) { @@ -3599,7 +3595,7 @@ public: [self addAnnotations:@[ annotation ]]; } -- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations +- (void)addAnnotations:(NSArray<id <MGLAnnotation>> *)annotations { if ( ! annotations) return; [self willChangeValueForKey:@"annotations"]; @@ -3740,7 +3736,7 @@ public: UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); } -- (void)updateAnnotationContainerViewWithAnnotationViews:(NS_ARRAY_OF(MGLAnnotationView *) *)annotationViews +- (void)updateAnnotationContainerViewWithAnnotationViews:(NSArray<MGLAnnotationView *> *)annotationViews { if (annotationViews.count == 0) return; @@ -3895,7 +3891,7 @@ public: [self removeAnnotations:@[ annotation ]]; } -- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations +- (void)removeAnnotations:(NSArray<id <MGLAnnotation>> *)annotations { if ( ! annotations) return; @@ -3956,11 +3952,11 @@ public: } } -- (nonnull NS_ARRAY_OF(id <MGLOverlay>) *)overlays +- (nonnull NSArray<id <MGLOverlay>> *)overlays { if (self.annotations == nil) { return @[]; } - NS_MUTABLE_ARRAY_OF(id <MGLOverlay>) *mutableOverlays = [NSMutableArray array]; + NSMutableArray<id <MGLOverlay>> *mutableOverlays = [NSMutableArray array]; [self.annotations enumerateObjectsUsingBlock:^(id<MGLAnnotation> _Nonnull annotation, NSUInteger idx, BOOL * _Nonnull stop) { if ([annotation conformsToProtocol:@protocol(MGLOverlay)]) @@ -3977,7 +3973,7 @@ public: [self addOverlays:@[ overlay ]]; } -- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays +- (void)addOverlays:(NSArray<id <MGLOverlay>> *)overlays { #if DEBUG for (id <MGLOverlay> overlay in overlays) @@ -3994,7 +3990,7 @@ public: [self removeOverlays:@[ overlay ]]; } -- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays +- (void)removeOverlays:(NSArray<id <MGLOverlay>> *)overlays { #if DEBUG for (id <MGLOverlay> overlay in overlays) @@ -4226,13 +4222,13 @@ public: [self didChangeValueForKey:@"selectedAnnotations"]; } -- (NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations +- (NSArray<id <MGLAnnotation>> *)selectedAnnotations { id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation; return (selectedAnnotation ? @[ selectedAnnotation ] : @[]); } -- (void)setSelectedAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations +- (void)setSelectedAnnotations:(NSArray<id <MGLAnnotation>> *)selectedAnnotations { if ( ! selectedAnnotations.count) return; @@ -4570,6 +4566,8 @@ public: return; } + __weak __typeof__(self) weakSelf = self; + // The user location callout view initially points to the user location // annotation’s implicit (visual) frame, which is offset from the // annotation’s explicit frame. Now the callout view needs to rendezvous @@ -4583,15 +4581,21 @@ public: UIViewAnimationOptionBeginFromCurrentState) animations:^ { + __typeof__(self) strongSelf = weakSelf; + if ( ! strongSelf) + { + return; + } + calloutView.frame = CGRectOffset(calloutView.frame, - _initialImplicitCalloutViewOffset.x, - _initialImplicitCalloutViewOffset.y); - _initialImplicitCalloutViewOffset = CGPointZero; + strongSelf->_initialImplicitCalloutViewOffset.x, + strongSelf->_initialImplicitCalloutViewOffset.y); + strongSelf->_initialImplicitCalloutViewOffset = CGPointZero; } completion:NULL]; } -- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations animated:(BOOL)animated { CGFloat maximumPadding = 100; CGFloat yPadding = (self.frame.size.height / 5 <= maximumPadding) ? (self.frame.size.height / 5) : maximumPadding; @@ -4602,7 +4606,7 @@ public: [self showAnnotations:annotations edgePadding:edgeInsets animated:animated]; } -- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated { if ( ! annotations || ! annotations.count) return; @@ -4790,7 +4794,7 @@ public: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingUserLocation ++ (NSSet<NSString *> *)keyPathsForValuesAffectingUserLocation { return [NSSet setWithObject:@"userLocationAnnotationView"]; } @@ -5281,16 +5285,16 @@ public: #pragma mark Data -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point { return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:nil]; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers { +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers { return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil]; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { mbgl::ScreenCoordinate screenCoordinate = { point.x, point.y }; @@ -5315,15 +5319,15 @@ public: return MGLFeaturesFromMBGLFeatures(features); } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect { +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect { return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:nil]; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers { +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers { return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil]; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { mbgl::ScreenBox screenBox = { { CGRectGetMinX(rect), CGRectGetMinY(rect) }, { CGRectGetMaxX(rect), CGRectGetMaxY(rect) }, @@ -5717,10 +5721,12 @@ public: if (annotationView.layer.animationKeys.count > 0) { continue; } + // Move the annotation view far out of view to the left - CGRect adjustedFrame = annotationView.frame; - adjustedFrame.origin.x = -CGRectGetWidth(self.frame) * 10.0; - annotationView.frame = adjustedFrame; + CGPoint adjustedCenter = annotationView.center; + adjustedCenter.x = -CGRectGetWidth(self.frame) * 10.0; + annotationView.center = adjustedCenter; + [self enqueueAnnotationViewForAnnotationContext:annotationContext]; } } @@ -6055,7 +6061,7 @@ public: views:views]]; } -- (NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *)annotationViewReuseQueueForIdentifier:(NSString *)identifier { +- (NSMutableArray<MGLAnnotationView *> *)annotationViewReuseQueueForIdentifier:(NSString *)identifier { if (!_annotationViewReuseQueueByIdentifier[identifier]) { _annotationViewReuseQueueByIdentifier[identifier] = [NSMutableArray array]; @@ -6212,7 +6218,7 @@ private: @implementation MGLMapView (IBAdditions) -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL__ ++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyleURL__ { return [NSSet setWithObject:@"styleURL"]; } @@ -6235,7 +6241,7 @@ private: self.styleURL = url; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLatitude ++ (NSSet<NSString *> *)keyPathsForValuesAffectingLatitude { return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil]; } @@ -6261,7 +6267,7 @@ private: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLongitude ++ (NSSet<NSString *> *)keyPathsForValuesAffectingLongitude { return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil]; } @@ -6287,7 +6293,7 @@ private: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsZooming ++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsZooming { return [NSSet setWithObject:@"zoomEnabled"]; } @@ -6302,7 +6308,7 @@ private: self.zoomEnabled = allowsZooming; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsScrolling ++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsScrolling { return [NSSet setWithObject:@"scrollEnabled"]; } @@ -6317,7 +6323,7 @@ private: self.scrollEnabled = allowsScrolling; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsRotating ++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsRotating { return [NSSet setWithObject:@"rotateEnabled"]; } @@ -6332,7 +6338,7 @@ private: self.rotateEnabled = allowsRotating; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsTilting ++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsTilting { return [NSSet setWithObject:@"pitchEnabled"]; } @@ -6347,7 +6353,7 @@ private: self.pitchEnabled = allowsTilting; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingShowsHeading ++ (NSSet<NSString *> *)keyPathsForValuesAffectingShowsHeading { return [NSSet setWithObject:@"showsUserHeadingIndicator"]; } diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h index 0368d8413c..201e3db84b 100644 --- a/platform/ios/src/MGLMapViewDelegate.h +++ b/platform/ios/src/MGLMapViewDelegate.h @@ -428,7 +428,7 @@ NS_ASSUME_NONNULL_BEGIN @param annotationViews An array of `MGLAnnotationView` objects representing the views that were added. */ -- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NS_ARRAY_OF(MGLAnnotationView *) *)annotationViews; +- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NSArray<MGLAnnotationView *> *)annotationViews; #pragma mark Selecting Annotations diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index 139dffdfab..a2fc24c75c 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -178,12 +178,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084; #pragma mark - Convenience methods - (BOOL)usesRightToLeftLayout { - // semanticContentAttribute is iOS 9+ - if ([self.superview respondsToSelector:@selector(semanticContentAttribute)]) { - return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft; - } else { - return UIApplication.sharedApplication.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; - } + return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft; } - (BOOL)usesMetricSystem { diff --git a/platform/ios/src/MGLUserLocation.m b/platform/ios/src/MGLUserLocation.m index 074d138a72..245cbf4371 100644 --- a/platform/ios/src/MGLUserLocation.m +++ b/platform/ios/src/MGLUserLocation.m @@ -68,7 +68,7 @@ NS_ASSUME_NONNULL_END return ! [key isEqualToString:@"location"] && ! [key isEqualToString:@"heading"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCoordinate ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCoordinate { return [NSSet setWithObject:@"location"]; } diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 20417dbbd4..7beb8b766b 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -66,3 +66,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLAttributionInfo.h" #import "MGLMapSnapshotter.h" #import "NSExpression+MGLAdditions.h" +#import "NSPredicate+MGLAdditions.h" diff --git a/platform/ios/src/UIColor+MGLAdditions.mm b/platform/ios/src/UIColor+MGLAdditions.mm index 9ca39acda4..7c1fbddc20 100644 --- a/platform/ios/src/UIColor+MGLAdditions.mm +++ b/platform/ios/src/UIColor+MGLAdditions.mm @@ -66,7 +66,7 @@ return [UIColor colorWithRed:[components[0].constantValue doubleValue] / 255.0 green:[components[1].constantValue doubleValue] / 255.0 blue:[components[2].constantValue doubleValue] / 255.0 - alpha:components.count == 3 ? [components[3].constantValue doubleValue] : 1.0]; + alpha:components.count == 3 ? 1.0 : [components[3].constantValue doubleValue]]; } @end diff --git a/platform/ios/test/MGLMapAccessibilityElementTests.m b/platform/ios/test/MGLMapAccessibilityElementTests.m index 89a595421e..916461e708 100644 --- a/platform/ios/test/MGLMapAccessibilityElementTests.m +++ b/platform/ios/test/MGLMapAccessibilityElementTests.m @@ -25,11 +25,7 @@ @"name_en": @"Цинциннати", }; element = [[MGLFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature]; - if (@available(iOS 9.0, *)) { - XCTAssertEqualObjects(element.accessibilityLabel, @"Cincinnati", @"Accessibility label should be romanized."); - } else { - XCTAssertEqualObjects(element.accessibilityLabel, @"Цинциннати", @"Accessibility label should not be romanized."); - } + XCTAssertEqualObjects(element.accessibilityLabel, @"Cincinnati", @"Accessibility label should be romanized."); } - (void)testPlaceFeatureValues { diff --git a/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj b/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj index c9744d41bf..108139e6e3 100644 --- a/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj +++ b/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj @@ -47,6 +47,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3EAB52CE206ACE7E008BFE2D /* libmbgl-loop-darwin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-loop-darwin.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3EAB52CF206ACE7E008BFE2D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; 96567A221B0E84CD00D78776 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = SOURCE_ROOT; }; 96567A301B0E8BB900D78776 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = SOURCE_ROOT; }; DA180EF11DD1A4DF000A211D /* OHHTTPStubs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OHHTTPStubs.framework; path = "OHHTTPStubs/OHHTTPStubs/build/Debug-iphoneos/OHHTTPStubs.framework"; sourceTree = "<group>"; }; @@ -122,6 +124,8 @@ DD043325196DB9BC00E6F39D /* Frameworks */ = { isa = PBXGroup; children = ( + 3EAB52CF206ACE7E008BFE2D /* OpenGLES.framework */, + 3EAB52CE206ACE7E008BFE2D /* libmbgl-loop-darwin.a */, DA180EF11DD1A4DF000A211D /* OHHTTPStubs.framework */, DA9C551B1CD9DFA7000A15C6 /* libKIF.a */, DA482C7F1C12582600772FE3 /* Mapbox.framework */, diff --git a/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme b/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme index b1a1db6ba1..2c8de87a0f 100644 --- a/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme +++ b/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0910" + LastUpgradeVersion = "0930" version = "1.7"> <BuildAction parallelizeBuildables = "YES" @@ -40,7 +40,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference @@ -91,7 +90,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/linux/README.md b/platform/linux/README.md index 8b8ac9d089..a8a4d459ee 100644 --- a/platform/linux/README.md +++ b/platform/linux/README.md @@ -6,7 +6,7 @@ We are using Ubuntu for development. While the software should work on other dis This process gives you a Linux desktop app built on a Linux host system. -### Build +### Prerequisites Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you can also use [Clang 3.5+](http://llvm.org/apt/). @@ -15,9 +15,6 @@ Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you ca sudo apt-get install gcc-4.9 g++-4.9 export CXX=g++-4.9 -**Note**: We partially support C++14 because GCC 4.9 does not fully implement the -final draft of the C++14 standard. More information in [DEVELOPING.md](DEVELOPING.md). - Ensure you have git and other build essentials: sudo apt-get install curl git build-essential zlib1g-dev automake \ @@ -38,6 +35,21 @@ Install glfw3 dependencies: x11proto-xf86vidmode-dev libxxf86vm-dev \ libxcursor-dev libxinerama-dev +[Node.js](https://nodejs.org/) 4.2.1 or later is also required. + +[ccache](https://ccache.samba.org) is optional, but improves recompilation performance. + +## Build + +Clone the git repository: + + git clone https://github.com/mapbox/mapbox-gl-native.git + cd mapbox-gl-native + +Note that this repository uses Git submodules. They'll be automatically checked out when you first run a `make` command, +but are not updated automatically. We recommended that you run `git submodule update` after pulling down new commits to +this repository. + Set the environment variable `MAPBOX_ACCESS_TOKEN` to your [Mapbox access token](ACCESS_TOKEN.md): export MAPBOX_ACCESS_TOKEN=MYTOKEN diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index dd5f0af112..53683daef0 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -11,7 +11,22 @@ mason_use(benchmark VERSION 1.2.0) mason_use(icu VERSION 58.1-min-size) mason_use(args VERSION 6.2.0 HEADER_ONLY) -include(cmake/loop-uv.cmake) +add_library(mbgl-loop-uv STATIC + platform/default/async_task.cpp + platform/default/run_loop.cpp + platform/default/timer.cpp +) + +target_include_directories(mbgl-loop-uv + PRIVATE include + PRIVATE src +) + +target_link_libraries(mbgl-loop-uv + PRIVATE mbgl-core +) + +target_add_mason_package(mbgl-loop-uv PUBLIC libuv) macro(mbgl_platform_core) target_add_mason_package(mbgl-core PUBLIC mesa) @@ -110,8 +125,6 @@ macro(mbgl_platform_glfw) PRIVATE mbgl-loop-uv ) - target_add_mason_package(mbgl-glfw PUBLIC libuv) - add_custom_command( TARGET mbgl-glfw POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy @@ -126,8 +139,6 @@ macro(mbgl_platform_render) PRIVATE mbgl-filesource PRIVATE mbgl-loop-uv ) - - target_add_mason_package(mbgl-render PUBLIC libuv) endmacro() @@ -136,8 +147,6 @@ macro(mbgl_platform_offline) PRIVATE mbgl-filesource PRIVATE mbgl-loop-uv ) - - target_add_mason_package(mbgl-offline PUBLIC libuv) endmacro() @@ -160,8 +169,6 @@ macro(mbgl_platform_test) PRIVATE mbgl-filesource PRIVATE mbgl-loop-uv ) - - target_add_mason_package(mbgl-test PUBLIC libuv) endmacro() @@ -180,8 +187,6 @@ macro(mbgl_platform_benchmark) PRIVATE mbgl-filesource PRIVATE mbgl-loop-uv ) - - target_add_mason_package(mbgl-benchmark PUBLIC libuv) endmacro() diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 1285de7c3b..c711f6f352 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -1,14 +1,40 @@ # Changelog for Mapbox Maps SDK for macOS +## master + +### Packaging + +* The minimum deployment target for this SDK is now macOS 10.11.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776)) + +### Style layers + +* Added support for aggregate expressions as input values to `MGL_MATCH` expressions. ([#11866](https://github.com/mapbox/mapbox-gl-native/pull/11866)) + +### Other changes + +* Unknown tokens in URLs are now preserved, rather than replaced with an empty string. ([#11787](https://github.com/mapbox/mapbox-gl-native/issues/11787)) +* Adjusted when and how the camera transition update and finish callbacks are called, fixing recursion bugs. ([#11614](https://github.com/mapbox/mapbox-gl-native/pull/11614)) + ## 0.7.1 ### Style layers -* Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Deprecated `+[NSExpression featurePropertiesVariableExpression]`; use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Added an `-[NSPredicate(MGLAdditions) predicateWithMGLJSONObject:]` method and `NSPredicate.mgl_jsonExpressionObject` property. ([#11810](https://github.com/mapbox/mapbox-gl-native/pull/11810)) +* Added `FIRST`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) +* Inside an expression, casting `nil` to a string turns it into the empty string instead of the string `"null"`. ([#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904)) +* Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) -### Other +### Annotations * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) +* Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) + +### Other changes + +* Reduced per-frame render CPU time. ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) +* Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) +* If English is the first language listed in the user’s Preferred Languages setting, `-[MGLStyle localizeLabelsIntoLocale:]` no longer prioritizes other languages over English. ([#11907](https://github.com/mapbox/mapbox-gl-native/pull/11907)) ## 0.7.0 - April 19, 2018 diff --git a/platform/macos/DEVELOPING.md b/platform/macos/DEVELOPING.md index bac5683266..f4d946c527 100644 --- a/platform/macos/DEVELOPING.md +++ b/platform/macos/DEVELOPING.md @@ -4,24 +4,10 @@ This document explains how to build the Mapbox Maps SDK for macOS from source. I ## Requirements -The Mapbox Maps SDK for macOS and the macosapp demo application run on macOS 10.10.0 or above. - -The Mapbox Maps SDK for macOS requires Xcode 8.0 or above. +See the "Requirements" section in [INSTALL.md](INSTALL.md). ## Building the SDK -1. [Install core dependencies](../../INSTALL.md). -1. Run `make xproj`. -1. Switch to the “dynamic” or “macosapp” scheme. The former builds just the Cocoa framework, while the latter also builds a Cocoa demo application based on it. - -### Packaging builds - -Install [jazzy](https://github.com/realm/jazzy) for generating API documentation: - -```bash -[sudo] gem install jazzy -``` - Build and package the SDK by using one of the following commands: * `make xpackage` builds a dynamic framework in the Debug configuration, including debug symbols. diff --git a/platform/macos/INSTALL.md b/platform/macos/INSTALL.md index c723d3e062..f0fb0278be 100644 --- a/platform/macos/INSTALL.md +++ b/platform/macos/INSTALL.md @@ -6,19 +6,41 @@ This document explains how to build a development version of the Mapbox Maps SDK The Mapbox Maps SDK for macOS requires the macOS 10.10.0 SDK (or above) and Xcode 8.0 (or above). To use this SDK with Xcode 7.3.1, download and use a symbols build from the [releases](https://github.com/mapbox/mapbox-gl-native/releases) page. -### Building the SDK from source - -To build the SDK from source: - -1. [Install core dependencies](../../INSTALL.md). +Before building, follow these steps to install prerequisites: +1. Install [Xcode](https://developer.apple.com/xcode/) +1. Launch Xcode and install any updates +1. Install [Homebrew](http://brew.sh) +1. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org): + ``` + brew install node cmake ccache + ``` +1. Install [xcpretty](https://github.com/supermarin/xcpretty) (optional, used for prettifying command line builds): + ``` + [sudo] gem install xcpretty + ``` 1. Install [jazzy](https://github.com/realm/jazzy) for generating API documentation: - ``` [sudo] gem install jazzy ``` -1. Run `make xpackage`, which produces a `Mapbox.framework` in the `build/macos/pkg/` folder. +### Building the SDK from source + +To build the SDK from source: + +1. Clone the git repository: + ``` + git clone https://github.com/mapbox/mapbox-gl-native.git + cd mapbox-gl-native + ``` + Note that this repository uses Git submodules. They'll be automatically checked out when you first run a `make` command, + but are not updated automatically. We recommended that you run `git submodule update` after pulling down new commits to + this repository. +1. Run: + ``` + make xpackage + ``` + This produces a `Mapbox.framework` in the `build/macos/pkg/` folder. ### Installation diff --git a/platform/macos/Mapbox-macOS-SDK-symbols.podspec b/platform/macos/Mapbox-macOS-SDK-symbols.podspec index 597f97d03c..ef2772336d 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.7.0' + version = '0.7.1' m.name = 'Mapbox-macOS-SDK-symbols' m.version = "#{version}-symbols" @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :osx - m.osx.deployment_target = '10.10' + m.osx.deployment_target = '10.11' m.requires_arc = true diff --git a/platform/macos/Mapbox-macOS-SDK.podspec b/platform/macos/Mapbox-macOS-SDK.podspec index 734ab19390..16b63cb34d 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.7.0' + version = '0.7.1' m.name = 'Mapbox-macOS-SDK' m.version = version @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :osx - m.osx.deployment_target = '10.10' + m.osx.deployment_target = '10.11' m.requires_arc = true diff --git a/platform/macos/app/AppDelegate.m b/platform/macos/app/AppDelegate.m index 0d780424f9..f7b35d0c83 100644 --- a/platform/macos/app/AppDelegate.m +++ b/platform/macos/app/AppDelegate.m @@ -29,7 +29,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesCompleted { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfResourcesCompleted { return [NSSet setWithObjects:@"progress", nil]; } @@ -37,7 +37,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; return self.progress.countOfResourcesCompleted; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesExpected { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfResourcesExpected { return [NSSet setWithObjects:@"progress", nil]; } @@ -45,7 +45,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; return self.progress.countOfResourcesExpected; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfBytesCompleted { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfBytesCompleted { return [NSSet setWithObjects:@"progress", nil]; } @@ -53,7 +53,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; return self.progress.countOfBytesCompleted; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfTilesCompleted { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfTilesCompleted { return [NSSet setWithObjects:@"progress", nil]; } @@ -61,7 +61,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; return self.progress.countOfTilesCompleted; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfTileBytesCompleted { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfTileBytesCompleted { return [NSSet setWithObjects:@"progress", nil]; } @@ -157,11 +157,11 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { // mapboxgl://?center=29.95,-90.066667&zoom=14&bearing=45&pitch=30 NSURL *url = [NSURL URLWithString:[event paramDescriptorForKeyword:keyDirectObject].stringValue]; - NS_MUTABLE_DICTIONARY_OF(NSString *, NSString *) *params = [[NSMutableDictionary alloc] init]; + NSMutableDictionary<NSString *, NSString *> *params = [[NSMutableDictionary alloc] init]; for (NSString *param in [url.query componentsSeparatedByString:@"&"]) { NSArray *parts = [param componentsSeparatedByString:@"="]; if (parts.count >= 2) { - params[parts[0]] = [parts[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + params[parts[0]] = [parts[1] stringByRemovingPercentEncoding]; } } diff --git a/platform/macos/app/MGLStyle+MBXAdditions.h b/platform/macos/app/MGLStyle+MBXAdditions.h index dcaf42af28..29c2cda867 100644 --- a/platform/macos/app/MGLStyle+MBXAdditions.h +++ b/platform/macos/app/MGLStyle+MBXAdditions.h @@ -2,6 +2,6 @@ @interface MGLStyle (MBXAdditions) -@property (nonatomic, strong) NS_ARRAY_OF(__kindof MGLStyleLayer *) *reversedLayers; +@property (nonatomic, strong) NSArray<__kindof MGLStyleLayer *> *reversedLayers; @end diff --git a/platform/macos/app/MGLStyle+MBXAdditions.m b/platform/macos/app/MGLStyle+MBXAdditions.m index be571d8b30..a0773cc2c0 100644 --- a/platform/macos/app/MGLStyle+MBXAdditions.m +++ b/platform/macos/app/MGLStyle+MBXAdditions.m @@ -2,15 +2,15 @@ @implementation MGLStyle (MBXAdditions) -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingReversedLayers { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingReversedLayers { return [NSSet setWithObject:@"layers"]; } -- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers { +- (NSArray<__kindof MGLStyleLayer *> *)reversedLayers { return self.layers.reverseObjectEnumerator.allObjects; } -- (void)setReversedLayers:(NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers { +- (void)setReversedLayers:(NSArray<__kindof MGLStyleLayer *> *)reversedLayers { self.layers = reversedLayers.reverseObjectEnumerator.allObjects; } diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 9b18dbd761..4ee6050565 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -19,7 +19,7 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = { { .latitude = -13.15589555, .longitude = -74.2178961777998 }, }; -NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotation>) *shapes) { +NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *shapes) { NSMutableArray *flattenedShapes = [NSMutableArray arrayWithCapacity:shapes.count]; for (id <MGLAnnotation> shape in shapes) { NSArray *subshapes; @@ -93,9 +93,8 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio BOOL _isTouringWorld; BOOL _isShowingPolygonAndPolylineAnnotations; BOOL _isShowingAnimatedAnnotation; - - // Snapshotter - MGLMapSnapshotter* snapshotter; + + MGLMapSnapshotter *_snapshotter; } #pragma mark Lifecycle @@ -185,17 +184,23 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio options.zoomLevel = self.mapView.zoomLevel; // Create and start the snapshotter - snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options]; - [snapshotter startWithCompletionHandler:^(MGLMapSnapshot *snapshot, NSError *error) { + __weak __typeof__(self) weakSelf = self; + _snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options]; + [_snapshotter startWithCompletionHandler:^(MGLMapSnapshot *snapshot, NSError *error) { + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + if (error) { NSLog(@"Could not load snapshot: %@", error.localizedDescription); } else { // Set the default name for the file and show the panel. NSSavePanel *panel = [NSSavePanel savePanel]; - panel.nameFieldStringValue = [self.mapView.styleURL.lastPathComponent.stringByDeletingPathExtension stringByAppendingPathExtension:@"png"]; + panel.nameFieldStringValue = [strongSelf.mapView.styleURL.lastPathComponent.stringByDeletingPathExtension stringByAppendingPathExtension:@"png"]; panel.allowedFileTypes = [@[(NSString *)kUTTypePNG] arrayByAddingObjectsFromArray:[NSBitmapImageRep imageUnfilteredTypes]]; - [panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) { + [panel beginSheetModalForWindow:strongSelf.window completionHandler:^(NSInteger result) { if (result == NSFileHandlingPanelOKButton) { // Write the contents in the new format. NSURL *fileURL = panel.URL; @@ -232,7 +237,8 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio }]; } - snapshotter = nil; + + strongSelf->_snapshotter = nil; }]; } @@ -340,7 +346,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio } - (void)toggleStyleLayersAtArrangedObjectIndexes:(NSIndexSet *)indices { - NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices]; + NSArray<MGLStyleLayer *> *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices]; BOOL isVisible = layers.firstObject.visible; [self.undoManager registerUndoWithTarget:self handler:^(MapDocument * _Nonnull target) { [target toggleStyleLayersAtArrangedObjectIndexes:indices]; @@ -376,7 +382,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio [self deleteStyleLayersAtArrangedObjectIndexes:indices]; } -- (void)insertStyleLayers:(NS_ARRAY_OF(MGLStyleLayer *) *)layers atArrangedObjectIndexes:(NSIndexSet *)indices { +- (void)insertStyleLayers:(NSArray<MGLStyleLayer *> *)layers atArrangedObjectIndexes:(NSIndexSet *)indices { [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) { [self deleteStyleLayersAtArrangedObjectIndexes:indices]; }]; @@ -396,7 +402,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio } - (void)deleteStyleLayersAtArrangedObjectIndexes:(NSIndexSet *)indices { - NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices]; + NSArray<MGLStyleLayer *> *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices]; [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) { [self insertStyleLayers:layers atArrangedObjectIndexes:indices]; }]; @@ -572,7 +578,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio [self continueWorldTourWithRemainingAnnotations:annotations]; } -- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations { +- (void)continueWorldTourWithRemainingAnnotations:(NSMutableArray<MGLPointAnnotation *> *)annotations { MGLPointAnnotation *nextAnnotation = annotations.firstObject; if (!nextAnnotation || !_isTouringWorld) { _isTouringWorld = NO; @@ -1179,7 +1185,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio return YES; } if (menuItem.action == @selector(takeSnapshot:)) { - return !(snapshotter && [snapshotter isLoading]); + return !(_snapshotter && [_snapshotter isLoading]); } return NO; } @@ -1236,7 +1242,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio #pragma mark NSSharingServicePickerDelegate methods -- (NS_ARRAY_OF(NSSharingService *) *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(NSArray *)items proposedSharingServices:(NS_ARRAY_OF(NSSharingService *) *)proposedServices { +- (NSArray<NSSharingService *> *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(NSArray *)items proposedSharingServices:(NSArray<NSSharingService *> *)proposedServices { NSURL *shareURL = self.shareURL; NSURL *browserURL = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:shareURL]; NSImage *browserIcon = [[NSWorkspace sharedWorkspace] iconForFile:browserURL.path]; diff --git a/platform/macos/app/ko.lproj/Localizable.strings b/platform/macos/app/ko.lproj/Localizable.strings new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/platform/macos/app/ko.lproj/Localizable.strings diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake index e929bb55c6..aa3f17eea1 100644 --- a/platform/macos/config.cmake +++ b/platform/macos/config.cmake @@ -6,7 +6,6 @@ mason_use(benchmark VERSION 1.2.0) mason_use(icu VERSION 58.1-min-size) mason_use(args VERSION 6.2.0 HEADER_ONLY) -include(cmake/loop-uv.cmake) include(cmake/loop-darwin.cmake) macro(mbgl_platform_core) @@ -155,7 +154,5 @@ macro(mbgl_platform_benchmark) endmacro() macro(mbgl_platform_node) - target_link_libraries(mbgl-node - PRIVATE "-Wl,-bind_at_load" - ) + # Define macro to enable this target on this platform endmacro() diff --git a/platform/macos/docs/doc-README.md b/platform/macos/docs/doc-README.md index 8089ae17d1..8ce2df39a4 100644 --- a/platform/macos/docs/doc-README.md +++ b/platform/macos/docs/doc-README.md @@ -1,6 +1,6 @@ # [Mapbox Maps SDK for macOS](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/macos/) -The Mapbox Maps SDK for macOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, Interface Builder, or AppleScript. The SDK takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. +The Mapbox Maps SDK for macOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.11.0 and above using Objective-C, Swift, Interface Builder, or AppleScript. The SDK takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. ![](img/screenshot.jpg) diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 22182c7810..08385a66d9 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -267,7 +267,7 @@ Each property representing a layout or paint attribute is set to an but you create the former using a very different syntax. `NSExpression`’s format string syntax is reminiscent of a spreadsheet formula or an expression in a database query. See the -“[Predicates and Expressions](Predicates and Expressions.md)” guide for an +“[Predicates and Expressions](predicates-and-expressions.html)” guide for an overview of the expression support in this SDK. This SDK no longer supports style functions; use expressions instead. @@ -318,7 +318,7 @@ In style specification | Method, function, or predicate type | Format string syn `number` | | `string` | | `to-boolean` | `boolValue` | -`to-color` | | +`to-color` | | `CAST(var, 'NSColor')` `to-number` | `mgl_numberWithFallbackValues:` | `CAST(zipCode, 'NSNumber')` `to-string` | `stringValue` | `CAST(ele, 'NSString')` `typeof` | | @@ -350,7 +350,7 @@ In style specification | Method, function, or predicate type | Format string syn `upcase` | `uppercase:` | `uppercase('Elysian Fields')` `rgb` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | `rgba` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | -`to-rgba` | | +`to-rgba` | | `CAST(noindex(var), 'NSArray')` `-` | `from:subtract:` | `2 - 1` `*` | `multiply:by:` | `1 * 2` `/` | `divide:by:` | `1 / 2` @@ -405,5 +405,5 @@ In style JSON | In the format string `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` -See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for +See the “[Predicates and Expressions](predicates-and-expressions.html)” guide for a full description of the supported operators and operand types. diff --git a/platform/macos/docs/pod-README.md b/platform/macos/docs/pod-README.md index 97e77673ee..4827124be0 100644 --- a/platform/macos/docs/pod-README.md +++ b/platform/macos/docs/pod-README.md @@ -10,7 +10,7 @@ Put interactive, scalable world maps into your native Cocoa application with the ![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/macos/docs/img/screenshot.jpg) -The Mapbox Maps SDK for macOS is compatible with macOS 10.10.0 and above for Cocoa applications developed in Objective-C, Swift, Interface Builder, or AppleScript. For hybrid applications, consider [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/). +The Mapbox Maps SDK for macOS is compatible with macOS 10.11.0 and above for Cocoa applications developed in Objective-C, Swift, Interface Builder, or AppleScript. For hybrid applications, consider [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/). ## Installation @@ -37,7 +37,7 @@ After running `carthage update`, you’ll find Mapbox.framework in the Carthage/ Create a [Podfile](https://guides.cocoapods.org/syntax/podfile.html) with the following specification: ```rb -platform :osx, '10.10' +platform :osx, '10.11' target 'TargetNameForYourApp' do pod 'Mapbox-iOS-SDK', '~> x.y' diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index e1053324fb..915c1f46a9 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -20,7 +20,7 @@ custom_categories: - Working with GeoJSON Data - Predicates and Expressions - For Style Authors - - Style Layers Using Expressions + - Migrating to Expressions - Tile URL Templates - Info.plist Keys - name: Maps diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 6a97794d85..4cb436fdbf 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 1F7454AB1ED1DDBD00021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */; }; 1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */; }; 1F9EF4061FBA1B0E0063FBB0 /* mapbox_helmet.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 1F9EF4051FBA1B0D0063FBB0 /* mapbox_helmet.pdf */; }; + 1FC481852098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC481842098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h */; }; 30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */; }; 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3508EC651D749D39009B0EE4 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */; }; @@ -37,7 +38,7 @@ 3527428D1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3527428B1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3527428E1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3527428C1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm */; }; 352742A11D4C25BD00A1ECE6 /* MGLStyleValue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3527429E1D4C25BD00A1ECE6 /* MGLStyleValue.mm */; }; - 3529039B1D6C63B80002C7DF /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */; }; + 3529039B1D6C63B80002C7DF /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3529039C1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3529039A1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm */; }; 3537CA741D3F93A600380318 /* MGLStyle_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3537CA731D3F93A600380318 /* MGLStyle_Private.h */; }; 3538AA231D542685008EC33D /* MGLStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3538AA211D542685008EC33D /* MGLStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -62,6 +63,7 @@ 35C6DF871E214C1800ACA483 /* MGLDistanceFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35C6DF861E214C1800ACA483 /* MGLDistanceFormatterTests.m */; }; 35D65C5A1D65AD5500722C23 /* NSDate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D65C581D65AD5500722C23 /* NSDate+MGLAdditions.h */; }; 35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35D65C591D65AD5500722C23 /* NSDate+MGLAdditions.mm */; }; + 3E6465D9206576A900685536 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E6465D7206576A800685536 /* LimeGreenStyleLayer.m */; }; 3EA9317388DC9A0BF46B7674 /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */; }; 3EA93BA38DBB4B814B6C1FCC /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */; }; 4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4031ACFB1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift */; }; @@ -163,7 +165,6 @@ DAA998FB1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; DAA998FC1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */; }; DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */; }; - DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */; }; DAC25FCA200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */; }; DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */; }; DACA8622201920BE00E9693A /* MGLRasterDEMSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA8620201920BE00E9693A /* MGLRasterDEMSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -309,6 +310,7 @@ 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLightTest.mm; sourceTree = "<group>"; }; 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = "<group>"; }; 1F9EF4051FBA1B0D0063FBB0 /* mapbox_helmet.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = mapbox_helmet.pdf; sourceTree = "<group>"; }; + 1FC481842098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLPrivateAdditions.h"; sourceTree = "<group>"; }; 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+MGLAdditions.h"; path = "src/NSImage+MGLAdditions.h"; sourceTree = SOURCE_ROOT; }; 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = "<group>"; }; 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSExpression+MGLAdditions.mm"; sourceTree = "<group>"; }; @@ -348,6 +350,8 @@ 35C6DF861E214C1800ACA483 /* MGLDistanceFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLDistanceFormatterTests.m; path = ../../darwin/test/MGLDistanceFormatterTests.m; sourceTree = "<group>"; }; 35D65C581D65AD5500722C23 /* NSDate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+MGLAdditions.h"; sourceTree = "<group>"; }; 35D65C591D65AD5500722C23 /* NSDate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDate+MGLAdditions.mm"; sourceTree = "<group>"; }; + 3E6465D7206576A800685536 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LimeGreenStyleLayer.m; path = ../../darwin/app/LimeGreenStyleLayer.m; sourceTree = "<group>"; }; + 3E6465D8206576A900685536 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LimeGreenStyleLayer.h; path = ../../darwin/app/LimeGreenStyleLayer.h; sourceTree = "<group>"; }; 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererConfiguration.h; sourceTree = "<group>"; }; 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRendererConfiguration.mm; sourceTree = "<group>"; }; 4031ACFB1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLMapViewDelegateIntegrationTests.swift; sourceTree = "<group>"; }; @@ -520,8 +524,6 @@ DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFillExtrusionStyleLayer.h; sourceTree = "<group>"; }; DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayer.mm; sourceTree = "<group>"; }; DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayerTests.mm; sourceTree = "<group>"; }; - DAB2CCE31DF632ED001B2FE1 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LimeGreenStyleLayer.h; sourceTree = "<group>"; }; - DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LimeGreenStyleLayer.m; sourceTree = "<group>"; }; DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLPrivateAdditions.h"; sourceTree = "<group>"; }; DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = "<group>"; }; DACA8620201920BE00E9693A /* MGLRasterDEMSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLRasterDEMSource.h; sourceTree = "<group>"; }; @@ -637,6 +639,9 @@ DAF2571D201902A500367EF5 /* MGLHillshadeStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHillshadeStyleLayerTests.mm; sourceTree = "<group>"; }; DAFBD0D51E3FA969000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; }; DAFBD0D61E3FA983000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = "<group>"; }; + DAFEB3702093ACBF00A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; }; + DAFEB3722093ACDA00A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Foundation.strings; sourceTree = "<group>"; }; + DAFEB3732093ACE400A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ko; path = ko.lproj/Foundation.stringsdict; sourceTree = "<group>"; }; DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = "<group>"; }; DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = "<group>"; }; DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLExpressionTests.mm; path = ../../darwin/test/MGLExpressionTests.mm; sourceTree = "<group>"; }; @@ -818,8 +823,8 @@ DA839E961CC2E3400062CAFB /* AppDelegate.m */, DAE6C2E31CC3050F00DB3429 /* DroppedPinAnnotation.h */, DAE6C2E41CC3050F00DB3429 /* DroppedPinAnnotation.m */, - DAB2CCE31DF632ED001B2FE1 /* LimeGreenStyleLayer.h */, - DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */, + 3E6465D8206576A900685536 /* LimeGreenStyleLayer.h */, + 3E6465D7206576A800685536 /* LimeGreenStyleLayer.m */, DAE6C2E51CC3050F00DB3429 /* LocationCoordinate2DTransformer.h */, DAE6C2E61CC3050F00DB3429 /* LocationCoordinate2DTransformer.m */, DA839E9B1CC2E3400062CAFB /* MapDocument.h */, @@ -1031,6 +1036,7 @@ 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */, 352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */, 3529039A1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm */, + 1FC481842098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h */, DAE6C3801CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h */, DAE6C3811CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.m */, DAE6C3821CC31E2A00DB3429 /* NSString+MGLAdditions.h */, @@ -1215,6 +1221,7 @@ DAE6C3631CC31E0400DB3429 /* MGLPointAnnotation.h in Headers */, DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */, DAE6C35F1CC31E0400DB3429 /* MGLOfflinePack.h in Headers */, + 1FC481852098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */, DAE6C39C1CC31E2A00DB3429 /* NSString+MGLAdditions.h in Headers */, 3529039B1D6C63B80002C7DF /* NSPredicate+MGLAdditions.h in Headers */, DA8F25971D51CAC70010E6B5 /* MGLVectorTileSource.h in Headers */, @@ -1373,7 +1380,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = MBX; - LastUpgradeCheck = 0920; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = Mapbox; TargetAttributes = { DA839E911CC2E3400062CAFB = { @@ -1421,6 +1428,7 @@ he, da, "pt-PT", + ko, ); mainGroup = DA839E891CC2E3400062CAFB; productRefGroup = DA839E931CC2E3400062CAFB /* Products */; @@ -1486,11 +1494,11 @@ DA839E9D1CC2E3400062CAFB /* MapDocument.m in Sources */, DAE6C2ED1CC3050F00DB3429 /* DroppedPinAnnotation.m in Sources */, DAE6C2EE1CC3050F00DB3429 /* LocationCoordinate2DTransformer.m in Sources */, - DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */, DAE6C2F11CC3050F00DB3429 /* TimeIntervalTransformer.m in Sources */, DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */, DA839E9A1CC2E3400062CAFB /* main.m in Sources */, DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */, + 3E6465D9206576A900685536 /* LimeGreenStyleLayer.m in Sources */, DAE6C2F01CC3050F00DB3429 /* OfflinePackNameValueTransformer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1661,6 +1669,7 @@ DACBC6082011885800C4D7E2 /* he */, DAD88E03202ACF5900AAA536 /* da */, DA934097208562590059919A /* pt-PT */, + DAFEB3702093ACBF00A86A83 /* ko */, ); name = Localizable.strings; sourceTree = "<group>"; @@ -1739,6 +1748,7 @@ DA3389621FA3EDEF001EA329 /* bg */, DACBC6092011888C00C4D7E2 /* he */, DAD88E04202ACF7C00AAA536 /* da */, + DAFEB3722093ACDA00A86A83 /* ko */, ); name = Foundation.strings; sourceTree = "<group>"; @@ -1763,6 +1773,7 @@ DA80E95F1FE84A540065FC9B /* ar */, DAD88E05202ACF8200AAA536 /* da */, DA934098208562870059919A /* pt-PT */, + DAFEB3732093ACE400A86A83 /* ko */, ); name = Foundation.stringsdict; sourceTree = "<group>"; @@ -1784,6 +1795,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -1791,6 +1803,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -1820,7 +1833,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SYMROOT = "$(PROJECT_DIR)/cmake"; @@ -1841,6 +1854,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -1848,6 +1862,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -1871,7 +1886,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SYMROOT = "$(PROJECT_DIR)/cmake"; diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme index 335441cc23..5f1cf773ae 100644 --- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme +++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0920" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -56,7 +56,7 @@ buildForAnalyzing = "YES"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "E933DEF922EF4BC1AD735333" + BlueprintIdentifier = "9EEBD19ABEE247CF8D3CF4C5" BuildableName = "mbgl-test" BlueprintName = "mbgl-test" ReferencedContainer = "container:../../build/macos/mbgl.xcodeproj"> @@ -70,7 +70,7 @@ buildForAnalyzing = "NO"> <BuildableReference BuildableIdentifier = "primary" - BlueprintIdentifier = "1D3EE78DAFFA4A42AFD7160D" + BlueprintIdentifier = "B328AB16ECE141AAAE2A72C8" BuildableName = "mbgl-benchmark" BlueprintName = "mbgl-benchmark" ReferencedContainer = "container:../../build/macos/mbgl.xcodeproj"> @@ -82,7 +82,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference @@ -112,7 +111,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme index 4dadaee743..fc623b22fa 100644 --- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme +++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0920" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -40,7 +40,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference @@ -70,7 +69,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme index aab7486fbe..ce62359388 100644 --- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme +++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0920" + LastUpgradeVersion = "0930" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/platform/macos/src/MGLMapView+IBAdditions.mm b/platform/macos/src/MGLMapView+IBAdditions.mm index eada47ef90..cef2863ee6 100644 --- a/platform/macos/src/MGLMapView+IBAdditions.mm +++ b/platform/macos/src/MGLMapView+IBAdditions.mm @@ -4,7 +4,7 @@ @implementation MGLMapView (IBAdditions) -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL__ { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyleURL__ { return [NSSet setWithObject:@"styleURL"]; } @@ -23,7 +23,7 @@ self.styleURL = url; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLatitude { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingLatitude { return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil]; } @@ -45,7 +45,7 @@ } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLongitude { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingLongitude { return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil]; } @@ -67,7 +67,7 @@ } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsZooming { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsZooming { return [NSSet setWithObject:@"zoomEnabled"]; } @@ -79,7 +79,7 @@ self.zoomEnabled = allowsZooming; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsScrolling { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsScrolling { return [NSSet setWithObject:@"scrollEnabled"]; } @@ -91,7 +91,7 @@ self.scrollEnabled = allowsScrolling; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsRotating { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsRotating { return [NSSet setWithObject:@"rotateEnabled"]; } @@ -103,7 +103,7 @@ self.rotateEnabled = allowsRotating; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsTilting { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsTilting { return [NSSet setWithObject:@"pitchEnabled"]; } diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h index e3de1069a9..824df827ac 100644 --- a/platform/macos/src/MGLMapView.h +++ b/platform/macos/src/MGLMapView.h @@ -443,7 +443,7 @@ MGL_EXPORT IB_DESIGNABLE @param animated `YES` if you want the map region change to be animated, or `NO` if you want the map to display the new region immediately without animations. */ -- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated; +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations animated:(BOOL)animated; /** Sets the visible region so that the map displays the specified annotations with @@ -458,7 +458,7 @@ MGL_EXPORT IB_DESIGNABLE @param animated `YES` if you want the map region change to be animated, or `NO` if you want the map to display the new region immediately without animations. */ -- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated; +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated; /** Returns the camera that best fits the given coordinate bounds. @@ -621,7 +621,7 @@ MGL_EXPORT IB_DESIGNABLE annotations are associated with the map view, the value of this property is `nil`. */ -@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations; +@property (nonatomic, readonly, nullable) NSArray<id <MGLAnnotation>> *annotations; /** Adds an annotation to the map view. @@ -651,7 +651,7 @@ MGL_EXPORT IB_DESIGNABLE must conform to the `MGLAnnotation` protocol. The map view retains each individual annotation object. */ -- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations; +- (void)addAnnotations:(NSArray<id <MGLAnnotation>> *)annotations; /** The complete list of annotations associated with the receiver that are @@ -661,7 +661,7 @@ MGL_EXPORT IB_DESIGNABLE annotations are associated with the map view or if no annotations associated with the map view are currently visible, the value of this property is `nil`. */ -@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *visibleAnnotations; +@property (nonatomic, readonly, nullable) NSArray<id <MGLAnnotation>> *visibleAnnotations; /** Removes an annotation from the map view, deselecting it if it is selected. @@ -686,7 +686,7 @@ MGL_EXPORT IB_DESIGNABLE @param annotations The array of annotation objects to remove. Objects in the array must conform to the `MGLAnnotation` protocol. */ -- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations; +- (void)removeAnnotations:(NSArray<id <MGLAnnotation>> *)annotations; /** Returns a reusable annotation image object associated with its identifier. @@ -712,7 +712,7 @@ MGL_EXPORT IB_DESIGNABLE no annotations associated with the map view are currently visible in the rectangle. */ -- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotationsInRect:(CGRect)rect; +- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotationsInRect:(CGRect)rect; #pragma mark Managing Annotation Selections @@ -729,7 +729,7 @@ MGL_EXPORT IB_DESIGNABLE @note In versions prior to `4.0.0` if the annotation was offscreen it was not selected. */ -@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations; +@property (nonatomic, copy) NSArray<id <MGLAnnotation>> *selectedAnnotations; /** Selects an annotation and displays a callout popover for it. @@ -791,7 +791,7 @@ MGL_EXPORT IB_DESIGNABLE overlays are associated with the map view, the value of this property is empty array. */ -@property (nonatomic, readonly, nonnull) NS_ARRAY_OF(id <MGLOverlay>) *overlays; +@property (nonatomic, readonly, nonnull) NSArray<id <MGLOverlay>> *overlays; /** Adds a single overlay to the map. @@ -811,7 +811,7 @@ MGL_EXPORT IB_DESIGNABLE @param overlays An array of objects, each of which must conform to the `MGLOverlay` protocol. */ -- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays; +- (void)addOverlays:(NSArray<id <MGLOverlay>> *)overlays; /** Removes a single overlay from the map. @@ -831,7 +831,7 @@ MGL_EXPORT IB_DESIGNABLE @param overlays An array of objects, each of which conforms to the `MGLOverlay` protocol. */ -- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays; +- (void)removeOverlays:(NSArray<id <MGLOverlay>> *)overlays; #pragma mark Accessing the Underlying Map Data @@ -847,7 +847,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point NS_SWIFT_NAME(visibleFeatures(at:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point NS_SWIFT_NAME(visibleFeatures(at:)); /** Returns an array of rendered map features that intersect with a given point, @@ -866,7 +866,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); /** Returns an array of rendered map features that intersect with a given point, @@ -928,7 +928,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:)); /** Returns an array of rendered map features that intersect with the given @@ -943,7 +943,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect NS_SWIFT_NAME(visibleFeatures(in:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect NS_SWIFT_NAME(visibleFeatures(in:)); /** Returns an array of rendered map features that intersect with the given @@ -962,7 +962,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); /** Returns an array of rendered map features that intersect with the given @@ -1026,7 +1026,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:)); +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:)); #pragma mark Converting Geographic Coordinates diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 9a9e19c363..bc9ac1e641 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -58,7 +58,7 @@ #import "NSURL+MGLAdditions.h" #import "NSColor+MGLAdditions.h" #import "NSImage+MGLAdditions.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import <QuartzCore/QuartzCore.h> #import <OpenGL/gl.h> @@ -149,7 +149,7 @@ public: @property (nonatomic, readwrite) MGLStyle *style; /// Mapping from reusable identifiers to annotation images. -@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier; +@property (nonatomic) NSMutableDictionary<NSString *, MGLAnnotationImage *> *annotationImagesByIdentifier; /// Currently shown popover representing the selected annotation. @property (nonatomic) NSPopover *calloutForSelectedAnnotation; @@ -611,7 +611,7 @@ public: #pragma mark Style -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyle { return [NSSet setWithObject:@"styleURL"]; } @@ -965,7 +965,7 @@ public: #pragma mark Viewport -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCenterCoordinate { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCenterCoordinate { return [NSSet setWithObjects:@"latitude", @"longitude", @"camera", nil]; } @@ -1015,7 +1015,7 @@ public: _pendingLongitude = pendingLongitude; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomLevel { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingZoomLevel { return [NSSet setWithObject:@"camera"]; } @@ -1083,7 +1083,7 @@ public: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingDirection { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingDirection { return [NSSet setWithObject:@"camera"]; } @@ -1107,7 +1107,7 @@ public: [self setDirection:_mbglMap->getBearing() + delta animated:animated]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCamera { ++ (NSSet<NSString *> *)keyPathsForValuesAffectingCamera { return [NSSet setWithObjects:@"latitude", @"longitude", @"centerCoordinate", @"zoomLevel", @"direction", nil]; } @@ -1765,7 +1765,7 @@ public: #pragma mark Annotations -- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)annotations { +- (nullable NSArray<id <MGLAnnotation>> *)annotations { if (_annotationContextsByAnnotationTag.empty()) { return nil; } @@ -1781,12 +1781,12 @@ public: return [NSArray arrayWithObjects:&annotations[0] count:annotations.size()]; } -- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotations +- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotations { return [self visibleFeaturesInRect:self.bounds]; } -- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotationsInRect:(CGRect)rect +- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotationsInRect:(CGRect)rect { if (_annotationContextsByAnnotationTag.empty()) { @@ -1852,7 +1852,7 @@ public: } } -- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations { +- (void)addAnnotations:(NSArray<id <MGLAnnotation>> *)annotations { if (!annotations) { return; } @@ -1987,7 +1987,7 @@ public: } } -- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations { +- (void)removeAnnotations:(NSArray<id <MGLAnnotation>> *)annotations { if (!annotations) { return; } @@ -2192,12 +2192,12 @@ public: [self didChangeValueForKey:@"selectedAnnotations"]; } -- (NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations { +- (NSArray<id <MGLAnnotation>> *)selectedAnnotations { id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation; return selectedAnnotation ? @[selectedAnnotation] : @[]; } -- (void)setSelectedAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations { +- (void)setSelectedAnnotations:(NSArray<id <MGLAnnotation>> *)selectedAnnotations { if (!selectedAnnotations.count) { return; } @@ -2338,7 +2338,7 @@ public: } } -- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated { +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations animated:(BOOL)animated { CGFloat maximumPadding = 100; CGFloat yPadding = (NSHeight(self.bounds) / 5 <= maximumPadding) ? (NSHeight(self.bounds) / 5) : maximumPadding; CGFloat xPadding = (NSWidth(self.bounds) / 5 <= maximumPadding) ? (NSWidth(self.bounds) / 5) : maximumPadding; @@ -2348,7 +2348,7 @@ public: [self showAnnotations:annotations edgePadding:edgeInsets animated:animated]; } -- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated { +- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated { if ( ! annotations || ! annotations.count) return; mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); @@ -2547,11 +2547,11 @@ public: #pragma mark Overlays -- (nonnull NS_ARRAY_OF(id <MGLOverlay>) *)overlays +- (nonnull NSArray<id <MGLOverlay>> *)overlays { if (self.annotations == nil) { return @[]; } - NS_MUTABLE_ARRAY_OF(id <MGLOverlay>) *mutableOverlays = [NSMutableArray array]; + NSMutableArray<id <MGLOverlay>> *mutableOverlays = [NSMutableArray array]; [self.annotations enumerateObjectsUsingBlock:^(id<MGLAnnotation> _Nonnull annotation, NSUInteger idx, BOOL * _Nonnull stop) { if ([annotation conformsToProtocol:@protocol(MGLOverlay)]) @@ -2567,7 +2567,7 @@ public: [self addOverlays:@[overlay]]; } -- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays +- (void)addOverlays:(NSArray<id <MGLOverlay>> *)overlays { #if DEBUG for (id <MGLOverlay> overlay in overlays) { @@ -2581,7 +2581,7 @@ public: [self removeOverlays:@[overlay]]; } -- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays { +- (void)removeOverlays:(NSArray<id <MGLOverlay>> *)overlays { #if DEBUG for (id <MGLOverlay> overlay in overlays) { NSAssert([overlay conformsToProtocol:@protocol(MGLOverlay)], @"Overlay does not conform to MGLOverlay"); @@ -2662,15 +2662,15 @@ public: #pragma mark Data -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point { +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point { return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:nil]; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers { +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers { return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil]; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { +- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { // Cocoa origin is at the lower-left corner. mbgl::ScreenCoordinate screenCoordinate = { point.x, NSHeight(self.bounds) - point.y }; @@ -2693,15 +2693,15 @@ public: return MGLFeaturesFromMBGLFeatures(features); } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect { +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect { return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:nil]; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers { +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers { return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil]; } -- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { +- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { // Cocoa origin is at the lower-left corner. mbgl::ScreenBox screenBox = { { NSMinX(rect), NSHeight(self.bounds) - NSMaxY(rect) }, diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index fcf41203cf..198998a874 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -62,3 +62,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLAttributionInfo.h" #import "MGLMapSnapshotter.h" #import "NSExpression+MGLAdditions.h" +#import "NSPredicate+MGLAdditions.h" diff --git a/platform/macos/src/NSColor+MGLAdditions.mm b/platform/macos/src/NSColor+MGLAdditions.mm index 8c9086ccf7..c73c1a41b7 100644 --- a/platform/macos/src/NSColor+MGLAdditions.mm +++ b/platform/macos/src/NSColor+MGLAdditions.mm @@ -79,8 +79,14 @@ components.push_back(component.doubleValue / 255.0); } - // Alpha - components.back() *= 255.0; + + if (components.size() < 4) { + components.push_back(1.0); + } else { + // Alpha + components.back() *= 255.0; + } + // macOS 10.12 Sierra and below uses calibrated RGB by default. if ([NSColor redColor].colorSpaceName == NSCalibratedRGBColorSpace) { diff --git a/platform/node/DEVELOPING.md b/platform/node/DEVELOPING.md index b313d75c13..215b06c7bf 100644 --- a/platform/node/DEVELOPING.md +++ b/platform/node/DEVELOPING.md @@ -4,11 +4,13 @@ This document explains how to build the [Node.js](https://nodejs.org/) bindings ## Building -To develop these bindings, you’ll need to build them from source. Building requires [installing all of the basic dependencies needed for Mapbox GL Native](../../INSTALL.md), then running: +To develop these bindings, you’ll need to build them from source. Building requires the prerequisites listed in either +the [macOS](../macos/INSTALL.md#requirements) or [Linux](../linux/README.md#prerequisites) install documentation, depending +on the target platform. - npm install --build-from-source +To compile the Node.js bindings and install module dependencies, from the repository root directory, run: -From the root directory. This will compile the Node.js bindings and install module dependencies. + npm install --build-from-source To recompile just the C++ code while developing, run `make node`. diff --git a/platform/node/README.md b/platform/node/README.md index d19b2a9343..ac5bcd7e8d 100644 --- a/platform/node/README.md +++ b/platform/node/README.md @@ -17,7 +17,8 @@ Run: npm install @mapbox/mapbox-gl-native ``` -Other platforms will fall back to a source compile with `make node`; see INSTALL.md in the repository root directory for prequisites. +Other platforms will fall back to a source compile with `make node`; see [DEVELOPING.md](DEVELOPING.md) for details on +building from source. ## Testing diff --git a/platform/node/index.js b/platform/node/index.js index 5944a0a27d..6f6b33058a 100644 --- a/platform/node/index.js +++ b/platform/node/index.js @@ -2,9 +2,8 @@ // Shim to wrap req.respond while preserving callback-passing API -var mbgl = require('../../lib/mapbox_gl_native.node'); +var mbgl = require('../../lib/mbgl-node.abi-' + process.versions.modules); var constructor = mbgl.Map.prototype.constructor; -var process = require('process'); var Map = function(options) { if (!(options instanceof Object)) { diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp index 27866ccbed..9faa41d8b8 100644 --- a/platform/node/src/node_expression.cpp +++ b/platform/node/src/node_expression.cpp @@ -52,7 +52,7 @@ type::Type parseType(v8::Local<v8::Object> type) { v8::Local<v8::String> Nkey = Nan::New("N").ToLocalChecked(); if (Nan::Has(type, Nkey).FromMaybe(false)) { - N = Nan::Get(type, Nkey).ToLocalChecked()->ToInt32()->Value(); + N = Nan::To<v8::Int32>(Nan::Get(type, Nkey).ToLocalChecked()).ToLocalChecked()->Value(); } return type::Array(itemType, N); } diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index 9b76f0f542..4d89077d64 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -353,6 +353,18 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) { return options; } +class RenderRequest : public Nan::AsyncResource { +public: + RenderRequest(v8::Local<v8::Function> callback_) : AsyncResource("mbgl:RenderRequest") { + callback.Reset(callback_); + } + ~RenderRequest() { + callback.Reset(); + } + + Nan::Persistent<v8::Function> callback; +}; + /** * Render an image from the currently-loaded style * @@ -385,15 +397,16 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowTypeError("Style is not loaded"); } - if (nodeMap->callback) { + if (nodeMap->req) { return Nan::ThrowError("Map is currently rendering an image"); } try { auto options = ParseOptions(Nan::To<v8::Object>(info[0]).ToLocalChecked()); - assert(!nodeMap->callback); + assert(!nodeMap->req); assert(!nodeMap->image.data); - nodeMap->callback = std::make_unique<Nan::Callback>(info[1].As<v8::Function>()); + nodeMap->req = std::make_unique<RenderRequest>(Nan::To<v8::Function>(info[1]).ToLocalChecked()); + nodeMap->startRender(std::move(options)); } catch (mbgl::style::conversion::Error& err) { return Nan::ThrowTypeError(err.message.c_str()); @@ -447,6 +460,12 @@ void NodeMap::startRender(NodeMap::RenderOptions options) { } void NodeMap::renderFinished() { + if (!req) { + // In some situations, the render finishes at the same time as we call cancel. Make sure + // we are only finishing a render once. + return; + } + Nan::HandleScope scope; // We're done with this render call, so we're unrefing so that the loop could close. @@ -457,14 +476,17 @@ void NodeMap::renderFinished() { Unref(); // Move the callback and image out of the way so that the callback can start a new render call. - auto cb = std::move(callback); + auto request = std::move(req); auto img = std::move(image); - assert(cb); + assert(request); // These have to be empty to be prepared for the next render call. - assert(!callback); + assert(!req); assert(!image.data); + v8::Local<v8::Function> callback = Nan::New(request->callback); + v8::Local<v8::Object> target = Nan::New<v8::Object>(); + if (error) { std::string errorMessage; @@ -482,7 +504,7 @@ void NodeMap::renderFinished() { error = nullptr; assert(!error); - cb->Call(1, argv); + request->runInAsyncScope(target, callback, 1, argv); } else if (img.data) { v8::Local<v8::Object> pixels = Nan::NewBuffer( reinterpret_cast<char *>(img.data.get()), img.bytes(), @@ -498,12 +520,12 @@ void NodeMap::renderFinished() { Nan::Null(), pixels }; - cb->Call(2, argv); + request->runInAsyncScope(target, callback, 2, argv); } else { v8::Local<v8::Value> argv[] = { Nan::Error("Didn't get an image") }; - cb->Call(1, argv); + request->runInAsyncScope(target, callback, 1, argv); } } @@ -546,7 +568,7 @@ void NodeMap::Cancel(const Nan::FunctionCallbackInfo<v8::Value>& info) { auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); - if (!nodeMap->callback) return Nan::ThrowError("No render in progress"); + if (!nodeMap->req) return Nan::ThrowError("No render in progress"); try { nodeMap->cancel(); @@ -1192,7 +1214,7 @@ std::unique_ptr<mbgl::AsyncRequest> NodeMap::request(const mbgl::Resource& resou Nan::New<v8::External>(&callback_) }; - auto instance = Nan::New(NodeRequest::constructor)->NewInstance(2, argv); + auto instance = Nan::NewInstance(Nan::New(NodeRequest::constructor), 2, argv).ToLocalChecked(); Nan::Set(instance, Nan::New("url").ToLocalChecked(), Nan::New(resource.url).ToLocalChecked()); Nan::Set(instance, Nan::New("kind").ToLocalChecked(), Nan::New<v8::Integer>(resource.kind)); diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp index 7fe23ad86a..19df095481 100644 --- a/platform/node/src/node_map.hpp +++ b/platform/node/src/node_map.hpp @@ -25,6 +25,8 @@ class NodeMapObserver : public mbgl::MapObserver { void onDidFailLoadingMap(std::exception_ptr) override; }; +class RenderRequest; + class NodeMap : public Nan::ObjectWrap, public mbgl::FileSource { public: @@ -84,7 +86,7 @@ public: std::exception_ptr error; mbgl::PremultipliedImage image; - std::unique_ptr<Nan::Callback> callback; + std::unique_ptr<RenderRequest> req; // Async for delivering the notifications of render completion. uv_async_t *async; diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp index de16710f78..8c26d44583 100644 --- a/platform/node/src/node_request.cpp +++ b/platform/node/src/node_request.cpp @@ -124,7 +124,8 @@ void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>& inf void NodeRequest::Execute() { v8::Local<v8::Value> argv[] = { handle() }; - Nan::MakeCallback(Nan::To<v8::Object>(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 1, argv); + Nan::AsyncResource res("mbgl:execute"); + res.runInAsyncScope(Nan::To<v8::Object>(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 1, argv); } NodeRequest::NodeAsyncRequest::NodeAsyncRequest(NodeRequest* request_) : request(request_) { diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index e0c2475b75..ac00235702 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -1,15 +1,30 @@ { - "query-tests/circle-pitch-scale/viewport-inside-align-map": "https://github.com/mapbox/mapbox-gl-native/issues/10615", - "query-tests/circle-pitch-scale/viewport-inside-align-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/10615", - "query-tests/edge-cases/box-cutting-antimeridian-z0": "https://github.com/mapbox/mapbox-gl-native/issues/11607", - "query-tests/edge-cases/null-island": "https://github.com/mapbox/mapbox-gl-native/issues/11607", + "expression-tests/collator/accent-equals-de": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/accent-lt-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/accent-not-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/base-default-locale": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/base-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/base-gt-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/case-lteq-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/case-not-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/case-omitted-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/comparison-number-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/diacritic-omitted-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/equals-non-string-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/non-object-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/variant-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/variant-gteq-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/is-supported-script/default": "This tests RTL text plugin behavior specific to GL JS", + "expression-tests/resolved-locale/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/to-string/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11719", "query-tests/geometry/multilinestring": "needs investigation", "query-tests/geometry/multipolygon": "needs investigation", "query-tests/geometry/polygon": "needs investigation", - "query-tests/symbol/panned-after-insert": "https://github.com/mapbox/mapbox-gl-native/issues/10408", - "query-tests/symbol/rotated-after-insert": "https://github.com/mapbox/mapbox-gl-native/issues/10408", "query-tests/world-wrapping/box": "skip - needs issue", "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/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/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841", "render-tests/debug/collision-lines": "https://github.com/mapbox/mapbox-gl-native/issues/10412", @@ -27,6 +42,9 @@ "render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327", "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary", "render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js", + "render-tests/icon-rotate/with-offset": "https://github.com/mapbox/mapbox-gl-native/issues/11872", + "render-tests/line-gradient/gradient": "https://github.com/mapbox/mapbox-gl-native/issues/11718", + "render-tests/line-gradient/translucent": "https://github.com/mapbox/mapbox-gl-native/issues/11718", "render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397", "render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195", "render-tests/real-world/bangkok": "https://github.com/mapbox/mapbox-gl-native/issues/10412", @@ -40,6 +58,7 @@ "render-tests/regressions/mapbox-gl-js#5370": "skip - https://github.com/mapbox/mapbox-gl-native/pull/9439", "render-tests/regressions/mapbox-gl-js#5740": "https://github.com/mapbox/mapbox-gl-native/issues/10619", "render-tests/regressions/mapbox-gl-js#5982": "https://github.com/mapbox/mapbox-gl-native/issues/10619", + "render-tests/regressions/mapbox-gl-js#6655": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357", "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", @@ -50,6 +69,7 @@ "render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", "render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732", + "render-tests/text-rotate/with-offset": "https://github.com/mapbox/mapbox-gl-native/issues/11872", "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601", "render-tests/background-color/colorSpace-hcl": "needs issue", "render-tests/combinations/background-opaque--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", @@ -92,5 +112,8 @@ "render-tests/combinations/fill-translucent--fill-extrusion-translucent": "needs investigation", "render-tests/combinations/line-translucent--fill-extrusion-translucent": "needs investigation", "render-tests/combinations/raster-translucent--fill-extrusion-translucent": "needs investigation", - "render-tests/combinations/symbol-translucent--fill-extrusion-translucent": "needs investigation" + "render-tests/combinations/symbol-translucent--fill-extrusion-translucent": "needs investigation", + "render-tests/feature-state/composite-expression": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", + "render-tests/feature-state/data-expression": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", + "render-tests/feature-state/vector-source": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue" } diff --git a/platform/qt/README.md b/platform/qt/README.md index 4d2d887828..f083d4b519 100644 --- a/platform/qt/README.md +++ b/platform/qt/README.md @@ -1,4 +1,4 @@ -# Mapbox Qt SDK +# Mapbox Maps SDK for Qt [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) @@ -9,7 +9,7 @@ available in the Qt SDK since Qt 5.9. Use the [Qt bugtracker](https://bugreports to the plugin and this GitHub repository for bugs related to Mapbox GL Native and the Qt bindings. You should build this repository if you want to develop/contribute using the low level Qt C++ bindings or -want to contribute to the Mapbox GL Native projectusing the Qt port for debugging. +want to contribute to the Mapbox GL Native project using the Qt port for debugging. See the Mapbox Qt landing page for more details: https://www.mapbox.com/qt/ @@ -17,9 +17,7 @@ See the Mapbox Qt landing page for more details: https://www.mapbox.com/qt/ #### Linux -For Linux (tested on Ubuntu) desktop, together with these [build -instructions](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/linux#build), -you also need: +For Linux (tested on Ubuntu) desktop, together with these [build instructions](../linux/README.md), you also need: ``` $ sudo apt-get install qt5-default @@ -55,8 +53,7 @@ At runtime, you will also need installed: ### Build instructions -Public API headers -can be found in the [platform/qt/include](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/qt/include) directory. +Public API headers can be found in the [platform/qt/include](qt/include) directory. #### Linux and macOS diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp index f6d5473192..390d89915a 100644 --- a/platform/qt/app/mapwindow.cpp +++ b/platform/qt/app/mapwindow.cpp @@ -22,6 +22,13 @@ MapWindow::MapWindow(const QMapboxGLSettings &settings) setWindowIcon(QIcon(":icon.png")); } +MapWindow::~MapWindow() +{ + // Make sure we have a valid context so we + // can delete the QMapboxGL. + makeCurrent(); +} + void MapWindow::selfTest() { if (m_bearingAnimation) { diff --git a/platform/qt/app/mapwindow.hpp b/platform/qt/app/mapwindow.hpp index c484114ec0..6c05f03562 100644 --- a/platform/qt/app/mapwindow.hpp +++ b/platform/qt/app/mapwindow.hpp @@ -29,6 +29,7 @@ class MapWindow : public QGLWidget public: MapWindow(const QMapboxGLSettings &); + ~MapWindow(); void selfTest(); diff --git a/platform/qt/config.qdocconf b/platform/qt/config.qdocconf index d6f0edbd32..9d3b8a7ef4 100644 --- a/platform/qt/config.qdocconf +++ b/platform/qt/config.qdocconf @@ -7,7 +7,7 @@ include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf) Cpp.ignoretokens = Q_MAPBOXGL_EXPORT project = QMapboxGL -description = Mapbox Qt SDK +description = Mapbox Maps SDK for Qt language = Cpp outputdir = docs diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp index 369890343f..1ab04403cf 100644 --- a/platform/qt/include/qmapbox.hpp +++ b/platform/qt/include/qmapbox.hpp @@ -9,10 +9,14 @@ // This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style -#if defined(QT_BUILD_MAPBOXGL_LIB) - #define Q_MAPBOXGL_EXPORT Q_DECL_EXPORT +#if !defined(QT_MAPBOXGL_STATIC) +# if defined(QT_BUILD_MAPBOXGL_LIB) +# define Q_MAPBOXGL_EXPORT Q_DECL_EXPORT +# else +# define Q_MAPBOXGL_EXPORT Q_DECL_IMPORT +# endif #else - #define Q_MAPBOXGL_EXPORT Q_DECL_IMPORT +# define Q_MAPBOXGL_EXPORT #endif namespace QMapbox { diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index 70fe270902..79eb672d1f 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -128,6 +128,13 @@ public: MapChangeSourceDidChange }; + enum MapLoadingFailure { + StyleParseFailure, + StyleLoadFailure, + NotFoundFailure, + UnknownFailure + }; + // Determines the orientation of the map. enum NorthOrientation { NorthUpwards, // Default @@ -248,6 +255,7 @@ public slots: signals: void needsRendering(); void mapChanged(QMapboxGL::MapChange); + void mapLoadingFailed(QMapboxGL::MapLoadingFailure, const QString &reason); void copyrightsChanged(const QString ©rightsHtml); void staticRenderFinished(const QString &error); @@ -259,5 +267,6 @@ private: }; Q_DECLARE_METATYPE(QMapboxGL::MapChange); +Q_DECLARE_METATYPE(QMapboxGL::MapLoadingFailure); #endif // QMAPBOXGL_H diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake index d1d597bda6..989618a59a 100644 --- a/platform/qt/qt.cmake +++ b/platform/qt/qt.cmake @@ -145,6 +145,7 @@ elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") add_definitions("-Wno-unused-command-line-argument") add_definitions("-Wno-unused-local-typedef") add_definitions("-Wno-unused-private-field") + add_definitions("-Wno-inconsistent-missing-override") list(APPEND MBGL_QT_CORE_FILES PRIVATE platform/qt/src/thread.cpp diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp index 87a9358772..2180f22d07 100644 --- a/platform/qt/src/qmapbox.cpp +++ b/platform/qt/src/qmapbox.cpp @@ -24,7 +24,7 @@ namespace QMapbox { /*! \namespace QMapbox - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Contains miscellaneous Mapbox bindings used throughout QMapboxGL. */ @@ -74,7 +74,7 @@ namespace QMapbox { /*! \class QMapbox::Feature - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents \l {https://www.mapbox.com/help/define-features/}{map features} via its \a type (PointType, LineStringType or PolygonType), \a geometry, \a @@ -94,7 +94,7 @@ namespace QMapbox { /*! \class QMapbox::ShapeAnnotationGeometry - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a shape annotation geometry. */ @@ -113,7 +113,7 @@ namespace QMapbox { /*! \class QMapbox::SymbolAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt A symbol annotation comprises of its geometry and an icon identifier. */ @@ -121,7 +121,7 @@ namespace QMapbox { /*! \class QMapbox::LineAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a line annotation object, along with its properties. @@ -131,7 +131,7 @@ namespace QMapbox { /*! \class QMapbox::FillAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a fill annotation object, along with its properties. @@ -177,7 +177,7 @@ namespace QMapbox { /*! \class QMapbox::CustomLayerRenderParameters - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapbox::CustomLayerRenderParameters provides the data passed on each render pass for a custom layer. diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 2e736c0aa2..8c3355dc09 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -89,15 +89,33 @@ QThreadStorage<std::shared_ptr<mbgl::util::RunLoop>> loop; std::shared_ptr<mbgl::DefaultFileSource> sharedDefaultFileSource( const std::string& cachePath, const std::string& assetRoot, uint64_t maximumCacheSize) { - static std::weak_ptr<mbgl::DefaultFileSource> weak; - auto fs = weak.lock(); + static std::mutex mutex; + static std::unordered_map<std::string, std::weak_ptr<mbgl::DefaultFileSource>> fileSources; - if (!fs) { - weak = fs = std::make_shared<mbgl::DefaultFileSource>( - cachePath, assetRoot, maximumCacheSize); + std::lock_guard<std::mutex> lock(mutex); + + // Purge entries no longer in use. + for (auto it = fileSources.begin(); it != fileSources.end();) { + if (!it->second.lock()) { + it = fileSources.erase(it); + } else { + ++it; + } + } + + // Return an existing FileSource if available. + auto sharedFileSource = fileSources.find(cachePath); + if (sharedFileSource != fileSources.end()) { + return sharedFileSource->second.lock(); } - return fs; + // New path, create a new FileSource. + auto newFileSource = std::make_shared<mbgl::DefaultFileSource>( + cachePath, assetRoot, maximumCacheSize); + + fileSources[cachePath] = newFileSource; + + return newFileSource; } // Conversion helper functions. @@ -136,15 +154,14 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage \class QMapboxGLSettings \brief The QMapboxGLSettings class stores the initial configuration for QMapboxGL. - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapboxGLSettings is used to configure QMapboxGL at the moment of its creation. Once created, the QMapboxGLSettings of a QMapboxGL can no longer be changed. - Cache-related settings are shared between all QMapboxGL instances because different - maps will share the same cache database file. The first map to configure cache properties - such as size and path will force the configuration to all newly instantiated QMapboxGL - objects. + Cache-related settings are shared between all QMapboxGL instances using the same cache path. + The first map to configure cache properties such as size will force the configuration + to all newly instantiated QMapboxGL objects using the same cache in the same process. \since 4.7 */ @@ -454,7 +471,7 @@ void QMapboxGLSettings::setResourceTransform(const std::function<std::string(con \class QMapboxGL \brief The QMapboxGL class is a Qt wrapper for the Mapbox GL Native engine. - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapboxGL is a Qt friendly version the Mapbox GL Native engine using Qt types and deep integration with Qt event loop. QMapboxGL relies as much as possible @@ -518,6 +535,19 @@ void QMapboxGLSettings::setResourceTransform(const std::function<std::string(con */ /*! + \enum QMapboxGL::MapLoadingFailure + + This enum represents map loading failure type. + + \value StyleParseFailure Failure to parse the style. + \value StyleLoadFailure Failure to load the style data. + \value NotFoundFailure Failure to obtain style resource file. + \value UnknownFailure Unknown map loading failure. + + \sa mapLoadingFailed() +*/ + +/*! \enum QMapboxGL::NorthOrientation This enum sets the orientation of the north bearing. It will directly affect bearing when @@ -1636,6 +1666,13 @@ void QMapboxGL::connectionEstablished() */ /*! + \fn void QMapboxGL::mapLoadingFailed(QMapboxGL::MapLoadingFailure type, const QString &description) + + This signal is emitted when a map loading failure happens. Details of the + failures are provided, including its \a type and textual \a description. +*/ + +/*! \fn void QMapboxGL::copyrightsChanged(const QString ©rightsHtml); This signal is emitted when the copyrights of the current content of the map @@ -1672,6 +1709,7 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin qRegisterMetaType<QMapboxGL::MapChange>("QMapboxGL::MapChange"); connect(m_mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q, SIGNAL(mapChanged(QMapboxGL::MapChange))); + connect(m_mapObserver.get(), SIGNAL(mapLoadingFailed(QMapboxGL::MapLoadingFailure,QString)), q, SIGNAL(mapLoadingFailed(QMapboxGL::MapLoadingFailure,QString))); connect(m_mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q, SIGNAL(copyrightsChanged(QString))); // Setup the Map object diff --git a/platform/qt/src/qmapboxgl_map_observer.cpp b/platform/qt/src/qmapboxgl_map_observer.cpp index e180ed8fda..44cb8c41d5 100644 --- a/platform/qt/src/qmapboxgl_map_observer.cpp +++ b/platform/qt/src/qmapboxgl_map_observer.cpp @@ -2,6 +2,10 @@ #include "qmapboxgl_p.hpp" +#include <mbgl/util/exception.hpp> + +#include <exception> + QMapboxGLMapObserver::QMapboxGLMapObserver(QMapboxGLPrivate *d) : d_ptr(d) { @@ -44,9 +48,30 @@ void QMapboxGLMapObserver::onDidFinishLoadingMap() emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap); } -void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr) +void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr exception) { emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap); + + QMapboxGL::MapLoadingFailure type; + QString description; + + try { + std::rethrow_exception(exception); + } catch (const mbgl::util::StyleParseException& e) { + type = QMapboxGL::MapLoadingFailure::StyleParseFailure; + description = e.what(); + } catch (const mbgl::util::StyleLoadException& e) { + type = QMapboxGL::MapLoadingFailure::StyleLoadFailure; + description = e.what(); + } catch (const mbgl::util::NotFoundException& e) { + type = QMapboxGL::MapLoadingFailure::NotFoundFailure; + description = e.what(); + } catch (const std::exception& e) { + type = QMapboxGL::MapLoadingFailure::UnknownFailure; + description = e.what(); + } + + emit mapLoadingFailed(type, description); } void QMapboxGLMapObserver::onWillStartRenderingFrame() @@ -65,7 +90,7 @@ void QMapboxGLMapObserver::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMo void QMapboxGLMapObserver::onWillStartRenderingMap() { - emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); + emit mapChanged(QMapboxGL::MapChangeWillStartRenderingMap); } void QMapboxGLMapObserver::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) diff --git a/platform/qt/src/qmapboxgl_map_observer.hpp b/platform/qt/src/qmapboxgl_map_observer.hpp index c9d0581a90..98da5b6add 100644 --- a/platform/qt/src/qmapboxgl_map_observer.hpp +++ b/platform/qt/src/qmapboxgl_map_observer.hpp @@ -36,6 +36,7 @@ public: signals: void mapChanged(QMapboxGL::MapChange); + void mapLoadingFailed(QMapboxGL::MapLoadingFailure, const QString &reason); void copyrightsChanged(const QString ©rightsHtml); private: diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp index 80efd6a326..351991f881 100644 --- a/platform/qt/src/sqlite3.cpp +++ b/platform/qt/src/sqlite3.cpp @@ -24,11 +24,11 @@ namespace mapbox { namespace sqlite { // https://www.sqlite.org/rescode.html#ok -static_assert(mbgl::underlying_type(Exception::OK) == 0, "error"); +static_assert(mbgl::underlying_type(ResultCode::OK) == 0, "error"); // https://www.sqlite.org/rescode.html#cantopen -static_assert(mbgl::underlying_type(Exception::CANTOPEN) == 14, "error"); +static_assert(mbgl::underlying_type(ResultCode::CantOpen) == 14, "error"); // https://www.sqlite.org/rescode.html#notadb -static_assert(mbgl::underlying_type(Exception::NOTADB) == 26, "error"); +static_assert(mbgl::underlying_type(ResultCode::NotADB) == 26, "error"); void checkQueryError(const QSqlQuery& query) { QSqlError lastError = query.lastError(); @@ -52,15 +52,6 @@ void checkDatabaseError(const QSqlDatabase &db) { } } -void checkDatabaseOpenError(const QSqlDatabase &db) { - // Assume every error when opening the data as CANTOPEN. Qt - // always returns -1 for `nativeErrorCode()` on database errors. - QSqlError lastError = db.lastError(); - if (lastError.type() != QSqlError::NoError) { - throw Exception { Exception::Code::CANTOPEN, "Error opening the database." }; - } -} - namespace { QString incrementCounter() { static QAtomicInt count = 0; @@ -70,32 +61,9 @@ namespace { class DatabaseImpl { public: - DatabaseImpl(const char* filename, int flags) - : connectionName(QString::number(uint64_t(QThread::currentThread())) + incrementCounter()) + DatabaseImpl(QString connectionName_) + : connectionName(std::move(connectionName_)) { - if (!QSqlDatabase::drivers().contains("QSQLITE")) { - throw Exception { Exception::Code::CANTOPEN, "SQLite driver not found." }; - } - - assert(!QSqlDatabase::contains(connectionName)); - auto db = QSqlDatabase::addDatabase("QSQLITE", connectionName); - - QString connectOptions = db.connectOptions(); - if (flags & OpenFlag::ReadOnly) { - if (!connectOptions.isEmpty()) connectOptions.append(';'); - connectOptions.append("QSQLITE_OPEN_READONLY"); - } - if (flags & OpenFlag::SharedCache) { - if (!connectOptions.isEmpty()) connectOptions.append(';'); - connectOptions.append("QSQLITE_ENABLE_SHARED_CACHE"); - } - - db.setConnectOptions(connectOptions); - db.setDatabaseName(QString(filename)); - - if (!db.open()) { - checkDatabaseOpenError(db); - } } ~DatabaseImpl() { @@ -127,12 +95,51 @@ public: template <typename T> using optional = std::experimental::optional<T>; +mapbox::util::variant<Database, Exception> Database::tryOpen(const std::string &filename, int flags) { + if (!QSqlDatabase::drivers().contains("QSQLITE")) { + return Exception { ResultCode::CantOpen, "SQLite driver not found." }; + } + + QString connectionName = QString::number(uint64_t(QThread::currentThread())) + incrementCounter(); -Database::Database(const std::string& file, int flags) - : impl(std::make_unique<DatabaseImpl>(file.c_str(), flags)) { - assert(impl); + assert(!QSqlDatabase::contains(connectionName)); + auto db = QSqlDatabase::addDatabase("QSQLITE", connectionName); + + QString connectOptions = db.connectOptions(); + if (flags & OpenFlag::ReadOnly) { + if (!connectOptions.isEmpty()) connectOptions.append(';'); + connectOptions.append("QSQLITE_OPEN_READONLY"); + } + if (flags & OpenFlag::SharedCache) { + if (!connectOptions.isEmpty()) connectOptions.append(';'); + connectOptions.append("QSQLITE_ENABLE_SHARED_CACHE"); + } + + db.setConnectOptions(connectOptions); + db.setDatabaseName(QString(filename.c_str())); + + if (!db.open()) { + // Assume every error when opening the data as CANTOPEN. Qt + // always returns -1 for `nativeErrorCode()` on database errors. + return Exception { ResultCode::CantOpen, "Error opening the database." }; + } + + return Database(std::make_unique<DatabaseImpl>(connectionName)); } +Database Database::open(const std::string &filename, int flags) { + auto result = tryOpen(filename, flags); + if (result.is<Exception>()) { + throw result.get<Exception>(); + } else { + return std::move(result.get<Database>()); + } +} + +Database::Database(std::unique_ptr<DatabaseImpl> impl_) + : impl(std::move(impl_)) +{} + Database::Database(Database &&other) : impl(std::move(other.impl)) { assert(impl); @@ -165,7 +172,9 @@ void Database::setBusyTimeout(std::chrono::milliseconds timeout) { } db.setConnectOptions(connectOptions); if (!db.open()) { - checkDatabaseOpenError(db); + // Assume every error when opening the data as CANTOPEN. Qt + // always returns -1 for `nativeErrorCode()` on database errors. + throw Exception { ResultCode::CantOpen, "Error opening the database." }; } } @@ -186,74 +195,82 @@ void Database::exec(const std::string &sql) { } } -Statement Database::prepare(const char *query) { - return Statement(this, query); -} - -Statement::Statement(Database *db, const char *sql) - : impl(std::make_unique<StatementImpl>(QString(sql), QSqlDatabase::database(db->impl->connectionName))) { +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique<StatementImpl>(QString(sql), + QSqlDatabase::database(db.impl->connectionName))) { assert(impl); } -Statement::Statement(Statement &&other) - : impl(std::move(other.impl)) { - assert(impl); +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif } -Statement &Statement::operator=(Statement &&other) { - assert(impl); - std::swap(impl, other.impl); - return *this; +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); + +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() { +Query::~Query() { + reset(); + clearBindings(); + +#ifndef NDEBUG + stmt.used = false; +#endif } -template void Statement::bind(int, int64_t); +template void Query::bind(int, int64_t); template <typename T> -void Statement::bind(int offset, T value) { - assert(impl); +void Query::bind(int offset, T value) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant::fromValue<T>(value), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant::fromValue<T>(value), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, std::nullptr_t) { - assert(impl); +void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, int32_t value) { +void Query::bind(int offset, int32_t value) { bind(offset, static_cast<int64_t>(value)); } template <> -void Statement::bind(int offset, bool value) { +void Query::bind(int offset, bool value) { bind(offset, static_cast<int>(value)); } template <> -void Statement::bind(int offset, int8_t value) { +void Query::bind(int offset, int8_t value) { bind(offset, static_cast<int64_t>(value)); } template <> -void Statement::bind(int offset, uint8_t value) { +void Query::bind(int offset, uint8_t value) { bind(offset, static_cast<int64_t>(value)); } template <> -void Statement::bind(int offset, mbgl::Timestamp value) { +void Query::bind(int offset, mbgl::Timestamp value) { bind(offset, std::chrono::system_clock::to_time_t(value)); } template <> -void Statement::bind(int offset, optional<std::string> value) { +void Query::bind(int offset, optional<std::string> value) { if (value) { bind(offset, *value); } else { @@ -262,7 +279,7 @@ void Statement::bind(int offset, optional<std::string> value) { } template <> -void Statement::bind(int offset, optional<mbgl::Timestamp> value) { +void Query::bind(int offset, optional<mbgl::Timestamp> value) { if (value) { bind(offset, *value); } else { @@ -270,30 +287,25 @@ void Statement::bind(int offset, optional<mbgl::Timestamp> value) { } } -void Statement::bind(int offset, const char* value, std::size_t length, bool retain) { - assert(impl); +void Query::bind(int offset, const char* value, std::size_t length, bool /* retain */) { + assert(stmt.impl); if (length > std::numeric_limits<int>::max()) { // Kept for consistence with the default implementation. throw std::range_error("value too long"); } - // Qt SQLite driver treats QByteArray as blob: we need to explicitly - // declare the variant type as string. - QVariant text(QVariant::Type::String); - text.setValue(retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length)); - // Field numbering starts at 0. - impl->query.bindValue(offset - 1, std::move(text), QSql::In); + stmt.impl->query.bindValue(offset - 1, QString(QByteArray(value, length)), QSql::In); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { - assert(impl); +void Query::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { + assert(stmt.impl); const char* value = reinterpret_cast<const char*>(value_); if (length > std::numeric_limits<int>::max()) { // Kept for consistence with the default implementation. @@ -301,123 +313,123 @@ void Statement::bindBlob(int offset, const void* value_, std::size_t length, boo } // Field numbering starts at 0. - impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : + stmt.impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length), QSql::In | QSql::Binary); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { +void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } -bool Statement::run() { - assert(impl); +bool Query::run() { + assert(stmt.impl); - if (!impl->query.isValid()) { - if (impl->query.exec()) { - impl->lastInsertRowId = impl->query.lastInsertId().value<int64_t>(); - impl->changes = impl->query.numRowsAffected(); + if (!stmt.impl->query.isValid()) { + if (stmt.impl->query.exec()) { + stmt.impl->lastInsertRowId = stmt.impl->query.lastInsertId().value<int64_t>(); + stmt.impl->changes = stmt.impl->query.numRowsAffected(); } else { - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } } - const bool hasNext = impl->query.next(); - if (!hasNext) impl->query.finish(); + const bool hasNext = stmt.impl->query.next(); + if (!hasNext) stmt.impl->query.finish(); return hasNext; } -template bool Statement::get(int); -template int Statement::get(int); -template int64_t Statement::get(int); -template double Statement::get(int); +template bool Query::get(int); +template int Query::get(int); +template int64_t Query::get(int); +template double Query::get(int); -template <typename T> T Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <typename T> T Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return value.value<T>(); } -template <> std::vector<uint8_t> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray byteArray = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::vector<uint8_t> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray byteArray = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); std::vector<uint8_t> blob(byteArray.begin(), byteArray.end()); return blob; } -template <> mbgl::Timestamp Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> mbgl::Timestamp Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return std::chrono::time_point_cast<std::chrono::seconds>( std::chrono::system_clock::from_time_t(value.value<::time_t>())); } -template <> optional<int64_t> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional<int64_t> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value<int64_t>() }; } -template <> optional<double> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional<double> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value<double>() }; } -template <> std::string Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::string Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); return std::string(value.constData(), value.size()); } -template <> optional<std::string> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> optional<std::string> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::string(value.constData(), value.size()) }; } -template <> optional<mbgl::Timestamp> Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional<mbgl::Timestamp> Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::chrono::time_point_cast<mbgl::Seconds>( std::chrono::system_clock::from_time_t(value.value<::time_t>())) }; } -void Statement::reset() { - assert(impl); - impl->query.finish(); +void Query::reset() { + assert(stmt.impl); + stmt.impl->query.finish(); } -void Statement::clearBindings() { +void Query::clearBindings() { // no-op } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - return (impl->changes < 0 ? 0 : impl->changes); +uint64_t Query::changes() const { + assert(stmt.impl); + return (stmt.impl->changes < 0 ? 0 : stmt.impl->changes); } Transaction::Transaction(Database& db_, Mode mode) diff --git a/scripts/generate-shaders.js b/scripts/generate-shaders.js index b1eeffb8a0..6758793056 100755 --- a/scripts/generate-shaders.js +++ b/scripts/generate-shaders.js @@ -7,6 +7,8 @@ const outputPath = 'src/mbgl/shaders'; var shaders = require('../mapbox-gl-js/src/shaders'); +delete shaders.lineGradient; + require('./style-code'); writeIfModified(path.join(outputPath, 'preludes.hpp'), `// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index 6ddb787f19..0059863e05 100755 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -15,10 +15,6 @@ function parseCSSColor(str) { ]; } -global.isDataDriven = function (property) { - return property['property-function'] === true; -}; - global.isLightProperty = function (property) { return property['light-property'] === true; }; @@ -77,30 +73,36 @@ function attributeUniformType(property, type) { } global.layoutPropertyType = function (property) { - if (isDataDriven(property)) { - return `DataDrivenLayoutProperty<${evaluatedType(property)}>`; - } else { - return `LayoutProperty<${evaluatedType(property)}>`; + switch (property['property-type']) { + case 'data-driven': + case 'cross-faded-data-driven': + return `DataDrivenLayoutProperty<${evaluatedType(property)}>`; + default: + return `LayoutProperty<${evaluatedType(property)}>`; } }; global.paintPropertyType = function (property, type) { - if (isDataDriven(property)) { - return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}>`; - } else if (/-pattern$/.test(property.name) || property.name === 'line-dasharray') { - return `CrossFadedPaintProperty<${evaluatedType(property)}>`; - } else { - return `PaintProperty<${evaluatedType(property)}>`; + switch (property['property-type']) { + case 'data-driven': + case 'cross-faded-data-driven': + return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}>`; + case 'cross-faded': + return `CrossFadedPaintProperty<${evaluatedType(property)}>`; + default: + return `PaintProperty<${evaluatedType(property)}>`; } }; global.propertyValueType = function (property) { - if (isDataDriven(property)) { - return `DataDrivenPropertyValue<${evaluatedType(property)}>`; - } else if (property.name === 'heatmap-color') { - return `HeatmapColorPropertyValue`; - } else { - return `PropertyValue<${evaluatedType(property)}>`; + switch (property['property-type']) { + case 'data-driven': + case 'cross-faded-data-driven': + return `DataDrivenPropertyValue<${evaluatedType(property)}>`; + case 'color-ramp': + return `HeatmapColorPropertyValue`; + default: + return `PropertyValue<${evaluatedType(property)}>`; } }; diff --git a/scripts/style-spec.js b/scripts/style-spec.js index 196adc0b32..dd9a127b70 100644 --- a/scripts/style-spec.js +++ b/scripts/style-spec.js @@ -1,3 +1,4 @@ var spec = module.exports = require('../mapbox-gl-js/src/style-spec/reference/v8'); // Make temporary modifications here when Native doesn't have all features that JS has. +delete spec.paint_line['line-gradient']; diff --git a/src/csscolorparser/csscolorparser.cpp b/src/csscolorparser/csscolorparser.cpp index 4d1c6a3d65..106dae6cef 100644 --- a/src/csscolorparser/csscolorparser.cpp +++ b/src/csscolorparser/csscolorparser.cpp @@ -185,7 +185,6 @@ optional<Color> parse(const std::string& css_str) { // Convert to lowercase. std::transform(str.begin(), str.end(), str.begin(), ::tolower); - for (const auto& namedColor : namedColors) { if (str == namedColor.name) { return { namedColor.color }; @@ -262,8 +261,9 @@ optional<Color> parse(const std::string& css_str) { } float h = parseFloat(params[0]) / 360.0f; - while (h < 0.0f) h++; - while (h > 1.0f) h--; + float i; + // Normalize the hue to [0..1[ + h = std::modf(h, &i); // NOTE(deanm): According to the CSS spec s/l should only be // percentages, but we don't bother and let float or percentage. diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index a237100d13..7d776f21c4 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -65,8 +65,8 @@ RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector<Feature> RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const { diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp index e812ec2883..da87d13814 100644 --- a/src/mbgl/annotation/render_annotation_source.hpp +++ b/src/mbgl/annotation/render_annotation_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index c67786274a..cc69a60ca0 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -7,7 +7,6 @@ #include <mbgl/util/math.hpp> #include <mbgl/math/minmax.hpp> #include <mbgl/style/filter.hpp> -#include <mbgl/style/filter_evaluator.hpp> #include <mbgl/tile/tile_id.hpp> #include <mapbox/geometry/envelope.hpp> @@ -25,34 +24,30 @@ FeatureIndex::FeatureIndex(std::unique_ptr<const GeometryTileData> tileData_) void FeatureIndex::insert(const GeometryCollection& geometries, std::size_t index, const std::string& sourceLayerName, - const std::string& bucketName) { + const std::string& bucketLeaderID) { for (const auto& ring : geometries) { auto envelope = mapbox::geometry::envelope(ring); - grid.insert(IndexedSubfeature(index, sourceLayerName, bucketName, sortIndex++), - {convertPoint<float>(envelope.min), convertPoint<float>(envelope.max)}); + if (envelope.min.x < util::EXTENT && + envelope.min.y < util::EXTENT && + envelope.max.x >= 0 && + envelope.max.y >= 0) { + grid.insert(IndexedSubfeature(index, sourceLayerName, bucketLeaderID, sortIndex++), + {convertPoint<float>(envelope.min), convertPoint<float>(envelope.max)}); + } } } -static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) { - return a.sortIndex > b.sortIndex; -} - -static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& b) { - return a.sortIndex < b.sortIndex; -} - void FeatureIndex::query( std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, - const float bearing, + const TransformState& transformState, + const mat4& posMatrix, const double tileSize, const double scale, const RenderedQueryOptions& queryOptions, const UnwrappedTileID& tileID, - const std::string& sourceID, const std::vector<const RenderLayer*>& layers, - const CollisionIndex& collisionIndex, - const float additionalQueryRadius) const { + const float additionalQueryPadding) const { if (!tileData) { return; @@ -60,15 +55,17 @@ void FeatureIndex::query( // Determine query radius const float pixelsToTileUnits = util::EXTENT / tileSize / scale; - const int16_t additionalRadius = std::min<int16_t>(util::EXTENT, additionalQueryRadius * pixelsToTileUnits); + const int16_t additionalPadding = std::min<int16_t>(util::EXTENT, additionalQueryPadding * pixelsToTileUnits); // Query the grid index mapbox::geometry::box<int16_t> box = mapbox::geometry::envelope(queryGeometry); - std::vector<IndexedSubfeature> features = grid.query({ convertPoint<float>(box.min - additionalRadius), - convertPoint<float>(box.max + additionalRadius) }); + std::vector<IndexedSubfeature> features = grid.query({ convertPoint<float>(box.min - additionalPadding), + convertPoint<float>(box.max + additionalPadding) }); - std::sort(features.begin(), features.end(), topDown); + std::sort(features.begin(), features.end(), [](const IndexedSubfeature& a, const IndexedSubfeature& b) { + return a.sortIndex > b.sortIndex; + }); size_t previousSortIndex = std::numeric_limits<size_t>::max(); for (const auto& indexedFeature : features) { @@ -76,25 +73,59 @@ void FeatureIndex::query( if (indexedFeature.sortIndex == previousSortIndex) continue; previousSortIndex = indexedFeature.sortIndex; - addFeature(result, indexedFeature, queryGeometry, queryOptions, tileID.canonical, layers, bearing, pixelsToTileUnits); + addFeature(result, indexedFeature, queryOptions, tileID.canonical, layers, queryGeometry, transformState, pixelsToTileUnits, posMatrix); } +} + +std::unordered_map<std::string, std::vector<Feature>> FeatureIndex::lookupSymbolFeatures(const std::vector<IndexedSubfeature>& symbolFeatures, + const RenderedQueryOptions& queryOptions, + const std::vector<const RenderLayer*>& layers, + const OverscaledTileID& tileID, + const std::shared_ptr<std::vector<size_t>>& featureSortOrder) const { + std::unordered_map<std::string, std::vector<Feature>> result; + if (!tileData) { + return result; + } + std::vector<IndexedSubfeature> sortedFeatures(symbolFeatures.begin(), symbolFeatures.end()); + + std::sort(sortedFeatures.begin(), sortedFeatures.end(), [featureSortOrder](const IndexedSubfeature& a, const IndexedSubfeature& b) { + // Same idea as the non-symbol sort order, but symbol features may have changed their sort order + // since their corresponding IndexedSubfeature was added to the CollisionIndex + // The 'featureSortOrder' is relatively inefficient for querying but cheap to build on every bucket sort + if (featureSortOrder) { + // queryRenderedSymbols documentation says we'll return features in + // "top-to-bottom" rendering order (aka last-to-first). + // Actually there can be multiple symbol instances per feature, so + // we sort each feature based on the first matching symbol instance. + auto sortedA = std::find(featureSortOrder->begin(), featureSortOrder->end(), a.index); + auto sortedB = std::find(featureSortOrder->begin(), featureSortOrder->end(), b.index); + assert(sortedA != featureSortOrder->end()); + assert(sortedB != featureSortOrder->end()); + return sortedA > sortedB; + } else { + // Bucket hasn't been re-sorted based on angle, so use same "reverse of appearance in source data" + // logic as non-symboles + return a.sortIndex > b.sortIndex; + } + }); - std::vector<IndexedSubfeature> symbolFeatures = collisionIndex.queryRenderedSymbols(queryGeometry, tileID, sourceID); - std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols); - for (const auto& symbolFeature : symbolFeatures) { - addFeature(result, symbolFeature, queryGeometry, queryOptions, tileID.canonical, layers, bearing, pixelsToTileUnits); + for (const auto& symbolFeature : sortedFeatures) { + mat4 unusedMatrix; + addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), {}, 0, unusedMatrix); } + return result; } void FeatureIndex::addFeature( std::unordered_map<std::string, std::vector<Feature>>& result, const IndexedSubfeature& indexedFeature, - const GeometryCoordinates& queryGeometry, const RenderedQueryOptions& options, const CanonicalTileID& tileID, const std::vector<const RenderLayer*>& layers, - const float bearing, - const float pixelsToTileUnits) const { + const GeometryCoordinates& queryGeometry, + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4& posMatrix) const { auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* { for (const auto& layer : layers) { @@ -109,7 +140,7 @@ void FeatureIndex::addFeature( std::unique_ptr<GeometryTileLayer> sourceLayer; std::unique_ptr<GeometryTileFeature> geometryTileFeature; - for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) { + for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketLeaderID)) { const RenderLayer* renderLayer = getRenderLayer(layerID); if (!renderLayer) { continue; @@ -124,7 +155,7 @@ void FeatureIndex::addFeature( } if (!renderLayer->is<RenderSymbolLayer>() && - !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) { + !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, transformState, pixelsToTileUnits, posMatrix)) { continue; } @@ -158,8 +189,8 @@ optional<GeometryCoordinates> FeatureIndex::translateQueryGeometry( return translated; } -void FeatureIndex::setBucketLayerIDs(const std::string& bucketName, const std::vector<std::string>& layerIDs) { - bucketLayerIDs[bucketName] = layerIDs; +void FeatureIndex::setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector<std::string>& layerIDs) { + bucketLayerIDs[bucketLeaderID] = layerIDs; } } // namespace mbgl diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index 9e0c172342..739c1f282f 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -5,6 +5,7 @@ #include <mbgl/tile/tile_id.hpp> #include <mbgl/util/grid_index.hpp> #include <mbgl/util/feature.hpp> +#include <mbgl/util/mat4.hpp> #include <vector> #include <string> @@ -14,6 +15,7 @@ namespace mbgl { class RenderedQueryOptions; class RenderLayer; +class TransformState; class CollisionIndex; @@ -23,29 +25,26 @@ public: IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_) : index(index_) , sourceLayerName(std::move(sourceLayerName_)) - , bucketName(std::move(bucketName_)) + , bucketLeaderID(std::move(bucketName_)) , sortIndex(sortIndex_) - , tileID(0, 0, 0) + , bucketInstanceId(0) {} - IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_, - std::string sourceID_, CanonicalTileID tileID_) - : index(index_) - , sourceLayerName(std::move(sourceLayerName_)) - , bucketName(std::move(bucketName_)) - , sortIndex(std::move(sortIndex_)) - , sourceID(std::move(sourceID_)) - , tileID(std::move(tileID_)) - {} + IndexedSubfeature(const IndexedSubfeature& other, uint32_t bucketInstanceId_) + : index(other.index) + , sourceLayerName(other.sourceLayerName) + , bucketLeaderID(other.bucketLeaderID) + , sortIndex(other.sortIndex) + , bucketInstanceId(bucketInstanceId_) + {} size_t index; std::string sourceLayerName; - std::string bucketName; + std::string bucketLeaderID; size_t sortIndex; // Only used for symbol features - std::string sourceID; - CanonicalTileID tileID; + uint32_t bucketInstanceId; }; class FeatureIndex { @@ -54,20 +53,19 @@ public: const GeometryTileData* getData() { return tileData.get(); } - void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketName); + void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketLeaderID); void query( std::unordered_map<std::string, std::vector<Feature>>& result, const GeometryCoordinates& queryGeometry, - const float bearing, + const TransformState&, + const mat4& posMatrix, const double tileSize, const double scale, const RenderedQueryOptions& options, const UnwrappedTileID&, - const std::string&, const std::vector<const RenderLayer*>&, - const CollisionIndex&, - const float additionalQueryRadius) const; + const float additionalQueryPadding) const; static optional<GeometryCoordinates> translateQueryGeometry( const GeometryCoordinates& queryGeometry, @@ -76,18 +74,26 @@ public: const float bearing, const float pixelsToTileUnits); - void setBucketLayerIDs(const std::string& bucketName, const std::vector<std::string>& layerIDs); + void setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector<std::string>& layerIDs); + + std::unordered_map<std::string, std::vector<Feature>> lookupSymbolFeatures( + const std::vector<IndexedSubfeature>& symbolFeatures, + const RenderedQueryOptions& options, + const std::vector<const RenderLayer*>& layers, + const OverscaledTileID& tileID, + const std::shared_ptr<std::vector<size_t>>& featureSortOrder) const; private: void addFeature( std::unordered_map<std::string, std::vector<Feature>>& result, const IndexedSubfeature&, - const GeometryCoordinates& queryGeometry, const RenderedQueryOptions& options, const CanonicalTileID&, const std::vector<const RenderLayer*>&, - const float bearing, - const float pixelsToTileUnits) const; + const GeometryCoordinates& queryGeometry, + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4& posMatrix) const; GridIndex<IndexedSubfeature> grid; unsigned int sortIndex = 0; diff --git a/src/mbgl/gl/attribute.cpp b/src/mbgl/gl/attribute.cpp index bb5b2ddc34..b2d05fe665 100644 --- a/src/mbgl/gl/attribute.cpp +++ b/src/mbgl/gl/attribute.cpp @@ -1,14 +1,20 @@ #include <mbgl/gl/attribute.hpp> +#include <mbgl/gl/context.hpp> #include <mbgl/gl/gl.hpp> namespace mbgl { namespace gl { -void bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) { - if (location >= MAX_ATTRIBUTES) { - throw gl::Error("too many vertex attributes"); +void bindAttributeLocation(Context& context, ProgramID id, AttributeLocation location, const char* name) { + // We're using sequentially numberered attribute locations starting with 0. Therefore we can use + // the location as a proxy for the number of attributes. + if (location >= context.maximumVertexBindingCount) { + // Don't bind the location on this hardware since it exceeds the limit (otherwise we'd get + // an OpenGL error). This means we'll see rendering errors, and possibly slow rendering due + // to unbound attributes. + } else { + MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name)); } - MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name)); } std::set<std::string> getActiveAttributes(ProgramID id) { diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp index fa6c2ddeab..3763f0a583 100644 --- a/src/mbgl/gl/attribute.hpp +++ b/src/mbgl/gl/attribute.hpp @@ -17,8 +17,6 @@ namespace mbgl { namespace gl { -static constexpr std::size_t MAX_ATTRIBUTES = 8; - template <class> struct DataTypeOf; template <> struct DataTypeOf< int8_t> : std::integral_constant<DataType, DataType::Byte> {}; template <> struct DataTypeOf<uint8_t> : std::integral_constant<DataType, DataType::UnsignedByte> {}; @@ -45,7 +43,7 @@ public: } }; -using AttributeBindingArray = std::array<optional<AttributeBinding>, MAX_ATTRIBUTES>; +using AttributeBindingArray = std::vector<optional<AttributeBinding>>; /* gl::Attribute<T,N> manages the binding of a vertex buffer to a GL program attribute. @@ -214,7 +212,8 @@ const std::size_t Vertex<A1, A2, A3, A4, A5>::attributeOffsets[5] = { } // namespace detail -void bindAttributeLocation(ProgramID, AttributeLocation, const char * name); +class Context; +void bindAttributeLocation(Context&, ProgramID, AttributeLocation, const char * name); std::set<std::string> getActiveAttributes(ProgramID); template <class... As> @@ -231,13 +230,13 @@ public: using Vertex = detail::Vertex<typename As::Type...>; - static Locations bindLocations(const ProgramID& id) { + static Locations bindLocations(Context& context, const ProgramID& id) { std::set<std::string> activeAttributes = getActiveAttributes(id); AttributeLocation location = 0; auto maybeBindLocation = [&](const char* name) -> optional<AttributeLocation> { if (activeAttributes.count(name)) { - bindAttributeLocation(id, location, name); + bindAttributeLocation(context, id, location, name); return location++; } else { return {}; @@ -277,6 +276,7 @@ public: static AttributeBindingArray toBindingArray(const Locations& locations, const Bindings& bindings) { AttributeBindingArray result; + result.resize(sizeof...(As)); auto maybeAddBinding = [&] (const optional<AttributeLocation>& location, const optional<AttributeBinding>& binding) { @@ -289,6 +289,12 @@ public: return result; } + + static uint32_t activeBindingCount(const Bindings& bindings) { + uint32_t result = 0; + util::ignore({ ((result += bool(bindings.template get<As>())), 0)... }); + return result; + } }; namespace detail { diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index ba44adb42b..c6352d3e84 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -94,7 +94,13 @@ static_assert(underlying_type(BufferUsage::DynamicDraw) == GL_DYNAMIC_DRAW, "Ope static_assert(std::is_same<BinaryProgramFormat, GLenum>::value, "OpenGL type mismatch"); -Context::Context() = default; +Context::Context() + : maximumVertexBindingCount([] { + GLint value; + MBGL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value)); + return value; + }()) { +} Context::~Context() { if (cleanupOnDestruction) { @@ -351,7 +357,7 @@ VertexArray Context::createVertexArray() { VertexArrayID id = 0; MBGL_CHECK_ERROR(vertexArray->genVertexArrays(1, &id)); UniqueVertexArray vao(std::move(id), { this }); - return { UniqueVertexArrayState(new VertexArrayState(std::move(vao), *this), VertexArrayStateDeleter { true })}; + return { UniqueVertexArrayState(new VertexArrayState(std::move(vao)), VertexArrayStateDeleter { true })}; } else { // On GL implementations which do not support vertex arrays, attribute bindings are global state. // So return a VertexArray which shares our global state tracking and whose deleter is a no-op. diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 67624288e2..d95311115e 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -226,7 +226,7 @@ public: State<value::BindVertexBuffer> vertexBuffer; State<value::BindVertexArray, const Context&> bindVertexArray { *this }; - VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }), *this }; + VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }) }; State<value::PixelStorePack> pixelStorePack; State<value::PixelStoreUnpack> pixelStoreUnpack; @@ -239,6 +239,8 @@ public: #endif // MBGL_USE_GLES2 bool supportsHalfFloatTextures = false; + const uint32_t maximumVertexBindingCount; + static constexpr const uint32_t minimumRequiredVertexBindingCount = 8; private: State<value::StencilFunc> stencilFunc; diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index af02ad3d54..f33501cd11 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -35,7 +35,7 @@ public: context.createProgram(context.createShader(ShaderType::Vertex, vertexSource), context.createShader(ShaderType::Fragment, fragmentSource))), uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))), - attributeLocations(Attributes::bindLocations(program)) { + attributeLocations(Attributes::bindLocations(context, program)) { // Re-link program after manually binding only active attributes in Attributes::bindLocations context.linkProgram(program); diff --git a/src/mbgl/gl/vertex_array.cpp b/src/mbgl/gl/vertex_array.cpp index 68a500ac45..d552a8292e 100644 --- a/src/mbgl/gl/vertex_array.cpp +++ b/src/mbgl/gl/vertex_array.cpp @@ -9,7 +9,11 @@ void VertexArray::bind(Context& context, BufferID indexBuffer, const AttributeBi context.bindVertexArray = state->vertexArray; state->indexBuffer = indexBuffer; - for (AttributeLocation location = 0; location < MAX_ATTRIBUTES; ++location) { + state->bindings.reserve(bindings.size()); + for (AttributeLocation location = 0; location < bindings.size(); ++location) { + if (state->bindings.size() <= location) { + state->bindings.emplace_back(context, AttributeLocation(location)); + } state->bindings[location] = bindings[location]; } } diff --git a/src/mbgl/gl/vertex_array.hpp b/src/mbgl/gl/vertex_array.hpp index 46c67017bb..604754f672 100644 --- a/src/mbgl/gl/vertex_array.hpp +++ b/src/mbgl/gl/vertex_array.hpp @@ -15,9 +15,8 @@ class Context; class VertexArrayState { public: - VertexArrayState(UniqueVertexArray vertexArray_, Context& context) - : vertexArray(std::move(vertexArray_)), - bindings(makeBindings(context, std::make_index_sequence<MAX_ATTRIBUTES>())) { + VertexArrayState(UniqueVertexArray vertexArray_) + : vertexArray(std::move(vertexArray_)) { } void setDirty() { @@ -31,13 +30,7 @@ public: State<value::BindElementBuffer> indexBuffer; using AttributeState = State<value::VertexAttribute, Context&, AttributeLocation>; - std::array<AttributeState, MAX_ATTRIBUTES> bindings; - -private: - template <std::size_t... I> - std::array<AttributeState, MAX_ATTRIBUTES> makeBindings(Context& context, std::index_sequence<I...>) { - return {{ AttributeState { context, I }... }}; - } + std::vector<AttributeState> bindings; }; class VertexArrayStateDeleter { diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 82a9255824..4249ffc24e 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -2,7 +2,6 @@ #include <mbgl/layout/merge_lines.hpp> #include <mbgl/layout/clip_lines.hpp> #include <mbgl/renderer/buckets/symbol_bucket.hpp> -#include <mbgl/style/filter_evaluator.hpp> #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/renderer/image_atlas.hpp> @@ -42,7 +41,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, std::unique_ptr<GeometryTileLayer> sourceLayer_, ImageDependencies& imageDependencies, GlyphDependencies& glyphDependencies) - : bucketName(layers.at(0)->getID()), + : bucketLeaderID(layers.at(0)->getID()), sourceLayer(std::move(sourceLayer_)), overscaling(parameters.tileID.overscaleFactor()), zoom(parameters.tileID.overscaledZ), @@ -181,8 +180,7 @@ bool SymbolLayout::hasSymbolInstances() const { } void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions, - const ImageMap& imageMap, const ImagePositions& imagePositions, - const OverscaledTileID& tileID, const std::string& sourceID) { + const ImageMap& imageMap, const ImagePositions& imagePositions) { const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && layout.get<SymbolPlacement>() == SymbolPlacementType::Line; @@ -253,7 +251,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph // if either shapedText or icon position is present, add the feature if (shapedTextOrientations.first || shapedIcon) { - addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap, tileID, sourceID); + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap); } feature.geometry.clear(); @@ -266,9 +264,7 @@ void SymbolLayout::addFeature(const std::size_t index, const SymbolFeature& feature, const std::pair<Shaping, Shaping>& shapedTextOrientations, optional<PositionedIcon> shapedIcon, - const GlyphPositionMap& glyphPositionMap, - const OverscaledTileID& tileID, - const std::string& sourceID) { + const GlyphPositionMap& glyphPositionMap) { const float minScale = 0.5f; const float glyphSize = 24.0f; @@ -297,8 +293,7 @@ void SymbolLayout::addFeature(const std::size_t index, : layout.get<SymbolPlacement>(); const float textRepeatDistance = symbolSpacing / 2; - IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size(), - sourceID, tileID.canonical); + 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 @@ -424,7 +419,7 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(const bool showCollisionBoxes) const bool mayOverlap = layout.get<TextAllowOverlap>() || layout.get<IconAllowOverlap>() || layout.get<TextIgnorePlacement>() || layout.get<IconIgnorePlacement>(); - auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, std::move(symbolInstances)); + auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, bucketLeaderID, std::move(symbolInstances)); for (SymbolInstance &symbolInstance : bucket->symbolInstances) { diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 6951c29ada..43b577859f 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -34,8 +34,7 @@ public: GlyphDependencies&); void prepare(const GlyphMap&, const GlyphPositions&, - const ImageMap&, const ImagePositions&, - const OverscaledTileID&, const std::string&); + const ImageMap&, const ImagePositions&); std::unique_ptr<SymbolBucket> place(const bool showCollisionBoxes); @@ -44,7 +43,7 @@ public: std::map<std::string, std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>> layerPaintProperties; - const std::string bucketName; + const std::string bucketLeaderID; std::vector<SymbolInstance> symbolInstances; private: @@ -52,9 +51,7 @@ private: const SymbolFeature&, const std::pair<Shaping, Shaping>& shapedTextOrientations, optional<PositionedIcon> shapedIcon, - const GlyphPositionMap&, - const OverscaledTileID&, - const std::string&); + const GlyphPositionMap&); bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&); std::map<std::u16string, std::vector<Anchor>> compareText; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index a7beb0f1e2..d81544eed5 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -747,6 +747,11 @@ void Map::Impl::onInvalidate() { } void Map::Impl::onUpdate() { + // Don't load/render anything in still mode until explicitly requested. + if (mode != MapMode::Continuous && !stillImageRequest) { + return; + } + TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max(); transform.updateTransitions(timePoint); diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 105adf0400..da8e243d91 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -594,13 +594,10 @@ void Transform::startTransition(const CameraOptions& camera, animation.transitionFrameFn(t); } observer.onCameraIsChanging(); + return false; } else { - transitionFinishFn(); - transitionFinishFn = nullptr; - - // This callback gets destroyed here, - // we can only return after this point. - transitionFrameFn = nullptr; + // Indicate that we need to terminate this transition + return true; } }; @@ -615,7 +612,14 @@ void Transform::startTransition(const CameraOptions& camera, }; if (!isAnimated) { - transitionFrameFn(Clock::now()); + auto update = std::move(transitionFrameFn); + auto finish = std::move(transitionFinishFn); + + transitionFrameFn = nullptr; + transitionFinishFn = nullptr; + + update(Clock::now()); + finish(); } } @@ -624,8 +628,43 @@ bool Transform::inTransition() const { } void Transform::updateTransitions(const TimePoint& now) { - if (transitionFrameFn) { - transitionFrameFn(now); + + // Use a temporary function to ensure that the transitionFrameFn lambda is + // called only once per update. + + // This addresses the symptoms of https://github.com/mapbox/mapbox-gl-native/issues/11180 + // where setting a shape source to nil (or similar) in the `onCameraIsChanging` + // observer function causes `Map::Impl::onUpdate()` to be called which + // in turn calls this function (before the current iteration has completed), + // leading to an infinite loop. See https://github.com/mapbox/mapbox-gl-native/issues/5833 + // for a similar, related, issue. + // + // By temporarily nulling the `transitionFrameFn` (and then restoring it + // after the temporary has been called) we stop this recursion. + // + // It's important to note that the scope of this change is stop the above + // crashes. It doesn't address any potential deeper issue (for example + // user error, how often and when transition callbacks are called). + + auto transition = std::move(transitionFrameFn); + transitionFrameFn = nullptr; + + if (transition && transition(now)) { + // If the transition indicates that it is complete, then we should call + // the finish lambda (going via a temporary as above) + auto finish = std::move(transitionFinishFn); + + transitionFinishFn = nullptr; + transitionFrameFn = nullptr; + + if (finish) { + finish(); + } + } else if (!transitionFrameFn) { + // We have to check `transitionFrameFn` is nil here, since a new transition + // may have been triggered in a user callback (from the transition call + // above) + transitionFrameFn = std::move(transition); } } diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index d429c57661..bff44a2dcd 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -165,7 +165,7 @@ private: TimePoint transitionStart; Duration transitionDuration; - std::function<void(const TimePoint)> transitionFrameFn; + std::function<bool(const TimePoint)> transitionFrameFn; std::function<void()> transitionFinishFn; }; diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index 18d2c24aee..a85b251fb4 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -421,4 +421,17 @@ float TransformState::getCameraToTileDistance(const UnwrappedTileID& tileID) con return projectedCenter[3]; } +float TransformState::maxPitchScaleFactor() const { + if (size.isEmpty()) { + return {}; + } + auto latLng = screenCoordinateToLatLng({ 0, static_cast<float>(getSize().height) }); + mat4 mat = coordinatePointMatrix(getZoom()); + Point<double> pt = Projection::project(latLng, scale) / double(util::tileSize); + vec4 p = {{ pt.x, pt.y, 0, 1 }}; + vec4 topPoint; + matrix::transformMat4(topPoint, p, mat); + return topPoint[3] / getCameraToCenterDistance(); +} + } // namespace mbgl diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 451802034d..b6f8ae4424 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -87,6 +87,7 @@ public: } float getCameraToTileDistance(const UnwrappedTileID&) const; + float maxPitchScaleFactor() const; private: bool rotatedNorth() const; diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp index bcdb270b9c..4d5de05337 100644 --- a/src/mbgl/programs/program.hpp +++ b/src/mbgl/programs/program.hpp @@ -46,26 +46,38 @@ public: Shaders::fragmentSource)) { } + static typename AllUniforms::Values computeAllUniformValues( + const UniformValues& uniformValues, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::PossiblyEvaluated& currentProperties, + float currentZoom) { + return uniformValues + .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); + } + + static typename Attributes::Bindings computeAllAttributeBindings( + const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::PossiblyEvaluated& currentProperties) { + return LayoutAttributes::bindings(layoutVertexBuffer) + .concat(paintPropertyBinders.attributeBindings(currentProperties)); + } + + static uint32_t activeBindingCount(const typename Attributes::Bindings& allAttributeBindings) { + return Attributes::activeBindingCount(allAttributeBindings); + } + template <class DrawMode> void draw(gl::Context& context, DrawMode drawMode, gl::DepthMode depthMode, gl::StencilMode stencilMode, gl::ColorMode colorMode, - const UniformValues& uniformValues, - const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer, const gl::IndexBuffer<DrawMode>& indexBuffer, const SegmentVector<Attributes>& segments, - const PaintPropertyBinders& paintPropertyBinders, - const typename PaintProperties::PossiblyEvaluated& currentProperties, - float currentZoom, + const typename AllUniforms::Values& allUniformValues, + const typename Attributes::Bindings& allAttributeBindings, const std::string& layerID) { - typename AllUniforms::Values allUniformValues = uniformValues - .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); - - typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer) - .concat(paintPropertyBinders.attributeBindings(currentProperties)); - for (auto& segment : segments) { auto vertexArrayIt = segment.vertexArrays.find(layerID); diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 9b5037ed9f..51f9a48f7f 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -278,35 +278,45 @@ public: Shaders::fragmentSource)) { } + static typename AllUniforms::Values computeAllUniformValues( + const UniformValues& uniformValues, + const SymbolSizeBinder& symbolSizeBinder, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::PossiblyEvaluated& currentProperties, + float currentZoom) { + return uniformValues.concat(symbolSizeBinder.uniformValues(currentZoom)) + .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); + } + + static typename Attributes::Bindings computeAllAttributeBindings( + const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer, + const gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>& dynamicLayoutVertexBuffer, + const gl::VertexBuffer<SymbolOpacityAttributes::Vertex>& opacityVertexBuffer, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::PossiblyEvaluated& currentProperties) { + assert(layoutVertexBuffer.vertexCount == dynamicLayoutVertexBuffer.vertexCount && + layoutVertexBuffer.vertexCount == opacityVertexBuffer.vertexCount); + return LayoutAttributes::bindings(layoutVertexBuffer) + .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer)) + .concat(SymbolOpacityAttributes::bindings(opacityVertexBuffer)) + .concat(paintPropertyBinders.attributeBindings(currentProperties)); + } + + static uint32_t activeBindingCount(const typename Attributes::Bindings& allAttributeBindings) { + return Attributes::activeBindingCount(allAttributeBindings); + } + template <class DrawMode> void draw(gl::Context& context, DrawMode drawMode, gl::DepthMode depthMode, gl::StencilMode stencilMode, gl::ColorMode colorMode, - const UniformValues& uniformValues, - const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer, - const gl::VertexBuffer<SymbolDynamicLayoutAttributes::Vertex>& dynamicLayoutVertexBuffer, - const gl::VertexBuffer<SymbolOpacityAttributes::Vertex>& opacityVertexBuffer, - const SymbolSizeBinder& symbolSizeBinder, const gl::IndexBuffer<DrawMode>& indexBuffer, const SegmentVector<Attributes>& segments, - const PaintPropertyBinders& paintPropertyBinders, - const typename PaintProperties::PossiblyEvaluated& currentProperties, - float currentZoom, + const typename AllUniforms::Values& allUniformValues, + const typename Attributes::Bindings& allAttributeBindings, const std::string& layerID) { - typename AllUniforms::Values allUniformValues = uniformValues - .concat(symbolSizeBinder.uniformValues(currentZoom)) - .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); - - typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer) - .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer)) - .concat(SymbolOpacityAttributes::bindings(opacityVertexBuffer)) - .concat(paintPropertyBinders.attributeBindings(currentProperties)); - - assert(layoutVertexBuffer.vertexCount == dynamicLayoutVertexBuffer.vertexCount && - layoutVertexBuffer.vertexCount == opacityVertexBuffer.vertexCount); - for (auto& segment : segments) { auto vertexArrayIt = segment.vertexArrays.find(layerID); diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp index 5e2c937091..4fcc761280 100644 --- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp @@ -84,7 +84,7 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, if (nVertices == 0) continue; - auto edgeDistance = 0; + std::size_t edgeDistance = 0; for (uint32_t i = 0; i < nVertices; i++) { const auto& p1 = ring[i]; @@ -102,7 +102,7 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, const Point<double> perp = util::unit(util::perp(d1 - d2)); const auto dist = util::dist<int16_t>(d1, d2); - if (dist > std::numeric_limits<int16_t>::max()) { + if (edgeDistance + dist > std::numeric_limits<int16_t>::max()) { edgeDistance = 0; } diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 60e8a0b504..4fe03eb453 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -18,11 +18,13 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo bool sdfIcons_, bool iconsNeedLinear_, bool sortFeaturesByY_, + const std::string bucketName_, const std::vector<SymbolInstance>&& symbolInstances_) : layout(std::move(layout_)), sdfIcons(sdfIcons_), iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()), sortFeaturesByY(sortFeaturesByY_), + bucketLeaderID(std::move(bucketName_)), symbolInstances(std::move(symbolInstances_)), textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())), iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) { @@ -200,8 +202,12 @@ void SymbolBucket::sortFeatures(const float angle) { text.triangles.clear(); icon.triangles.clear(); + featureSortOrder = std::make_unique<std::vector<size_t>>(); + featureSortOrder->reserve(symbolInstanceIndexes.size()); + for (auto i : symbolInstanceIndexes) { const SymbolInstance& symbolInstance = symbolInstances[i]; + featureSortOrder->push_back(symbolInstance.featureIndex); if (symbolInstance.placedTextIndex) { addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]); diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index ed8afb052c..e4aaf5ba30 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -47,6 +47,7 @@ public: bool sdfIcons, bool iconsNeedLinear, bool sortFeaturesByY, + const std::string bucketLeaderID, const std::vector<SymbolInstance>&&); void upload(gl::Context&) override; @@ -64,6 +65,8 @@ public: const bool iconsNeedLinear; const bool sortFeaturesByY; + const std::string bucketLeaderID; + optional<float> sortedAngle; bool staticUploaded = false; @@ -131,6 +134,8 @@ public: uint32_t bucketInstanceId = 0; bool justReloaded = false; + + std::shared_ptr<std::vector<size_t>> featureSortOrder; }; } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index aebc4cc9aa..2dc5fe7339 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -7,6 +7,7 @@ #include <mbgl/programs/programs.hpp> #include <mbgl/programs/background_program.hpp> #include <mbgl/util/tile_cover.hpp> +#include <mbgl/map/transform_state.hpp> namespace mbgl { @@ -49,6 +50,35 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { const Properties<>::PossiblyEvaluated properties; const BackgroundProgram::PaintPropertyBinders paintAttributeData(properties, 0); + auto draw = [&](auto& program, auto&& uniformValues) { + const auto allUniformValues = program.computeAllUniformValues( + std::move(uniformValues), + paintAttributeData, + properties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = program.computeAllAttributeBindings( + parameters.staticData.tileVertexBuffer, + paintAttributeData, + properties + ); + + checkRenderability(parameters, program.activeBindingCount(allAttributeBindings)); + + program.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + allUniformValues, + allAttributeBindings, + getID() + ); + }; + if (!evaluated.get<BackgroundPattern>().to.empty()) { optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from); optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().to); @@ -59,12 +89,8 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { parameters.imageManager.bind(parameters.context, 0); for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { - parameters.programs.backgroundPattern.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + draw( + parameters.programs.backgroundPattern, BackgroundPatternUniforms::values( parameters.matrixForTile(tileID), evaluated.get<BackgroundOpacity>(), @@ -74,36 +100,18 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { evaluated.get<BackgroundPattern>(), tileID, parameters.state - ), - parameters.staticData.tileVertexBuffer, - parameters.staticData.quadTriangleIndexBuffer, - parameters.staticData.tileTriangleSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), - getID() + ) ); } } else { for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { - parameters.programs.background.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + draw( + parameters.programs.background, BackgroundProgram::UniformValues { uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) }, uniforms::u_color::Value{ evaluated.get<BackgroundColor>() }, uniforms::u_opacity::Value{ evaluated.get<BackgroundOpacity>() }, - }, - parameters.staticData.tileVertexBuffer, - parameters.staticData.quadTriangleIndexBuffer, - parameters.staticData.tileTriangleSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), - getID() + } ); } } diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp index 6092ff5452..b433a9d3fa 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.cpp +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -59,14 +59,11 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) { assert(dynamic_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl))); CircleBucket& bucket = *reinterpret_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl)); - parameters.programs.circle.get(evaluated).draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - parameters.mapMode != MapMode::Continuous - ? parameters.stencilModeForClipping(tile.clip) - : gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + auto& programInstance = parameters.programs.circle.get(evaluated); + + const auto allUniformValues = programInstance.computeAllUniformValues( CircleProgram::UniformValues { uniforms::u_matrix::Value{ tile.translatedMatrix(evaluated.get<CircleTranslate>(), @@ -82,38 +79,104 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) { uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }, uniforms::u_pitch_with_map::Value{ pitchWithMap } }, + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + parameters.mapMode != MapMode::Continuous + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), *bucket.indexBuffer, bucket.segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); } } +GeometryCoordinate projectPoint(const GeometryCoordinate& p, const mat4& posMatrix, const Size& size) { + vec4 pos = {{ static_cast<double>(p.x), static_cast<double>(p.y), 0, 1 }}; + matrix::transformMat4(pos, pos, posMatrix); + return { + static_cast<int16_t>((static_cast<float>(pos[0] / pos[3]) + 1) * size.width * 0.5), + static_cast<int16_t>((static_cast<float>(pos[1] / pos[3]) + 1) * size.height * 0.5) + }; +} + +GeometryCoordinates projectQueryGeometry(const GeometryCoordinates& queryGeometry, const mat4& posMatrix, const Size& size) { + GeometryCoordinates projectedGeometry; + for (auto& p : queryGeometry) { + projectedGeometry.push_back(projectPoint(p, posMatrix, size)); + } + return projectedGeometry; +} + bool RenderCircleLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4& posMatrix) const { // Translate query geometry - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + const GeometryCoordinates& translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get<style::CircleTranslate>(), evaluated.get<style::CircleTranslateAnchor>(), - bearing, - pixelsToTileUnits); + transformState.getAngle(), + pixelsToTileUnits).value_or(queryGeometry); // Evaluate functions - auto radius = evaluated.evaluate<style::CircleRadius>(zoom, feature) * pixelsToTileUnits; - auto stroke = evaluated.evaluate<style::CircleStrokeWidth>(zoom, feature) * pixelsToTileUnits; + auto radius = evaluated.evaluate<style::CircleRadius>(zoom, feature); + auto stroke = evaluated.evaluate<style::CircleStrokeWidth>(zoom, feature); + auto size = radius + stroke; + + // For pitch-alignment: map, compare feature geometry to query geometry in the plane of the tile + // Otherwise, compare geometry in the plane of the viewport + // A circle with fixed scaling relative to the viewport gets larger in tile space as it moves into the distance + // A circle with fixed scaling relative to the map gets smaller in viewport space as it moves into the distance + bool alignWithMap = evaluated.evaluate<style::CirclePitchAlignment>(zoom, feature) == AlignmentType::Map; + const GeometryCoordinates& transformedQueryGeometry = alignWithMap ? + translatedQueryGeometry : + projectQueryGeometry(translatedQueryGeometry, posMatrix, transformState.getSize()); + auto transformedSize = alignWithMap ? size * pixelsToTileUnits : size; + + auto geometry = feature.getGeometries(); + for (auto& ring : geometry) { + for (auto& point : ring) { + const GeometryCoordinate& transformedPoint = alignWithMap ? point : projectPoint(point, posMatrix, transformState.getSize()); + + float adjustedSize = transformedSize; + vec4 center = {{ static_cast<double>(point.x), static_cast<double>(point.y), 0, 1 }}; + matrix::transformMat4(center, center, posMatrix); + auto pitchScale = evaluated.evaluate<style::CirclePitchScale>(zoom, feature); + auto pitchAlignment = evaluated.evaluate<style::CirclePitchAlignment>(zoom, feature); + if (pitchScale == CirclePitchScaleType::Viewport && pitchAlignment == AlignmentType::Map) { + adjustedSize *= center[3] / transformState.getCameraToCenterDistance(); + } else if (pitchScale == CirclePitchScaleType::Map && pitchAlignment == AlignmentType::Viewport) { + adjustedSize *= transformState.getCameraToCenterDistance() / center[3]; + } + + if (util::polygonIntersectsBufferedPoint(transformedQueryGeometry, transformedPoint, adjustedSize)) return true; + } + } - // Test intersection - return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), radius + stroke); + return false; } } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp index f31715f98f..c9eeae4652 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.hpp +++ b/src/mbgl/renderer/layers/render_circle_layer.hpp @@ -20,8 +20,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index fbd6160e8a..d5282c9b0d 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -68,24 +68,53 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource* parameters.context.setStencilMode(gl::StencilMode::disabled()); parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {}); + auto draw = [&](auto& programInstance, const auto& tileBucket, auto&& uniformValues) { + const auto& paintPropertyBinders = tileBucket.paintPropertyBinders.at(getID()); + + const auto allUniformValues = programInstance.computeAllUniformValues( + std::move(uniformValues), + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( + *tileBucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + *tileBucket.indexBuffer, + tileBucket.triangleSegments, + allUniformValues, + allAttributeBindings, + getID()); + }; + if (evaluated.get<FillExtrusionPattern>().from.empty()) { for (const RenderTile& tile : renderTiles) { assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); FillExtrusionBucket& bucket = *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); - parameters.programs.fillExtrusion.get(evaluated).draw( - parameters.context, gl::Triangles(), - parameters.depthModeFor3D(gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + draw( + parameters.programs.fillExtrusion.get(evaluated), + bucket, FillExtrusionUniforms::values( tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), evaluated.get<FillExtrusionTranslateAnchor>(), parameters.state), - parameters.state, parameters.evaluatedLight), - *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, - bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), - getID()); + parameters.state, + parameters.evaluatedLight + ) + ); } } else { optional<ImagePosition> imagePosA = @@ -104,10 +133,9 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource* FillExtrusionBucket& bucket = *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); - parameters.programs.fillExtrusionPattern.get(evaluated).draw( - parameters.context, gl::Triangles(), - parameters.depthModeFor3D(gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + draw( + parameters.programs.fillExtrusionPattern.get(evaluated), + bucket, FillExtrusionPatternUniforms::values( tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), evaluated.get<FillExtrusionTranslateAnchor>(), @@ -115,10 +143,9 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource* parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB, evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state, -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, - parameters.evaluatedLight), - *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, - bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), - getID()); + parameters.evaluatedLight + ) + ); } } @@ -131,19 +158,39 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource* matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); const Properties<>::PossiblyEvaluated properties; + const ExtrusionTextureProgram::PaintPropertyBinders paintAttributeData{ properties, 0 }; + + auto& programInstance = parameters.programs.extrusionTexture; - parameters.programs.extrusionTexture.draw( - parameters.context, gl::Triangles(), gl::DepthMode::disabled(), - gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + const auto allUniformValues = programInstance.computeAllUniformValues( ExtrusionTextureProgram::UniformValues{ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, uniforms::u_image::Value{ 0 }, - uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } }, + uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( parameters.staticData.extrusionTextureVertexBuffer, + paintAttributeData, + properties + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + gl::DepthMode::disabled(), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.extrusionTextureSegments, - ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, - parameters.state.getZoom(), getID()); + allUniformValues, + allAttributeBindings, + getID()); } } @@ -151,14 +198,15 @@ bool RenderFillExtrusionLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4&) const { auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get<style::FillExtrusionTranslate>(), evaluated.get<style::FillExtrusionTranslateAnchor>(), - bearing, + transformState.getAngle(), pixelsToTileUnits); return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp index 838494cf91..f7ba13c267 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp @@ -22,8 +22,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index 22cb9563c1..c59ca6f906 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -69,12 +69,11 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { const auto& depthMode, const auto& indexBuffer, const auto& segments) { - program.get(evaluated).draw( - parameters.context, - drawMode, - depthMode, - parameters.stencilModeForClipping(tile.clip), - parameters.colorModeForRenderPass(), + auto& programInstance = program.get(evaluated); + + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + const auto allUniformValues = programInstance.computeAllUniformValues( FillProgram::UniformValues { uniforms::u_matrix::Value{ tile.translatedMatrix(evaluated.get<FillTranslate>(), @@ -83,12 +82,28 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { }, uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, }, + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), indexBuffer, segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; @@ -139,12 +154,11 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { const auto& depthMode, const auto& indexBuffer, const auto& segments) { - program.get(evaluated).draw( - parameters.context, - drawMode, - depthMode, - parameters.stencilModeForClipping(tile.clip), - parameters.colorModeForRenderPass(), + auto& programInstance = program.get(evaluated); + + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + const auto allUniformValues = programInstance.computeAllUniformValues( FillPatternUniforms::values( tile.translatedMatrix(evaluated.get<FillTranslate>(), evaluated.get<FillTranslateAnchor>(), @@ -157,12 +171,28 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { tile.id, parameters.state ), + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), indexBuffer, segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; @@ -188,14 +218,15 @@ bool RenderFillLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4&) const { auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get<style::FillTranslate>(), evaluated.get<style::FillTranslateAnchor>(), - bearing, + transformState.getAngle(), pixelsToTileUnits); return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp index a51865698f..bd195fb828 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_layer.hpp @@ -20,8 +20,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index 4f2e899220..dec9edb318 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -92,23 +92,38 @@ void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { ? parameters.stencilModeForClipping(tile.clip) : gl::StencilMode::disabled(); - parameters.programs.heatmap.get(evaluated).draw( + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + auto& programInstance = parameters.programs.heatmap.get(evaluated); + + const auto allUniformValues = programInstance.computeAllUniformValues( + HeatmapProgram::UniformValues { + uniforms::u_intensity::Value{ evaluated.get<style::HeatmapIntensity>() }, + uniforms::u_matrix::Value{ tile.matrix }, + uniforms::heatmap::u_extrude_scale::Value{ extrudeScale } + }, + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( + *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( parameters.context, gl::Triangles(), parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), stencilMode, gl::ColorMode::additive(), - HeatmapProgram::UniformValues { - uniforms::u_intensity::Value{evaluated.get<style::HeatmapIntensity>()}, - uniforms::u_matrix::Value{tile.matrix}, - uniforms::heatmap::u_extrude_scale::Value{extrudeScale} - }, - *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); } @@ -123,20 +138,41 @@ void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); const Properties<>::PossiblyEvaluated properties; + const HeatmapTextureProgram::PaintPropertyBinders paintAttributeData{ properties, 0 }; + + auto& programInstance = parameters.programs.heatmapTexture; - parameters.programs.heatmapTexture.draw( - parameters.context, gl::Triangles(), gl::DepthMode::disabled(), - gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + const auto allUniformValues = programInstance.computeAllUniformValues( HeatmapTextureProgram::UniformValues{ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, uniforms::u_image::Value{ 0 }, uniforms::u_color_ramp::Value{ 1 }, - uniforms::u_opacity::Value{ evaluated.get<HeatmapOpacity>() } }, + uniforms::u_opacity::Value{ evaluated.get<HeatmapOpacity>() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( parameters.staticData.extrusionTextureVertexBuffer, + paintAttributeData, + properties + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + gl::DepthMode::disabled(), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.extrusionTextureSegments, - HeatmapTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, - parameters.state.getZoom(), getID()); + allUniformValues, + allAttributeBindings, + getID() + ); } } @@ -165,12 +201,12 @@ bool RenderHeatmapLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState&, + const float pixelsToTileUnits, + const mat4&) const { (void) queryGeometry; (void) feature; (void) zoom; - (void) bearing; (void) pixelsToTileUnits; return false; } diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.hpp b/src/mbgl/renderer/layers/render_heatmap_layer.hpp index 3f0b1f91b4..29fad7d8b8 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.hpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.hpp @@ -22,8 +22,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; void updateColorRamp(); diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp index bcfd4ffe99..411305edf4 100644 --- a/src/mbgl/renderer/layers/render_hillshade_layer.cpp +++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp @@ -69,12 +69,11 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src const auto& indexBuffer, const auto& segments, const UnwrappedTileID& id) { - parameters.programs.hillshade.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + auto& programInstance = parameters.programs.hillshade; + + const HillshadeProgram::PaintPropertyBinders paintAttributeData{ evaluated, 0 }; + + const auto allUniformValues = programInstance.computeAllUniformValues( HillshadeProgram::UniformValues { uniforms::u_matrix::Value{ matrix }, uniforms::u_image::Value{ 0 }, @@ -84,12 +83,28 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src uniforms::u_light::Value{ getLight(parameters) }, uniforms::u_latrange::Value{ getLatRange(id) }, }, + paintAttributeData, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( vertexBuffer, + paintAttributeData, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), indexBuffer, segments, - HillshadeProgram::PaintPropertyBinders { evaluated, 0 }, - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; @@ -112,13 +127,11 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src parameters.context.bindTexture(*bucket.dem, 0, gl::TextureFilter::Nearest, gl::TextureMipMap::No, gl::TextureWrap::Clamp, gl::TextureWrap::Clamp); const Properties<>::PossiblyEvaluated properties; + const HillshadePrepareProgram::PaintPropertyBinders paintAttributeData{ properties, 0 }; - parameters.programs.hillshadePrepare.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + auto& programInstance = parameters.programs.hillshadePrepare; + + const auto allUniformValues = programInstance.computeAllUniformValues( HillshadePrepareProgram::UniformValues { uniforms::u_matrix::Value { mat }, uniforms::u_dimension::Value { {{uint16_t(tilesize * 2), uint16_t(tilesize * 2) }} }, @@ -126,12 +139,28 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src uniforms::u_maxzoom::Value{ float(maxzoom) }, uniforms::u_image::Value{ 0 } }, + paintAttributeData, + properties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( parameters.staticData.rasterVertexBuffer, + paintAttributeData, + properties + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.rasterSegments, - HillshadePrepareProgram::PaintPropertyBinders { properties, 0 }, - properties, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); bucket.texture = std::move(view.getTexture()); diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index 1b4a1c0ff7..361ad0c76b 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -62,19 +62,34 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { LineBucket& bucket = *reinterpret_cast<LineBucket*>(tile.tile.getBucket(*baseImpl)); auto draw = [&] (auto& program, auto&& uniformValues) { - program.get(evaluated).draw( + auto& programInstance = program.get(evaluated); + + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + const auto allUniformValues = programInstance.computeAllUniformValues( + std::move(uniformValues), + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( + *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( parameters.context, gl::Triangles(), parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), parameters.stencilModeForClipping(tile.clip), parameters.colorModeForRenderPass(), - std::move(uniformValues), - *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; @@ -162,15 +177,16 @@ bool RenderLineLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4&) const { // Translate query geometry auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get<style::LineTranslate>(), evaluated.get<style::LineTranslateAnchor>(), - bearing, + transformState.getAngle(), pixelsToTileUnits); // Evaluate function diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 8bf7e2329d..5d5d79c044 100644 --- a/src/mbgl/renderer/layers/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -29,8 +29,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp index b41b2ac560..f202ed4ebb 100644 --- a/src/mbgl/renderer/layers/render_raster_layer.cpp +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -73,16 +73,15 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source if (parameters.pass != RenderPass::Translucent) return; + RasterProgram::PaintPropertyBinders paintAttributeData{ evaluated, 0 }; + auto draw = [&] (const mat4& matrix, const auto& vertexBuffer, const auto& indexBuffer, const auto& segments) { - parameters.programs.raster.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + auto& programInstance = parameters.programs.raster; + + const auto allUniformValues = programInstance.computeAllUniformValues( RasterProgram::UniformValues { uniforms::u_matrix::Value{ matrix }, uniforms::u_image0::Value{ 0 }, @@ -98,12 +97,28 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source uniforms::u_scale_parent::Value{ 1.0f }, uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} }, }, + paintAttributeData, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( vertexBuffer, + paintAttributeData, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), indexBuffer, segments, - RasterProgram::PaintPropertyBinders { evaluated, 0 }, - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 9e493003c0..e48c0e2f92 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -88,7 +88,26 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { const auto& binders, const auto& paintProperties) { - program.get(paintProperties).draw( + auto& programInstance = program.get(paintProperties); + + const auto allUniformValues = programInstance.computeAllUniformValues( + std::move(uniformValues), + *symbolSizeBinder, + binders, + paintProperties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( + *buffers.vertexBuffer, + *buffers.dynamicVertexBuffer, + *buffers.opacityVertexBuffer, + binders, + paintProperties + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( parameters.context, gl::Triangles(), values_.pitchAlignment == AlignmentType::Map @@ -96,16 +115,10 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { : gl::DepthMode::disabled(), gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), - std::move(uniformValues), - *buffers.vertexBuffer, - *buffers.dynamicVertexBuffer, - *buffers.opacityVertexBuffer, - *symbolSizeBinder, *buffers.indexBuffer, buffers.segments, - binders, - paintProperties, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp index bcdc175f14..a667d5837e 100644 --- a/src/mbgl/renderer/render_layer.cpp +++ b/src/mbgl/renderer/render_layer.cpp @@ -9,8 +9,10 @@ #include <mbgl/renderer/layers/render_raster_layer.hpp> #include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/renderer/layers/render_heatmap_layer.hpp> +#include <mbgl/renderer/paint_parameters.hpp> #include <mbgl/style/types.hpp> #include <mbgl/renderer/render_tile.hpp> +#include <mbgl/util/logging.hpp> namespace mbgl { @@ -73,5 +75,32 @@ void RenderLayer::setRenderTiles(std::vector<std::reference_wrapper<RenderTile>> renderTiles = std::move(tiles); } +void RenderLayer::checkRenderability(const PaintParameters& parameters, + const uint32_t activeBindingCount) { + // Only warn once for every layer. + if (hasRenderFailures) { + return; + } + + if (activeBindingCount > parameters.context.maximumVertexBindingCount) { + Log::Error(Event::OpenGL, + "The layer '%s' uses more data-driven properties than the current device " + "supports, and will have rendering errors. To ensure compatibility with this " + "device, use %d fewer data driven properties in this layer.", + getID().c_str(), + activeBindingCount - parameters.context.minimumRequiredVertexBindingCount); + hasRenderFailures = true; + } else if (activeBindingCount > parameters.context.minimumRequiredVertexBindingCount) { + Log::Error(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 " + "data-driven properties in this layer.", + getID().c_str(), + activeBindingCount - parameters.context.minimumRequiredVertexBindingCount); + hasRenderFailures = true; + } +} + } //namespace mbgl diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index 55831cb72c..3e2f1d7525 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -4,6 +4,7 @@ #include <mbgl/style/layer_impl.hpp> #include <mbgl/style/layer_type.hpp> #include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/util/mat4.hpp> #include <memory> #include <string> @@ -17,6 +18,7 @@ class PropertyEvaluationParameters; class PaintParameters; class RenderSource; class RenderTile; +class TransformState; class RenderLayer { protected: @@ -69,8 +71,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const { return false; }; + const mat4&) const { return false; }; virtual std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const = 0; @@ -82,6 +85,11 @@ public: friend std::string layoutKey(const RenderLayer&); protected: + // Checks whether the current hardware can render this layer. If it can't, we'll show a warning + // in the console to inform the developer. + void checkRenderability(const PaintParameters&, uint32_t activeBindingCount); + +protected: // renderTiles are exposed directly to CrossTileSymbolIndex and Placement so they // can update opacities in the symbol buckets immediately before rendering friend class CrossTileSymbolIndex; @@ -92,6 +100,12 @@ protected: // Stores what render passes this layer is currently enabled for. This depends on the // evaluated StyleProperties object and is updated accordingly. RenderPass passes = RenderPass::None; + +private: + // Some layers may not render correctly on some hardware when the vertex attribute limit of + // that GPU is exceeded. More attributes are used when adding many data driven paint properties + // to a layer. + bool hasRenderFailures = false; }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index 53519c763e..dc80cb1dc6 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -65,7 +65,7 @@ public: const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const = 0; + const mat4& projMatrix) const = 0; virtual std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const = 0; diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index 35b34833e4..64790938ef 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -74,6 +74,8 @@ void RenderTile::finishRender(PaintParameters& parameters) { static const style::Properties<>::PossiblyEvaluated properties {}; static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0); + auto& program = parameters.programs.debug; + if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) { if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() || tile.debugBucket->complete != tile.isComplete() || @@ -85,41 +87,51 @@ void RenderTile::finishRender(PaintParameters& parameters) { tile.expires, parameters.debugOptions, parameters.context); } - parameters.programs.debug.draw( + const auto allAttributeBindings = program.computeAllAttributeBindings( + *tile.debugBucket->vertexBuffer, + paintAttributeData, + properties + ); + + program.draw( parameters.context, gl::Lines { 4.0f * parameters.pixelRatio }, gl::DepthMode::disabled(), parameters.stencilModeForClipping(clip), gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ matrix }, - uniforms::u_color::Value{ Color::white() } - }, - *tile.debugBucket->vertexBuffer, *tile.debugBucket->indexBuffer, tile.debugBucket->segments, - paintAttributeData, - properties, - parameters.state.getZoom(), + program.computeAllUniformValues( + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::white() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + allAttributeBindings, "debug" ); - parameters.programs.debug.draw( + program.draw( parameters.context, gl::Lines { 2.0f * parameters.pixelRatio }, gl::DepthMode::disabled(), parameters.stencilModeForClipping(clip), gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ matrix }, - uniforms::u_color::Value{ Color::black() } - }, - *tile.debugBucket->vertexBuffer, *tile.debugBucket->indexBuffer, tile.debugBucket->segments, - paintAttributeData, - properties, - parameters.state.getZoom(), + program.computeAllUniformValues( + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::black() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + allAttributeBindings, "debug" ); } @@ -131,16 +143,22 @@ void RenderTile::finishRender(PaintParameters& parameters) { gl::DepthMode::disabled(), parameters.stencilModeForClipping(clip), gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ matrix }, - uniforms::u_color::Value{ Color::red() } - }, - parameters.staticData.tileVertexBuffer, parameters.staticData.tileBorderIndexBuffer, parameters.staticData.tileBorderSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), + program.computeAllUniformValues( + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::red() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + program.computeAllAttributeBindings( + parameters.staticData.tileVertexBuffer, + paintAttributeData, + properties + ), "debug" ); } diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 2ac714e122..4380ef24a1 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -85,11 +85,6 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) { void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (updateParameters.mode != MapMode::Continuous) { - // Don't load/render anyting in still mode until explicitly requested. - if (!updateParameters.stillImageRequest) { - return; - } - // Reset zoom history state. zoomHistory.first = true; } @@ -401,10 +396,6 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { } placementChanged = newPlacement->commit(*placement, parameters.timePoint); - // commitFeatureIndexes depends on the assumption that no new FeatureIndex has been loaded since placement - // started. If we violate this assumption, then we need to either make CollisionIndex completely independendent of - // FeatureIndex, or find a way for its entries to point to multiple FeatureIndexes. - commitFeatureIndexes(); crossTileSymbolIndex.pruneUnusedLayers(usedSymbolLayers); if (placementChanged || symbolBucketsChanged) { placement = std::move(newPlacement); @@ -495,7 +486,9 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { static const ClippingMaskProgram::PaintPropertyBinders paintAttributeData(properties, 0); for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) { - parameters.staticData.programs.clippingMask.draw( + auto& program = parameters.staticData.programs.clippingMask; + + program.draw( parameters.context, gl::Triangles(), gl::DepthMode::disabled(), @@ -508,15 +501,21 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { gl::StencilMode::Replace }, gl::ColorMode::disabled(), - ClippingMaskProgram::UniformValues { - uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) }, - }, - parameters.staticData.tileVertexBuffer, parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.tileTriangleSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), + program.computeAllUniformValues( + ClippingMaskProgram::UniformValues { + uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) }, + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + program.computeAllAttributeBindings( + parameters.staticData.tileVertexBuffer, + paintAttributeData, + properties + ), "clipping" ); } @@ -673,6 +672,39 @@ std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin return queryRenderedFeatures(geometry, options, layers); } + +void Renderer::Impl::queryRenderedSymbols(std::unordered_map<std::string, std::vector<Feature>>& resultsByLayer, + const ScreenLineString& geometry, + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options) const { + + auto renderedSymbols = placement->getCollisionIndex().queryRenderedSymbols(geometry); + std::vector<std::reference_wrapper<const RetainedQueryData>> bucketQueryData; + for (auto entry : renderedSymbols) { + bucketQueryData.push_back(placement->getQueryData(entry.first)); + } + // Although symbol query is global, symbol results are only sortable within a bucket + // For a predictable global sort order, we sort the buckets based on their corresponding tile position + std::sort(bucketQueryData.begin(), bucketQueryData.end(), [](const RetainedQueryData& a, const RetainedQueryData& b) { + return + std::tie(a.tileID.canonical.z, a.tileID.canonical.y, a.tileID.wrap, a.tileID.canonical.x) < + std::tie(b.tileID.canonical.z, b.tileID.canonical.y, b.tileID.wrap, b.tileID.canonical.x); + }); + + for (auto wrappedQueryData : bucketQueryData) { + auto& queryData = wrappedQueryData.get(); + auto bucketSymbols = queryData.featureIndex->lookupSymbolFeatures(renderedSymbols[queryData.bucketInstanceId], + options, + layers, + queryData.tileID, + queryData.featureSortOrder); + + for (auto layer : bucketSymbols) { + auto& resultFeatures = resultsByLayer[layer.first]; + std::move(layer.second.begin(), layer.second.end(), std::inserter(resultFeatures, resultFeatures.end())); + } + } +} std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options, const std::vector<const RenderLayer*>& layers) const { std::unordered_set<std::string> sourceIDs; @@ -680,13 +712,18 @@ std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin sourceIDs.emplace(layer->baseImpl->source); } + mat4 projMatrix; + transformState.getProjMatrix(projMatrix); + std::unordered_map<std::string, std::vector<Feature>> resultsByLayer; for (const auto& sourceID : sourceIDs) { if (RenderSource* renderSource = getRenderSource(sourceID)) { - auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, placement->getCollisionIndex()); + auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); } } + + queryRenderedSymbols(resultsByLayer, geometry, layers, options); std::vector<Feature> result; @@ -785,15 +822,6 @@ bool Renderer::Impl::hasTransitions(TimePoint timePoint) const { return false; } -void Renderer::Impl::commitFeatureIndexes() { - for (auto& source : renderSources) { - for (auto& renderTile : source.second->getRenderTiles()) { - Tile& tile = renderTile.get().tile; - tile.commitFeatureIndex(); - } - } -} - void Renderer::Impl::updateFadingTiles() { fadingTiles = false; for (auto& source : renderSources) { diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 4675ac79ea..4124f6f416 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -64,7 +64,12 @@ private: RenderLayer* getRenderLayer(const std::string& id); const RenderLayer* getRenderLayer(const std::string& id) const; - + + void queryRenderedSymbols(std::unordered_map<std::string, std::vector<Feature>>& resultsByLayer, + const ScreenLineString& geometry, + const std::vector<const RenderLayer*>& layers, + const RenderedQueryOptions& options) const; + std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector<const RenderLayer*>&) const; // GlyphManagerObserver implementation. @@ -74,7 +79,6 @@ private: void onTileChanged(RenderSource&, const OverscaledTileID&) override; void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; - void commitFeatureIndexes(); void updateFadingTiles(); friend class Renderer; diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp index 057ad5a4a7..2d28b8dd84 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp @@ -68,8 +68,8 @@ RenderCustomGeometrySource::queryRenderedFeatures(const ScreenLineString& geomet const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector<Feature> RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp index 033d731029..5533fe2b83 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index cbf4db70b5..0e265efff4 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -86,8 +86,8 @@ RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector<Feature> RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp index 72fccbd043..297fa09a29 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -32,7 +32,7 @@ public: const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex&) const final; + const mat4& projMatrix) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp index 31a5916a34..dce5e40185 100644 --- a/src/mbgl/renderer/sources/render_image_source.cpp +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -58,24 +58,32 @@ void RenderImageSource::finishRender(PaintParameters& parameters) { static const style::Properties<>::PossiblyEvaluated properties {}; static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0); + auto& programInstance = parameters.programs.debug; + for (auto matrix : matrices) { - parameters.programs.debug.draw( + programInstance.draw( parameters.context, gl::LineStrip { 4.0f * parameters.pixelRatio }, gl::DepthMode::disabled(), gl::StencilMode::disabled(), gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ matrix }, - uniforms::u_color::Value{ Color::red() } - }, - parameters.staticData.tileVertexBuffer, parameters.staticData.tileBorderIndexBuffer, parameters.staticData.tileBorderSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), - "debug" + programInstance.computeAllUniformValues( + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::red() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + programInstance.computeAllAttributeBindings( + parameters.staticData.tileVertexBuffer, + paintAttributeData, + properties + ), + "image" ); } } @@ -85,7 +93,7 @@ RenderImageSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector<const RenderLayer*>&, const RenderedQueryOptions&, - const CollisionIndex&) const { + const mat4&) const { return std::unordered_map<std::string, std::vector<Feature>> {}; } diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp index 85ee0ace11..cf14e180fd 100644 --- a/src/mbgl/renderer/sources/render_image_source.hpp +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -33,7 +33,7 @@ public: const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp index b3153622c3..fbf2c09d19 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -147,7 +147,7 @@ RenderRasterDEMSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector<const RenderLayer*>&, const RenderedQueryOptions&, - const CollisionIndex& ) const { + const mat4&) const { return std::unordered_map<std::string, std::vector<Feature>> {}; } diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp index 741214a14d..48c7803e92 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index 60b3fa9a3b..f97ce4e65b 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -77,7 +77,7 @@ RenderRasterSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector<const RenderLayer*>&, const RenderedQueryOptions&, - const CollisionIndex& ) const { + const mat4&) const { return std::unordered_map<std::string, std::vector<Feature>> {}; } diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index 78eda199ac..32539a046d 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index e87bea5dcd..4de4f01e3f 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -80,8 +80,8 @@ RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector<Feature> RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 592160dc16..6fd2425aa3 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -28,7 +28,7 @@ public: const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const mat4& projMatrix) const final; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 8f83a0f982..d28e95181b 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -242,7 +242,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { + const mat4& projMatrix) const { std::unordered_map<std::string, std::vector<Feature>> result; if (renderTiles.empty() || geometry.empty()) { return result; @@ -264,14 +264,19 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x); }); + auto maxPitchScaleFactor = transformState.maxPitchScaleFactor(); + for (const RenderTile& renderTile : sortedTiles) { + const float scale = std::pow(2, transformState.getZoom() - renderTile.id.canonical.z); + auto queryPadding = maxPitchScaleFactor * renderTile.tile.getQueryPadding(layers) * util::EXTENT / util::tileSize / scale; + GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); - if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) { + if (tileSpaceBoundsMin.x - queryPadding >= util::EXTENT || tileSpaceBoundsMin.y - queryPadding >= util::EXTENT) { continue; } GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max); - if (tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) { + if (tileSpaceBoundsMax.x + queryPadding < 0 || tileSpaceBoundsMax.y + queryPadding < 0) { continue; } @@ -286,7 +291,7 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered transformState, layers, options, - collisionIndex); + projMatrix); } return result; diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index 2638599c38..0cef9e2c40 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -54,7 +54,7 @@ public: const TransformState& transformState, const std::vector<const RenderLayer*>&, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const; + const mat4& projMatrix) const; std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const; diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp index c700295a15..68d2dcc468 100644 --- a/src/mbgl/shaders/line.cpp +++ b/src/mbgl/shaders/line.cpp @@ -31,6 +31,7 @@ uniform vec2 u_gl_units_to_pixels; varying vec2 v_normal; varying vec2 v_width2; varying float v_gamma_scale; +varying highp float v_linesofar; #ifndef HAS_UNIFORM_u_color @@ -131,6 +132,8 @@ void main() { vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; + v_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0; + vec2 pos = a_pos_normal.xy; // x is 1 if it's a round cap, 0 otherwise diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp index 207dd2ee69..ba85f87dea 100644 --- a/src/mbgl/storage/resource.cpp +++ b/src/mbgl/storage/resource.cpp @@ -79,13 +79,13 @@ Resource Resource::spriteJSON(const std::string& base, float pixelRatio) { Resource Resource::glyphs(const std::string& urlTemplate, const FontStack& fontStack, const std::pair<uint16_t, uint16_t>& glyphRange) { return Resource { Resource::Kind::Glyphs, - util::replaceTokens(urlTemplate, [&](const std::string& token) { + util::replaceTokens(urlTemplate, [&](const std::string& token) -> optional<std::string> { if (token == "fontstack") { return util::percentEncode(fontStackToString(fontStack)); } else if (token == "range") { return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second); } else { - return std::string(); + return {}; } }) }; @@ -104,7 +104,7 @@ Resource Resource::tile(const std::string& urlTemplate, } return Resource { Resource::Kind::Tile, - util::replaceTokens(urlTemplate, [&](const std::string& token) { + util::replaceTokens(urlTemplate, [&](const std::string& token) -> optional<std::string> { if (token == "z") { return util::toString(z); } else if (token == "x") { @@ -123,7 +123,7 @@ Resource Resource::tile(const std::string& urlTemplate, } else if (token == "ratio") { return std::string(pixelRatio > 1.0 ? "@2x" : ""); } else { - return std::string(); + return {}; } }), Resource::TileData { diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp index 3c941945fd..386d85e921 100644 --- a/src/mbgl/style/conversion/filter.cpp +++ b/src/mbgl/style/conversion/filter.cpp @@ -1,17 +1,39 @@ #include <mbgl/style/conversion/filter.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/parsing_context.hpp> +#include <mbgl/style/expression/compound_expression.hpp> +#include <mbgl/style/expression/boolean_operator.hpp> namespace mbgl { namespace style { namespace conversion { -using GeometryValue = mapbox::geometry::value; +using namespace mbgl::style::expression; + +static bool isExpression(const Convertible& filter); +std::unique_ptr<Expression> convertLegacyFilter(const Convertible& values, Error& error); + +optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const { + if (isExpression(value)) { + ParsingContext parsingContext(type::Boolean); + ParseResult parseResult = parsingContext.parseExpression(value); + if (!parseResult) { + error = { parsingContext.getCombinedErrors() }; + return {}; + } else { + return { Filter(std::move(parseResult)) }; + } + } else { + std::unique_ptr<Expression> expression = convertLegacyFilter(value, error); + if (!expression) return {}; + return Filter(optional<std::unique_ptr<Expression>>(std::move(expression))); + } +} // This is a port from https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/feature_filter/index.js -static bool isExpressionFilter(const Convertible& filter) { +bool isExpression(const Convertible& filter) { if (!isArray(filter) || arrayLength(filter) == 0) { return false; } @@ -20,7 +42,7 @@ static bool isExpressionFilter(const Convertible& filter) { if (!op) { return false; - + } else if (*op == "has") { if (arrayLength(filter) < 2) return false; optional<std::string> operand = toString(arrayMember(filter, 1)); @@ -35,7 +57,7 @@ static bool isExpressionFilter(const Convertible& filter) { } else if (*op == "any" || *op == "all") { for (std::size_t i = 1; i < arrayLength(filter); i++) { Convertible f = arrayMember(filter, i); - if (!isExpressionFilter(f) && !toBool(f)) { + if (!isExpression(f) && !toBool(f)) { return false; } } @@ -46,257 +68,136 @@ static bool isExpressionFilter(const Convertible& filter) { } } -static optional<GeometryValue> normalizeValue(const optional<GeometryValue>& value, Error& error) { - if (!value) { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - } else { - return *value; - } -} +std::unique_ptr<Expression> createExpression(std::string op, std::vector<std::unique_ptr<Expression>> args, Error& error) { + if (op == "any") { + return std::make_unique<Any>(std::move(args)); + + } else if (op == "all") { + return std::make_unique<All>(std::move(args)); -static optional<FeatureType> toFeatureType(const Convertible& value, Error& error) { - optional<std::string> type = toString(value); - if (!type) { - error = { "value for $type filter must be a string" }; - return {}; - } else if (*type == "Point") { - return FeatureType::Point; - } else if (*type == "LineString") { - return FeatureType::LineString; - } else if (*type == "Polygon") { - return FeatureType::Polygon; } else { - error = { "value for $type filter must be Point, LineString, or Polygon" }; - return {}; + ParsingContext parsingContext(type::Boolean); + ParseResult parseResult = createCompoundExpression(op, std::move(args), parsingContext); + if (!parseResult) { + error = { parsingContext.getCombinedErrors() }; + return {}; + } else { + return std::move(*parseResult); + } } } -static optional<FeatureIdentifier> toFeatureIdentifier(const Convertible& value, Error& error) { - optional<GeometryValue> identifier = toValue(value); - if (!identifier) { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - } else { - return (*identifier).match( - [] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; }, - [] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; }, - [] ( double t) -> optional<FeatureIdentifier> { return { t }; }, - [] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; }, - [&] (const auto&) -> optional<FeatureIdentifier> { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - }); - } +std::unique_ptr<Expression> createExpression(std::string op, std::unique_ptr<Expression> expression, Error& error) { + std::vector<std::unique_ptr<Expression>> args; + args.push_back(std::move(expression)); + return createExpression(op, std::move(args), error); } -template <class FilterType, class IdentifierFilterType> -optional<Filter> convertUnaryFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 2) { - error = { "filter expression must have 2 elements" }; - return {}; - } - - optional<std::string> key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$id") { - return { IdentifierFilterType {} }; +std::unique_ptr<Expression> convertLiteral(const Convertible& convertible, Error& error) { + ParsingContext parsingContext; + ParseResult parseResult = Literal::parse(convertible, parsingContext); + if (parseResult) { + return std::move(*parseResult); } else { - return { FilterType { *key } }; + error = { parsingContext.getCombinedErrors() }; + return {}; } } -template <class FilterType, class TypeFilterType, class IdentifierFilterType> -optional<Filter> convertEqualityFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; +std::vector<std::unique_ptr<Expression>> convertLiteralArray(const Convertible &input, Error& error, std::size_t startIndex = 0) { + std::vector<std::unique_ptr<Expression>> output; + for (std::size_t i = startIndex; i < arrayLength(input); i++) { + output.push_back(convertLiteral(arrayMember(input, i), error)); } + return output; +} - optional<std::string> key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; +std::unique_ptr<Expression> convertLegacyComparisonFilter(const Convertible& values, Error& error, optional<std::string> opOverride = {}) { + optional<std::string> op = opOverride ? opOverride : toString(arrayMember(values, 0)); + optional<std::string> property = toString(arrayMember(values, 1)); + + if (!property) { + error = { "filter property must be a string" }; return {}; - } - - if (*key == "$type") { - optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { TypeFilterType { *filterValue } }; - - } else if (*key == "$id") { - optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { IdentifierFilterType { *filterValue } }; - + } else if (*property == "$type") { + return createExpression("filter-type-" + *op, convertLiteralArray(values, error, 2), error); + } else if (*property == "$id") { + return createExpression("filter-id-" + *op, convertLiteralArray(values, error, 2), error); } else { - optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { - return {}; - } - - return { FilterType { *key, *filterValue } }; + return createExpression("filter-" + *op, convertLiteralArray(values, error, 1), error); } } - -template <class FilterType> -optional<Filter> convertBinaryFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; - } - - optional<std::string> key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { + +std::unique_ptr<Expression> convertLegacyHasFilter(const Convertible& values, Error& error) { + optional<std::string> property = toString(arrayMember(values, 1)); + + if (!property) { + error = { "filter property must be a string" }; return {}; + } else if (*property == "$type") { + return std::make_unique<Literal>(true); + } else if (*property == "$id") { + return createExpression("filter-has-id", std::vector<std::unique_ptr<Expression>>(), error); + } else { + return createExpression("filter-has", std::make_unique<Literal>(*property), error); } - - return { FilterType { *key, *filterValue } }; } -template <class FilterType, class TypeFilterType, class IdentifierFilterType> -optional<Filter> convertSetFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 2) { - error = { "filter expression must at least 2 elements" }; - return {}; - } - - optional<std::string> key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$type") { - std::vector<FeatureType> values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { TypeFilterType { std::move(values) } }; - - } else if (*key == "$id") { - std::vector<FeatureIdentifier> values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { IdentifierFilterType { std::move(values) } }; - +std::unique_ptr<Expression> convertLegacyInFilter(const Convertible& values, Error& error) { + optional<std::string> property = toString(arrayMember(values, 1)); + + if (!property) { + error = { "filter property must be a string" }; + return {}; + } else if (arrayLength(values) == 0) { + return std::make_unique<Literal>(false); + } else if (*property == "$type") { + return createExpression("filter-type-in", convertLiteralArray(values, error, 2), error); + } else if (*property == "$id") { + return createExpression("filter-id-in", convertLiteralArray(values, error, 2), error); } else { - std::vector<GeometryValue> values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional<GeometryValue> filterValue = normalizeValue(toValue(arrayMember(value, i)), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { FilterType { *key, std::move(values) } }; + return createExpression("filter-in", convertLiteralArray(values, error, 1), error); } } -template <class FilterType> -optional<Filter> convertCompoundFilter(const Convertible& value, Error& error) { - std::vector<Filter> filters; - for (std::size_t i = 1; i < arrayLength(value); ++i) { - optional<Filter> element = convert<Filter>(arrayMember(value, i), error); - if (!element) { - return {}; - } - filters.push_back(*element); +std::vector<std::unique_ptr<Expression>> convertLegacyFilterArray(const Convertible &input, Error& error, std::size_t startIndex = 0) { + std::vector<std::unique_ptr<Expression>> output; + for (std::size_t i = startIndex; i < arrayLength(input); i++) { + output.push_back(convertLegacyFilter(arrayMember(input, i), error)); } - - return { FilterType { std::move(filters) } }; + return output; } -optional<Filter> convertExpressionFilter(const Convertible& value, Error& error) { - expression::ParsingContext ctx(expression::type::Boolean); - expression::ParseResult expression = ctx.parseExpression(value); - if (!expression) { - error = { ctx.getCombinedErrors() }; - return {}; - } - - return { ExpressionFilter { std::move(*expression) } }; -} - -optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const { - if (isExpressionFilter(value)) { - return convertExpressionFilter(value, error); +std::unique_ptr<Expression> convertLegacyFilter(const Convertible& values, Error& error) { + if (isUndefined(values)) { + return std::make_unique<Literal>(true); } - if (!isArray(value)) { - error = { "filter expression must be an array" }; - return {}; - } - - if (arrayLength(value) < 1) { - error = { "filter expression must have at least 1 element" }; - return {}; - } + optional<std::string> op = toString(arrayMember(values, 0)); - optional<std::string> op = toString(arrayMember(value, 0)); if (!op) { error = { "filter operator must be a string" }; return {}; + } else if (arrayLength(values) <= 1) { + return std::make_unique<Literal>(*op != "any"); + } else { + return ( + *op == "==" || + *op == "<" || + *op == ">" || + *op == "<=" || + *op == ">=" ? convertLegacyComparisonFilter(values, error) : + *op == "!=" ? createExpression("!", convertLegacyComparisonFilter(values, error, {"=="}), error) : + *op == "any" ? createExpression("any", convertLegacyFilterArray(values, error, 1), error) : + *op == "all" ? createExpression("all", convertLegacyFilterArray(values, error, 1), error) : + *op == "none" ? createExpression("!", createExpression("any", convertLegacyFilterArray(values, error, 1), error), error) : + *op == "in" ? convertLegacyInFilter(values, error) : + *op == "!in" ? createExpression("!", convertLegacyInFilter(values, error), error) : + *op == "has" ? convertLegacyHasFilter(values, error) : + *op == "!has" ? createExpression("!", convertLegacyHasFilter(values, error), error) : + std::make_unique<Literal>(true) + ); } - - if (*op == "==") { - return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error); - } else if (*op == "!=") { - return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error); - } else if (*op == ">") { - return convertBinaryFilter<GreaterThanFilter>(value, error); - } else if (*op == ">=") { - return convertBinaryFilter<GreaterThanEqualsFilter>(value, error); - } else if (*op == "<") { - return convertBinaryFilter<LessThanFilter>(value, error); - } else if (*op == "<=") { - return convertBinaryFilter<LessThanEqualsFilter>(value, error); - } else if (*op == "in") { - return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error); - } else if (*op == "!in") { - return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error); - } else if (*op == "all") { - return convertCompoundFilter<AllFilter>(value, error); - } else if (*op == "any") { - return convertCompoundFilter<AnyFilter>(value, error); - } else if (*op == "none") { - return convertCompoundFilter<NoneFilter>(value, error); - } else if (*op == "has") { - return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error); - } else if (*op == "!has") { - return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error); - } - - error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" }; - return {}; } } // namespace conversion diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 7b7727d7c4..74171763a0 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -126,162 +126,9 @@ void stringify(Writer& writer, const FeatureIdentifier& id) { } template <class Writer> -class StringifyFilter { -public: - Writer& writer; - - void operator()(const NullFilter&) { - writer.Null(); - } - - void operator()(const EqualsFilter& f) { - stringifyBinaryFilter(f, "=="); - } - - void operator()(const NotEqualsFilter& f) { - stringifyBinaryFilter(f, "!="); - } - - void operator()(const LessThanFilter& f) { - stringifyBinaryFilter(f, "<"); - } - - void operator()(const LessThanEqualsFilter& f) { - stringifyBinaryFilter(f, "<="); - } - - void operator()(const GreaterThanFilter& f) { - stringifyBinaryFilter(f, ">"); - } - - void operator()(const GreaterThanEqualsFilter& f) { - stringifyBinaryFilter(f, ">="); - } - - void operator()(const InFilter& f) { - stringifySetFilter(f, "in"); - } - - void operator()(const NotInFilter& f) { - stringifySetFilter(f, "!in"); - } - - void operator()(const AllFilter& f) { - stringifyCompoundFilter(f, "all"); - } - - void operator()(const AnyFilter& f) { - stringifyCompoundFilter(f, "any"); - } - - void operator()(const NoneFilter& f) { - stringifyCompoundFilter(f, "none"); - } - - void operator()(const HasFilter& f) { - stringifyUnaryFilter("has", f.key); - } - - void operator()(const NotHasFilter& f) { - stringifyUnaryFilter("!has", f.key); - } - - void operator()(const TypeEqualsFilter& f) { - stringifyBinaryFilter(f, "==", "$type"); - } - - void operator()(const TypeNotEqualsFilter& f) { - stringifyBinaryFilter(f, "!=", "$type"); - } - - void operator()(const TypeInFilter& f) { - stringifySetFilter(f, "in", "$type"); - } - - void operator()(const TypeNotInFilter& f) { - stringifySetFilter(f, "!in", "$type"); - } - - void operator()(const IdentifierEqualsFilter& f) { - stringifyBinaryFilter(f, "==", "$id"); - } - - void operator()(const IdentifierNotEqualsFilter& f) { - stringifyBinaryFilter(f, "!=", "$id"); - } - - void operator()(const IdentifierInFilter& f) { - stringifySetFilter(f, "in", "$id"); - } - - void operator()(const IdentifierNotInFilter& f) { - stringifySetFilter(f, "!in", "$id"); - } - - void operator()(const HasIdentifierFilter&) { - stringifyUnaryFilter("has", "$id"); - } - - void operator()(const NotHasIdentifierFilter&) { - stringifyUnaryFilter("!has", "$id"); - } - - void operator()(const ExpressionFilter& filter) { - stringify(writer, filter.expression->serialize()); - } - -private: - template <class F> - void stringifyBinaryFilter(const F& f, const char * op) { - stringifyBinaryFilter(f, op, f.key); - } - - template <class F> - void stringifyBinaryFilter(const F& f, const char * op, const std::string& key) { - writer.StartArray(); - writer.String(op); - writer.String(key); - stringify(writer, f.value); - writer.EndArray(); - } - - template <class F> - void stringifySetFilter(const F& f, const char * op) { - stringifySetFilter(f, op, f.key); - } - - template <class F> - void stringifySetFilter(const F& f, const char * op, const std::string& key) { - writer.StartArray(); - writer.String(op); - writer.String(key); - for (const auto& value : f.values) { - stringify(writer, value); - } - writer.EndArray(); - } - - template <class F> - void stringifyCompoundFilter(const F& f, const char * op) { - writer.StartArray(); - writer.String(op); - for (const auto& filter : f.filters) { - Filter::visit(filter, *this); - } - writer.EndArray(); - } - - void stringifyUnaryFilter(const char * op, const std::string& key) { - writer.StartArray(); - writer.String(op); - writer.String(key); - writer.EndArray(); - } -}; - -template <class Writer> -void stringify(Writer& writer, const Filter& f) { - Filter::visit(f, StringifyFilter<Writer> { writer }); +void stringify(Writer& writer, const Filter& filter) { + if (!filter.expression) writer.Null(); + else stringify(writer, (*filter.expression)->serialize()); } template <class Writer> diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp index a2c4aa80b3..15ed10a90f 100644 --- a/src/mbgl/style/conversion/tileset.cpp +++ b/src/mbgl/style/conversion/tileset.cpp @@ -1,14 +1,11 @@ #include <mbgl/style/conversion/tileset.hpp> #include <mbgl/util/geo.hpp> +#include <mbgl/math/clamp.hpp> namespace mbgl { namespace style { namespace conversion { -bool validateLatitude(const double lat) { - return lat <= 90 && lat >= -90; -} - optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error& error) const { Tileset result; @@ -95,16 +92,20 @@ optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error error = { "bounds array must contain numeric longitude and latitude values" }; return {}; } - if (!validateLatitude(*bottom) || !validateLatitude(*top) || top <= bottom){ - error = { "bounds latitude values must be between -90 and 90 with bottom less than top" }; + + bottom = util::clamp(*bottom, -90.0, 90.0); + top = util::clamp(*top, -90.0, 90.0); + if (top <= bottom){ + error = { "bounds bottom latitude must be between smaller than top latitude" }; return {}; } + if(*left >= *right) { error = { "bounds left longitude should be less than right longitude" }; return {}; } - *left = util::max(-180.0, *left); - *right = util::min(180.0, *right); + left = util::max(-180.0, *left); + right = util::min(180.0, *right); result.bounds = LatLngBounds::hull({ *bottom, *left }, { *top, *right }); } diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index c36ffa33e3..4226756fe4 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/util.hpp> #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/math/log2.hpp> +#include <mbgl/util/i18n.hpp> #include <mbgl/util/ignore.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/platform.hpp> @@ -18,7 +19,7 @@ namespace detail { The Signature<Fn> structs are wrappers around an "evaluate()" function whose purpose is to extract the necessary Type data from the evaluate function's type. There are three key (partial) specializations: - + Signature<R (Params...)>: Wraps a simple evaluate function (const T0&, const T1&, ...) -> Result<U> @@ -29,9 +30,9 @@ namespace detail { Signature<R (const EvaluationContext&, Params...)>: Wraps an evaluate function that needs to access the expression evaluation parameters in addition to its subexpressions, i.e., - (const EvaluationParams& const T0&, const T1&, ...) -> Result<U>. Needed + (const EvaluationParams&, const T0&, const T1&, ...) -> Result<U>. Needed for expressions like ["zoom"], ["get", key], etc. - + In each of the above evaluate signatures, T0, T1, etc. are the types of the successfully evaluated subexpressions. */ @@ -42,7 +43,7 @@ struct Signature; template <class R, class... Params> struct Signature<R (Params...)> : SignatureBase { using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>; - + Signature(R (*evaluate_)(Params...), std::string name_) : SignatureBase( valueTypeToExpressionType<std::decay_t<typename R::Value>>(), @@ -54,7 +55,7 @@ struct Signature<R (Params...)> : SignatureBase { EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{}); } - + std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override { typename Signature::Args argsArray; std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin()); @@ -79,7 +80,7 @@ private: template <class R, typename T> struct Signature<R (const Varargs<T>&)> : SignatureBase { using Args = std::vector<std::unique_ptr<Expression>>; - + Signature(R (*evaluate_)(const Varargs<T>&), std::string name_) : SignatureBase( valueTypeToExpressionType<std::decay_t<typename R::Value>>(), @@ -88,11 +89,11 @@ struct Signature<R (const Varargs<T>&)> : SignatureBase { ), evaluate(evaluate_) {} - + std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override { return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(args)); }; - + EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { Varargs<T> evaluated; evaluated.reserve(args.size()); @@ -114,7 +115,7 @@ struct Signature<R (const Varargs<T>&)> : SignatureBase { template <class R, class... Params> struct Signature<R (const EvaluationContext&, Params...)> : SignatureBase { using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>; - + Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) : SignatureBase( valueTypeToExpressionType<std::decay_t<typename R::Value>>(), @@ -123,17 +124,17 @@ struct Signature<R (const EvaluationContext&, Params...)> : SignatureBase { ), evaluate(evaluate_) {} - + std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override { typename Signature::Args argsArray; std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin()); return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(argsArray)); } - + EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{}); } - + private: template <std::size_t ...I> EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence<I...>) const { @@ -149,6 +150,41 @@ private: R (*evaluate)(const EvaluationContext&, Params...); }; + +// Evaluate function needing EvaluationContext and Varargs +// (const EvaluationContext&, const Varargs<T>&) -> Result<U> +template <class R, typename T> +struct Signature<R (const EvaluationContext&, const Varargs<T>&)> : SignatureBase { + using Args = std::vector<std::unique_ptr<Expression>>; + + Signature(R (*evaluate_)(const EvaluationContext&, const Varargs<T>&), std::string name_) : + SignatureBase( + valueTypeToExpressionType<std::decay_t<typename R::Value>>(), + VarargsType { valueTypeToExpressionType<T>() }, + std::move(name_) + ), + evaluate(evaluate_) + {} + + std::unique_ptr<Expression> makeExpression(std::vector<std::unique_ptr<Expression>> args) const override { + return std::make_unique<CompoundExpression<Signature>>(name, *this, std::move(args)); + }; + + EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { + Varargs<T> evaluated; + evaluated.reserve(args.size()); + for (const auto& arg : args) { + const EvaluationResult evaluatedArg = arg->evaluate(evaluationParameters); + if(!evaluatedArg) return evaluatedArg.error(); + evaluated.push_back(*fromExpressionValue<std::decay_t<T>>(*evaluatedArg)); + } + const R value = evaluate(evaluationParameters, evaluated); + if (!value) return value.error(); + return *value; + } + + R (*evaluate)(const EvaluationContext&, const Varargs<T>&); +}; // Machinery to pull out function types from class methods, lambdas, etc. template <class R, class... Params> @@ -180,29 +216,104 @@ static std::unique_ptr<detail::SignatureBase> makeSignature(Fn evaluateFunction, return std::make_unique<detail::Signature<Fn>>(evaluateFunction, std::move(name)); } +Value featureIdAsExpressionValue(EvaluationContext params) { + assert(params.feature); + auto id = params.feature->getID(); + if (!id) return Null; + return id->match([](const auto& idid) { + return toExpressionValue(mbgl::Value(idid)); + }); +}; + +optional<Value> featurePropertyAsExpressionValue(EvaluationContext params, const std::string& key) { + assert(params.feature); + auto property = params.feature->getValue(key); + return property ? toExpressionValue(*property) : optional<Value>(); +}; + +optional<std::string> featureTypeAsString(FeatureType type) { + switch(type) { + case FeatureType::Point: + return optional<std::string>("Point"); + case FeatureType::LineString: + return optional<std::string>("LineString"); + case FeatureType::Polygon: + return optional<std::string>("Polygon"); + case FeatureType::Unknown: + return optional<std::string>("Unknown"); + default: + return {}; + } +}; + +optional<double> featurePropertyAsDouble(EvaluationContext params, const std::string& key) { + assert(params.feature); + auto property = params.feature->getValue(key); + if (!property) return {}; + return property->match( + [](double value) { return value; }, + [](uint64_t value) { return optional<double>(static_cast<double>(value)); }, + [](int64_t value) { return optional<double>(static_cast<double>(value)); }, + [](auto) { return optional<double>(); } + ); +}; + +optional<std::string> featurePropertyAsString(EvaluationContext params, const std::string& key) { + assert(params.feature); + auto property = params.feature->getValue(key); + if (!property) return {}; + return property->match( + [](std::string value) { return value; }, + [](auto) { return optional<std::string>(); } + ); +}; + +optional<double> featureIdAsDouble(EvaluationContext params) { + assert(params.feature); + auto id = params.feature->getID(); + if (!id) return optional<double>(); + return id->match( + [](double value) { return value; }, + [](uint64_t value) { return optional<double>(static_cast<double>(value)); }, + [](int64_t value) { return optional<double>(static_cast<double>(value)); }, + [](auto) { return optional<double>(); } + ); +}; + +optional<std::string> featureIdAsString(EvaluationContext params) { + assert(params.feature); + auto id = params.feature->getID(); + if (!id) return optional<std::string>(); + return id->match( + [](std::string value) { return value; }, + [](auto) { return optional<std::string>(); } + ); +}; + std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initializeDefinitions() { std::unordered_map<std::string, CompoundExpressionRegistry::Definition> definitions; auto define = [&](std::string name, auto fn) { definitions[name].push_back(makeSignature(fn, name)); }; - + define("e", []() -> Result<double> { return 2.718281828459045; }); define("pi", []() -> Result<double> { return 3.141592653589793; }); define("ln2", []() -> Result<double> { return 0.6931471805599453; }); define("typeof", [](const Value& v) -> Result<std::string> { return toString(typeOf(v)); }); - + define("to-string", [](const Value& value) -> Result<std::string> { return value.match( + [](const NullValue&) -> Result<std::string> { return std::string(); }, [](const Color& c) -> Result<std::string> { return c.stringify(); }, // avoid quoting [](const std::string& s) -> Result<std::string> { return s; }, // avoid quoting [](const auto& v) -> Result<std::string> { return stringify(v); } ); }); - + define("to-boolean", [](const Value& v) -> Result<bool> { return v.match( - [&] (double f) { return (bool)f; }, + [&] (double f) { return static_cast<bool>(f); }, [&] (const std::string& s) { return s.length() > 0; }, [&] (bool b) { return b; }, [&] (const NullValue&) { return false; }, @@ -212,10 +323,10 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali define("to-rgba", [](const Color& color) -> Result<std::array<double, 4>> { return color.toArray(); }); - + define("rgba", rgba); define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); }); - + define("zoom", [](const EvaluationContext& params) -> Result<double> { if (!params.zoom) { return EvaluationError { @@ -224,7 +335,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali } return *(params.zoom); }); - + define("heatmap-density", [](const EvaluationContext& params) -> Result<double> { if (!params.heatmapDensity) { return EvaluationError { @@ -240,7 +351,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali "Feature data is unavailable in the current evaluation context." }; } - + return params.feature->getValue(key) ? true : false; }); define("has", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<bool> { @@ -266,7 +377,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali } return object.at(key); }); - + define("properties", [](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> { if (!params.feature) { return EvaluationError { @@ -280,14 +391,14 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali } return result; }); - + define("geometry-type", [](const EvaluationContext& params) -> Result<std::string> { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } - + auto type = params.feature->getType(); if (type == FeatureType::Point) { return "Point"; @@ -299,14 +410,14 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali return "Unknown"; } }); - + define("id", [](const EvaluationContext& params) -> Result<Value> { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } - + auto id = params.feature->getID(); if (!id) { return Null; @@ -317,7 +428,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali } ); }); - + define("+", [](const Varargs<double>& args) -> Result<double> { double sum = 0.0f; for (auto arg : args) { @@ -347,7 +458,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali define("asin", [](double x) -> Result<double> { return asin(x); }); define("acos", [](double x) -> Result<double> { return acos(x); }); define("atan", [](double x) -> Result<double> { return atan(x); }); - + define("min", [](const Varargs<double>& args) -> Result<double> { double result = std::numeric_limits<double>::infinity(); for (double arg : args) { @@ -363,7 +474,7 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali return result; }); - define("round", [](double x) -> Result<double> { return std::round(x); }); + define("round", [](double x) -> Result<double> { return ::round(x); }); define("floor", [](double x) -> Result<double> { return std::floor(x); }); define("ceil", [](double x) -> Result<double> { return std::ceil(x); }); define("abs", [](double x) -> Result<double> { return std::abs(x); }); @@ -376,9 +487,13 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali define("<", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs < rhs; }); define("<=", [](double lhs, double rhs) -> Result<bool> { return lhs <= rhs; }); define("<=", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs <= rhs; }); - + define("!", [](bool e) -> Result<bool> { return !e; }); + define("is-supported-script", [](const std::string& x) -> Result<bool> { + return util::i18n::isStringInSupportedScript(x); + }); + define("upcase", [](const std::string& input) -> Result<std::string> { return platform::uppercase(input); }); @@ -395,7 +510,130 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali define("error", [](const std::string& input) -> Result<type::ErrorType> { return EvaluationError { input }; }); + + // Legacy Filters + define("filter-==", [](const EvaluationContext& params, const std::string& key, const Value &lhs) -> Result<bool> { + const auto rhs = featurePropertyAsExpressionValue(params, key); + return rhs ? lhs == *rhs : false; + }); + + define("filter-id-==", [](const EvaluationContext& params, const Value &lhs) -> Result<bool> { + return lhs == featureIdAsExpressionValue(params); + }); + + define("filter-type-==", [](const EvaluationContext& params, const std::string &lhs) -> Result<bool> { + if (!params.feature) return false; + return featureTypeAsString(params.feature->getType()) == lhs; + }); + + define("filter-<", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs < lhs : false; + }); + + define("filter-<", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs < lhs : false; + }); + + define("filter-id-<", [](const EvaluationContext& params, double lhs) -> Result<bool> { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs < lhs : false; + }); + + define("filter-id-<", [](const EvaluationContext& params, std::string lhs) -> Result<bool> { + auto rhs = featureIdAsString(params); + return rhs ? rhs < lhs : false; + }); + + define("filter->", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs > lhs : false; + }); + + define("filter->", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs > lhs : false; + }); + + define("filter-id->", [](const EvaluationContext& params, double lhs) -> Result<bool> { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs > lhs : false; + }); + + define("filter-id->", [](const EvaluationContext& params, std::string lhs) -> Result<bool> { + auto rhs = featureIdAsString(params); + return rhs ? rhs > lhs : false; + }); + + define("filter-<=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs <= lhs : false; + }); + + define("filter-<=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs <= lhs : false; + }); + + define("filter-id-<=", [](const EvaluationContext& params, double lhs) -> Result<bool> { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs <= lhs : false; + }); + + define("filter-id-<=", [](const EvaluationContext& params, std::string lhs) -> Result<bool> { + auto rhs = featureIdAsString(params); + return rhs ? rhs <= lhs : false; + }); + + define("filter->=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result<bool> { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs >= lhs : false; + }); + + define("filter->=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result<bool> { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs >= lhs : false; + }); + + define("filter-id->=", [](const EvaluationContext& params, double lhs) -> Result<bool> { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs >= lhs : false; + }); + + define("filter-id->=", [](const EvaluationContext& params, std::string lhs) -> Result<bool> { + auto rhs = featureIdAsString(params); + return rhs ? rhs >= lhs : false; + }); + + define("filter-has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> { + assert(params.feature); + return bool(params.feature->getValue(key)); + }); + + define("filter-has-id", [](const EvaluationContext& params) -> Result<bool> { + assert(params.feature); + return bool(params.feature->getID()); + }); + + define("filter-type-in", [](const EvaluationContext& params, const Varargs<std::string>& types) -> Result<bool> { + assert(params.feature); + optional<std::string> type = featureTypeAsString(params.feature->getType()); + return std::find(types.begin(), types.end(), type) != types.end(); + }); + define("filter-id-in", [](const EvaluationContext& params, const Varargs<Value>& ids) -> Result<bool> { + auto id = featureIdAsExpressionValue(params); + return std::find(ids.begin(), ids.end(), id) != ids.end(); + }); + + define("filter-in", [](const EvaluationContext& params, const Varargs<Value>& varargs) -> Result<bool> { + if (varargs.size() < 2) return false; + assert(varargs[0].is<std::string>()); + auto value = featurePropertyAsExpressionValue(params, varargs[0].get<std::string>()); + return value ? std::find(varargs.begin() + 1, varargs.end(), *value) != varargs.end() : false; + }); + return definitions; } @@ -414,7 +652,7 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v return ParseResult(); } const CompoundExpressionRegistry::Definition& definition = it->second; - + auto length = arrayLength(value); // Check if we have a single signature with the correct number of @@ -440,14 +678,14 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v args.reserve(length - 1); for (std::size_t i = 1; i < length; i++) { optional<type::Type> expected; - + if (singleMatchingSignature) { expected = definition[*singleMatchingSignature]->params.match( [](const VarargsType& varargs) { return varargs.type; }, [&](const std::vector<type::Type>& params_) { return params_[i - 1]; } ); } - + auto parsed = ctx.parse(arrayMember(value, i), i, expected); if (!parsed) { return parsed; @@ -474,7 +712,7 @@ ParseResult createCompoundExpression(const Definition& definition, for (const std::unique_ptr<detail::SignatureBase>& signature : definition) { signatureContext.clearErrors(); - + if (signature->params.is<std::vector<type::Type>>()) { const std::vector<type::Type>& params = signature->params.get<std::vector<type::Type>>(); if (params.size() != args.size()) { @@ -502,12 +740,12 @@ ParseResult createCompoundExpression(const Definition& definition, } } } - + if (signatureContext.getErrors().size() == 0) { return ParseResult(signature->makeExpression(std::move(args))); } } - + if (definition.size() == 1) { ctx.appendErrors(std::move(signatureContext)); } else { @@ -540,10 +778,32 @@ ParseResult createCompoundExpression(const Definition& definition, } ctx.error("Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead."); } - + return ParseResult(); } +ParseResult createCompoundExpression(const std::string& name, ParsingContext& ctx) { + return createCompoundExpression(name, std::vector<std::unique_ptr<Expression>>(), ctx); +} + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr<Expression> arg1, + ParsingContext& ctx) { + std::vector<std::unique_ptr<Expression>> args; + args.push_back(std::move(arg1)); + return createCompoundExpression(name, std::move(args), ctx); +} + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr<Expression> arg1, + std::unique_ptr<Expression> arg2, + ParsingContext& ctx) { + std::vector<std::unique_ptr<Expression>> args; + args.push_back(std::move(arg1)); + args.push_back(std::move(arg2)); + return createCompoundExpression(name, std::move(args), ctx); +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/literal.cpp b/src/mbgl/style/expression/literal.cpp index 8a63980dba..f68cfd5cf5 100644 --- a/src/mbgl/style/expression/literal.cpp +++ b/src/mbgl/style/expression/literal.cpp @@ -109,6 +109,14 @@ mbgl::Value Literal::serialize() const { return *fromExpressionValue<mbgl::Value>(value); } } + +std::unique_ptr<Literal> createLiteral(const char* value) { + return createLiteral(std::string(value)); +} + +std::unique_ptr<Literal> createLiteral(Value value) { + return std::make_unique<Literal>(value); +} } // namespace expression } // namespace style diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp index 3d41f0bdd3..59123c9812 100644 --- a/src/mbgl/style/expression/match.cpp +++ b/src/mbgl/style/expression/match.cpp @@ -83,6 +83,10 @@ template<> EvaluationResult Match<std::string>::evaluate(const EvaluationContext return inputValue.error(); } + if (!inputValue->is<std::string>()) { + return otherwise->evaluate(params); + } + auto it = branches.find(inputValue->get<std::string>()); if (it != branches.end()) { return (*it).second->evaluate(params); @@ -96,7 +100,11 @@ template<> EvaluationResult Match<int64_t>::evaluate(const EvaluationContext& pa if (!inputValue) { return inputValue.error(); } - + + if (!inputValue->is<double>()) { + return otherwise->evaluate(params); + } + const auto numeric = inputValue->get<double>(); int64_t rounded = std::floor(numeric); if (numeric == rounded) { @@ -280,7 +288,7 @@ ParseResult parseMatch(const Convertible& value, ParsingContext& ctx) { branches.push_back(std::make_pair(std::move(labels), std::move(*output))); } - auto input = ctx.parse(arrayMember(value, 1), 1, inputType); + auto input = ctx.parse(arrayMember(value, 1), 1, {type::Value}); if (!input) { return ParseResult(); } @@ -292,6 +300,12 @@ ParseResult parseMatch(const Convertible& value, ParsingContext& ctx) { assert(inputType && outputType); + optional<std::string> err; + if ((*input)->getType() != type::Value && (err = type::checkSubtype(*inputType, (*input)->getType()))) { + ctx.error(*err, 1); + return ParseResult(); + } + return inputType->match( [&](const type::NumberType&) { return create<int64_t>(*outputType, std::move(*input), std::move(branches), std::move(*otherwise), ctx); diff --git a/src/mbgl/style/filter.cpp b/src/mbgl/style/filter.cpp index 51aa6bcf82..2559eb4816 100644 --- a/src/mbgl/style/filter.cpp +++ b/src/mbgl/style/filter.cpp @@ -1,12 +1,20 @@ #include <mbgl/style/filter.hpp> -#include <mbgl/style/filter_evaluator.hpp> #include <mbgl/tile/geometry_tile_data.hpp> namespace mbgl { namespace style { bool Filter::operator()(const expression::EvaluationContext &context) const { - return FilterBase::visit(*this, FilterEvaluator { context }); + + if (!this->expression) return true; + + const expression::EvaluationResult result = (*this->expression)->evaluate(context); + if (result) { + const optional<bool> typed = expression::fromExpressionValue<bool>(*result); + return typed ? *typed : false; + } else { + return true; + } } } // namespace style diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp deleted file mode 100644 index 72022172f4..0000000000 --- a/src/mbgl/style/filter_evaluator.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include <mbgl/style/filter.hpp> -#include <mbgl/style/filter_evaluator.hpp> -#include <mbgl/tile/geometry_tile_data.hpp> - -namespace mbgl { -namespace style { - -template <class Op> -struct Comparator { - const Op& op; - - template <class T> - bool operator()(const T& lhs, const T& rhs) const { - return op(lhs, rhs); - } - - template <class T0, class T1> - auto operator()(const T0& lhs, const T1& rhs) const - -> typename std::enable_if_t<std::is_arithmetic<T0>::value && !std::is_same<T0, bool>::value && - std::is_arithmetic<T1>::value && !std::is_same<T1, bool>::value, bool> { - return op(double(lhs), double(rhs)); - } - - template <class T0, class T1> - auto operator()(const T0&, const T1&) const - -> typename std::enable_if_t<!std::is_arithmetic<T0>::value || std::is_same<T0, bool>::value || - !std::is_arithmetic<T1>::value || std::is_same<T1, bool>::value, bool> { - return false; - } - - bool operator()(const NullValue&, - const NullValue&) const { - // Should be unreachable; null is not currently allowed by the style specification. - assert(false); - return false; - } - - bool operator()(const std::vector<Value>&, - const std::vector<Value>&) const { - // Should be unreachable; nested values are not currently allowed by the style specification. - assert(false); - return false; - } - - bool operator()(const PropertyMap&, - const PropertyMap&) const { - // Should be unreachable; nested values are not currently allowed by the style specification. - assert(false); - return false; - } -}; - -template <class Op> -bool compare(const Value& lhs, const Value& rhs, const Op& op) { - return Value::binary_visit(lhs, rhs, Comparator<Op> { op }); -} - -bool equal(const Value& lhs, const Value& rhs) { - return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; }); -} - -bool FilterEvaluator::operator()(const NullFilter&) const { - return true; -} - -bool FilterEvaluator::operator()(const EqualsFilter& filter) const { - optional<Value> actual = context.feature->getValue(filter.key); - return actual && equal(*actual, filter.value); -} - -bool FilterEvaluator::operator()(const NotEqualsFilter& filter) const { - optional<Value> actual = context.feature->getValue(filter.key); - return !actual || !equal(*actual, filter.value); -} - -bool FilterEvaluator::operator()(const LessThanFilter& filter) const { - optional<Value> actual = context.feature->getValue(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; }); -} - -bool FilterEvaluator::operator()(const LessThanEqualsFilter& filter) const { - optional<Value> actual = context.feature->getValue(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; }); -} - -bool FilterEvaluator::operator()(const GreaterThanFilter& filter) const { - optional<Value> actual = context.feature->getValue(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; }); -} - -bool FilterEvaluator::operator()(const GreaterThanEqualsFilter& filter) const { - optional<Value> actual = context.feature->getValue(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; }); -} - -bool FilterEvaluator::operator()(const InFilter& filter) const { - optional<Value> actual = context.feature->getValue(filter.key); - if (!actual) - return false; - for (const auto& v: filter.values) { - if (equal(*actual, v)) { - return true; - } - } - return false; -} - -bool FilterEvaluator::operator()(const NotInFilter& filter) const { - optional<Value> actual = context.feature->getValue(filter.key); - if (!actual) - return true; - for (const auto& v: filter.values) { - if (equal(*actual, v)) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const AnyFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return true; - } - } - return false; -} - -bool FilterEvaluator::operator()(const AllFilter& filter) const { - for (const auto& f: filter.filters) { - if (!Filter::visit(f, *this)) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const NoneFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const HasFilter& filter) const { - return bool(context.feature->getValue(filter.key)); -} - -bool FilterEvaluator::operator()(const NotHasFilter& filter) const { - return !context.feature->getValue(filter.key); -} - -bool FilterEvaluator::operator()(const TypeEqualsFilter& filter) const { - return context.feature->getType() == filter.value; -} - -bool FilterEvaluator::operator()(const TypeNotEqualsFilter& filter) const { - return context.feature->getType() != filter.value; -} - -bool FilterEvaluator::operator()(const TypeInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getType() == v) { - return true; - } - } - return false; -} - -bool FilterEvaluator::operator()(const TypeNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getType() == v) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const IdentifierEqualsFilter& filter) const { - return context.feature->getID() == filter.value; -} - -bool FilterEvaluator::operator()(const IdentifierNotEqualsFilter& filter) const { - return context.feature->getID() != filter.value; -} - -bool FilterEvaluator::operator()(const IdentifierInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getID() == v) { - return true; - } - } - return false; -} - -bool FilterEvaluator::operator()(const IdentifierNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getID() == v) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const HasIdentifierFilter&) const { - return bool(context.feature->getID()); -} - -bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const { - return !context.feature->getID(); -} - -bool FilterEvaluator::operator()(const ExpressionFilter& filter) const { - const expression::EvaluationResult result = filter.expression->evaluate(context); - if (result) { - const optional<bool> typed = expression::fromExpressionValue<bool>(*result); - return typed ? *typed : false; - } - return false; -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/text/collision_index.cpp b/src/mbgl/text/collision_index.cpp index 833658c33e..091840a371 100644 --- a/src/mbgl/text/collision_index.cpp +++ b/src/mbgl/text/collision_index.cpp @@ -219,7 +219,7 @@ std::pair<bool,bool> CollisionIndex::placeLineFeature(CollisionFeature& feature, } -void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement) { +void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId) { if (feature.alongLine) { for (auto& circle : feature.boxes) { if (!circle.used) { @@ -227,18 +227,18 @@ void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlaceme } if (ignorePlacement) { - ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius}); + ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius}); } else { - collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius}); + collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius}); } } } else { assert(feature.boxes.size() == 1); auto& box = feature.boxes[0]; if (ignorePlacement) { - ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); + ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); } else { - collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); + collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); } } } @@ -262,66 +262,41 @@ bool polygonIntersectsBox(const LineString<float>& polygon, const GridIndex<Inde return util::polygonIntersectsPolygon(integerPolygon, bboxPoints); } -std::vector<IndexedSubfeature> CollisionIndex::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, const UnwrappedTileID& tileID, const std::string& sourceID) const { - std::vector<IndexedSubfeature> result; +std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> CollisionIndex::queryRenderedSymbols(const ScreenLineString& queryGeometry) const { + std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> result; if (queryGeometry.empty() || (collisionGrid.empty() && ignoredGrid.empty())) { return result; } - - mat4 posMatrix; - mat4 projMatrix; - transformState.getProjMatrix(projMatrix); - transformState.matrixFor(posMatrix, tileID); - matrix::multiply(posMatrix, projMatrix, posMatrix); - - // queryGeometry is specified in integer tile units, but in projecting we switch to float pixels - LineString<float> projectedQuery; + + LineString<float> gridQuery; for (const auto& point : queryGeometry) { - auto projected = projectPoint(posMatrix, convertPoint<float>(point)); - projectedQuery.push_back(projected); + gridQuery.emplace_back(point.x + viewportPadding, point.y + viewportPadding); } - auto envelope = mapbox::geometry::envelope(projectedQuery); + auto envelope = mapbox::geometry::envelope(gridQuery); using QueryResult = std::pair<IndexedSubfeature, GridIndex<IndexedSubfeature>::BBox>; - std::vector<QueryResult> thisTileFeatures; std::vector<QueryResult> features = collisionGrid.queryWithBoxes(envelope); - - for (auto& queryResult : features) { - auto& feature = queryResult.first; - if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) { - // We only have to filter on the canonical ID because even if the feature is showing multiple times - // we treat it as one feature. - thisTileFeatures.push_back(queryResult); - } - } - std::vector<QueryResult> ignoredFeatures = ignoredGrid.queryWithBoxes(envelope); - for (auto& queryResult : ignoredFeatures) { - auto& feature = queryResult.first; - if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) { - thisTileFeatures.push_back(queryResult); - } - } + features.insert(features.end(), ignoredFeatures.begin(), ignoredFeatures.end()); - std::unordered_map<std::string, std::unordered_map<std::string, std::unordered_set<std::size_t>>> sourceLayerFeatures; - for (auto& queryResult : thisTileFeatures) { + std::unordered_map<uint32_t, std::unordered_set<size_t>> seenBuckets; + for (auto& queryResult : features) { auto& feature = queryResult.first; auto& bbox = queryResult.second; // Skip already seen features. - auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName][feature.bucketName]; + auto& seenFeatures = seenBuckets[feature.bucketInstanceId]; if (seenFeatures.find(feature.index) != seenFeatures.end()) continue; - - seenFeatures.insert(feature.index); - if (!polygonIntersectsBox(projectedQuery, bbox)) { + if (!polygonIntersectsBox(gridQuery, bbox)) { continue; } - result.push_back(feature); + seenFeatures.insert(feature.index); + result[feature.bucketInstanceId].push_back(feature); } return result; diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp index 8653c1d76c..b2be4c6ade 100644 --- a/src/mbgl/text/collision_index.hpp +++ b/src/mbgl/text/collision_index.hpp @@ -28,11 +28,10 @@ public: const bool pitchWithMap, const bool collisionDebug); - void insertFeature(CollisionFeature& feature, bool ignorePlacement); + void insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId); - std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, const UnwrappedTileID& tileID, const std::string& sourceID) const; + std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> queryRenderedSymbols(const ScreenLineString&) const; - private: bool isOffscreen(const CollisionBox&) const; bool isInsideGrid(const CollisionBox&) const; diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp index f88bab9d6f..b0c3511ce3 100644 --- a/src/mbgl/text/cross_tile_symbol_index.cpp +++ b/src/mbgl/text/cross_tile_symbol_index.cpp @@ -62,7 +62,7 @@ CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() { } bool CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& tileID, SymbolBucket& bucket, uint32_t& maxCrossTileID) { - auto thisZoomIndexes = indexes[tileID.overscaledZ]; + const auto& thisZoomIndexes = indexes[tileID.overscaledZ]; auto previousIndex = thisZoomIndexes.find(tileID); if (previousIndex != thisZoomIndexes.end()) { if (previousIndex->second.bucketInstanceId == bucket.bucketInstanceId) { @@ -153,6 +153,10 @@ bool CrossTileSymbolIndex::addLayer(RenderSymbolLayer& symbolLayer) { auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast<SymbolBucket*>(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket); + if (symbolBucket.bucketLeaderID != symbolLayer.getID()) { + // Only add this layer if it's the "group leader" for the bucket + continue; + } if (!symbolBucket.bucketInstanceId) { symbolBucket.bucketInstanceId = ++maxBucketInstanceId; diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 54b2b7539b..9883a1f456 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -49,17 +49,25 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri if (!renderTile.tile.isRenderable()) { continue; } - - auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl); + assert(dynamic_cast<GeometryTile*>(&renderTile.tile)); + GeometryTile& geometryTile = static_cast<GeometryTile&>(renderTile.tile); + + + auto bucket = geometryTile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast<SymbolBucket*>(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket); + + if (symbolBucket.bucketLeaderID != symbolLayer.getID()) { + // Only place this layer if it's the "group leader" for the bucket + continue; + } auto& layout = symbolBucket.layout; const float pixelsToTileUnits = renderTile.id.pixelsToTileUnits(1, state.getZoom()); - const float scale = std::pow(2, state.getZoom() - renderTile.tile.id.overscaledZ); - const float textPixelRatio = (util::tileSize * renderTile.tile.id.overscaleFactor()) / util::EXTENT; + const float scale = std::pow(2, state.getZoom() - geometryTile.id.overscaledZ); + const float textPixelRatio = (util::tileSize * geometryTile.id.overscaleFactor()) / util::EXTENT; mat4 posMatrix; state.matrixFor(posMatrix, renderTile.id); @@ -76,7 +84,14 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri layout.get<style::IconRotationAlignment>() == style::AlignmentType::Map, state, pixelsToTileUnits); - + + + // As long as this placement lives, we have to hold onto this bucket's + // matching FeatureIndex/data for querying purposes + retainedQueryData.emplace(std::piecewise_construct, + std::forward_as_tuple(symbolBucket.bucketInstanceId), + std::forward_as_tuple(symbolBucket.bucketInstanceId, geometryTile.getFeatureIndex(), geometryTile.id)); + placeLayerBucket(symbolBucket, posMatrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, textPixelRatio, showCollisionBoxes, seenCrossTileIDs, renderTile.tile.holdForFade()); } } @@ -150,11 +165,11 @@ void Placement::placeLayerBucket( } if (placeText) { - collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<style::TextIgnorePlacement>()); + collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId); } if (placeIcon) { - collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>()); + collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId); } assert(symbolInstance.crossTileID != 0); @@ -220,6 +235,10 @@ void Placement::updateLayerOpacities(RenderSymbolLayer& symbolLayer) { auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast<SymbolBucket*>(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket); + if (symbolBucket.bucketLeaderID != symbolLayer.getID()) { + // Only update opacities this layer if it's the "group leader" for the bucket + continue; + } updateBucketOpacities(symbolBucket, seenCrossTileIDs); } } @@ -305,6 +324,10 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>& bucket.updateOpacity(); bucket.sortFeatures(state.getAngle()); + auto retainedData = retainedQueryData.find(bucket.bucketInstanceId); + if (retainedData != retainedQueryData.end()) { + retainedData->second.featureSortOrder = bucket.featureSortOrder; + } } float Placement::symbolFadeChange(TimePoint now) const { @@ -337,5 +360,13 @@ void Placement::setStale() { const CollisionIndex& Placement::getCollisionIndex() const { return collisionIndex; } + +const RetainedQueryData& Placement::getQueryData(uint32_t bucketInstanceId) const { + auto it = retainedQueryData.find(bucketInstanceId); + if (it == retainedQueryData.end()) { + throw std::runtime_error("Placement::getQueryData with unrecognized bucketInstanceId"); + } + return it->second; +} } // namespace mbgl diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp index 653ae352ed..0e1751b127 100644 --- a/src/mbgl/text/placement.hpp +++ b/src/mbgl/text/placement.hpp @@ -44,7 +44,21 @@ public: // visible right away. const bool skipFade; }; - + +struct RetainedQueryData { + uint32_t bucketInstanceId; + std::shared_ptr<FeatureIndex> featureIndex; + OverscaledTileID tileID; + std::shared_ptr<std::vector<size_t>> featureSortOrder; + + RetainedQueryData(uint32_t bucketInstanceId_, + std::shared_ptr<FeatureIndex> featureIndex_, + OverscaledTileID tileID_) + : bucketInstanceId(bucketInstanceId_) + , featureIndex(std::move(featureIndex_)) + , tileID(std::move(tileID_)) {} +}; + class Placement { public: Placement(const TransformState&, MapMode mapMode); @@ -59,6 +73,8 @@ public: bool stillRecent(TimePoint now) const; void setRecent(TimePoint now); void setStale(); + + const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const; private: void placeLayerBucket( @@ -85,6 +101,8 @@ private: TimePoint recentUntil; bool stale = false; + + std::unordered_map<uint32_t, RetainedQueryData> retainedQueryData; }; } // namespace mbgl diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp index a2fefcfa9f..272a1594d4 100644 --- a/src/mbgl/tile/custom_geometry_tile.cpp +++ b/src/mbgl/tile/custom_geometry_tile.cpp @@ -3,7 +3,6 @@ #include <mbgl/renderer/query.hpp> #include <mbgl/renderer/tile_parameters.hpp> #include <mbgl/actor/scheduler.hpp> -#include <mbgl/style/filter_evaluator.hpp> #include <mbgl/util/string.hpp> #include <mbgl/tile/tile_observer.hpp> #include <mbgl/style/custom_tile_loader.hpp> diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index f211c03569..7a83da2267 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -2,7 +2,6 @@ #include <mbgl/tile/geojson_tile_data.hpp> #include <mbgl/renderer/query.hpp> #include <mbgl/renderer/tile_parameters.hpp> -#include <mbgl/style/filter_evaluator.hpp> namespace mbgl { diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index a99cb91d26..af28fe3963 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -16,7 +16,6 @@ #include <mbgl/storage/file_source.hpp> #include <mbgl/geometry/feature_index.hpp> #include <mbgl/map/transform_state.hpp> -#include <mbgl/style/filter_evaluator.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/actor/scheduler.hpp> @@ -133,7 +132,7 @@ void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelatio buckets = std::move(result.buckets); - featureIndexPendingCommit = { std::move(result.featureIndex) }; + latestFeatureIndex = std::move(result.featureIndex); if (result.glyphAtlasImage) { glyphAtlasImage = std::move(*result.glyphAtlasImage); @@ -201,13 +200,15 @@ Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { return it->second.get(); } -void GeometryTile::commitFeatureIndex() { - // We commit our pending FeatureIndex when a global placement has run, - // synchronizing the global CollisionIndex with the latest buckets/FeatureIndex - if (featureIndexPendingCommit) { - featureIndex = std::move(*featureIndexPendingCommit); - featureIndexPendingCommit = nullopt; +float GeometryTile::getQueryPadding(const std::vector<const RenderLayer*>& layers) { + float queryPadding = 0; + for (const RenderLayer* layer : layers) { + auto bucket = getBucket(*layer->baseImpl); + if (bucket && bucket->hasData()) { + queryPadding = std::max(queryPadding, bucket->getQueryRadius(*layer)); + } } + return queryPadding; } void GeometryTile::queryRenderedFeatures( @@ -216,30 +217,26 @@ void GeometryTile::queryRenderedFeatures( const TransformState& transformState, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) { + const mat4& projMatrix) { if (!getData()) return; - // Determine the additional radius needed factoring in property functions - float additionalRadius = 0; - for (const RenderLayer* layer : layers) { - auto bucket = getBucket(*layer->baseImpl); - if (bucket) { - additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(*layer)); - } - } - - featureIndex->query(result, - queryGeometry, - transformState.getAngle(), - util::tileSize * id.overscaleFactor(), - std::pow(2, transformState.getZoom() - id.overscaledZ), - options, - id.toUnwrapped(), - sourceID, - layers, - collisionIndex, - additionalRadius); + const float queryPadding = getQueryPadding(layers); + + mat4 posMatrix; + transformState.matrixFor(posMatrix, id.toUnwrapped()); + matrix::multiply(posMatrix, projMatrix, posMatrix); + + latestFeatureIndex->query(result, + queryGeometry, + transformState, + posMatrix, + util::tileSize * id.overscaleFactor(), + std::pow(2, transformState.getZoom() - id.overscaledZ), + options, + id.toUnwrapped(), + layers, + queryPadding * transformState.maxPitchScaleFactor()); } void GeometryTile::querySourceFeatures( diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 418db4a0b2..af122474c2 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -5,7 +5,6 @@ #include <mbgl/renderer/image_manager.hpp> #include <mbgl/text/glyph_manager.hpp> #include <mbgl/util/feature.hpp> -#include <mbgl/util/throttler.hpp> #include <mbgl/actor/actor.hpp> #include <mbgl/geometry/feature_index.hpp> @@ -55,12 +54,14 @@ public: const TransformState&, const std::vector<const RenderLayer*>& layers, const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) override; + const mat4& projMatrix) override; void querySourceFeatures( std::vector<Feature>& result, const SourceQueryOptions&) override; + float getQueryPadding(const std::vector<const RenderLayer*>&) override; + void cancel() override; class LayoutResult { @@ -88,11 +89,11 @@ public: void markRenderedPreviously() override; void performedFadePlacement() override; - void commitFeatureIndex() override; + const std::shared_ptr<FeatureIndex> getFeatureIndex() const { return latestFeatureIndex; } protected: const GeometryTileData* getData() { - return featureIndex ? featureIndex->getData() : nullptr; + return latestFeatureIndex ? latestFeatureIndex->getData() : nullptr; } private: @@ -113,8 +114,7 @@ private: std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; - optional<std::unique_ptr<FeatureIndex>> featureIndexPendingCommit; - std::unique_ptr<FeatureIndex> featureIndex; + std::shared_ptr<FeatureIndex> latestFeatureIndex; optional<AlphaImage> glyphAtlasImage; optional<PremultipliedImage> iconAtlasImage; diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 1378ad5d3a..ca20c4b8ab 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -5,7 +5,6 @@ #include <mbgl/renderer/bucket_parameters.hpp> #include <mbgl/renderer/group_by_layout.hpp> #include <mbgl/style/filter.hpp> -#include <mbgl/style/filter_evaluator.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/renderer/layers/render_symbol_layer.hpp> #include <mbgl/renderer/buckets/symbol_bucket.hpp> @@ -441,8 +440,7 @@ void GeometryTileWorker::performSymbolLayout() { } symbolLayout->prepare(glyphMap, glyphAtlas.positions, - imageMap, imageAtlas.positions, - id, sourceID); + imageMap, imageAtlas.positions); } symbolLayoutsNeedPreparation = false; diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index 88db2ba07c..b95944f10e 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -38,7 +38,11 @@ void Tile::queryRenderedFeatures( const TransformState&, const std::vector<const RenderLayer*>&, const RenderedQueryOptions&, - const CollisionIndex&) {} + const mat4&) {} + +float Tile::getQueryPadding(const std::vector<const RenderLayer*>&) { + return 0; +} void Tile::querySourceFeatures( std::vector<Feature>&, diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 23365c6ae3..23d6864205 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -58,12 +58,14 @@ public: const TransformState&, const std::vector<const RenderLayer*>&, const RenderedQueryOptions& options, - const CollisionIndex&); + const mat4& projMatrix); virtual void querySourceFeatures( std::vector<Feature>& result, const SourceQueryOptions&); + virtual float getQueryPadding(const std::vector<const RenderLayer*>&); + void setTriedCache(); // Returns true when the tile source has received a first response, regardless of whether a load @@ -109,11 +111,6 @@ public: // and will have time to finish by the second placement. virtual void performedFadePlacement() {} - // FeatureIndexes are loaded asynchronously, but must be used with a CollisionIndex - // generated from the same data. Calling commitFeatureIndex signals the current - // CollisionIndex is up-to-date and allows us to start using the last loaded FeatureIndex - virtual void commitFeatureIndex() {} - void dumpDebugLogs() const; const OverscaledTileID id; diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp index 30e813cbb8..ee3ebe7cae 100644 --- a/src/mbgl/util/compression.cpp +++ b/src/mbgl/util/compression.cpp @@ -1,6 +1,6 @@ #include <mbgl/util/compression.hpp> -#if defined(__QT__) && defined(_WINDOWS) +#if defined(__QT__) && defined(_WINDOWS) && !defined(__GNUC__) #include <QtZlib/zlib.h> #else #include <zlib.h> diff --git a/src/mbgl/util/http_header.cpp b/src/mbgl/util/http_header.cpp index 5921edfb14..8048dfe84a 100644 --- a/src/mbgl/util/http_header.cpp +++ b/src/mbgl/util/http_header.cpp @@ -8,6 +8,7 @@ #pragma GCC diagnostic ignored "-Wshadow" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshorten-64-to-32" +#pragma clang diagnostic ignored "-Wtautological-constant-compare" #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix_core.hpp> #include <boost/spirit/include/phoenix_operator.hpp> diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp index 1fc13bfb7d..5530796915 100644 --- a/src/mbgl/util/i18n.cpp +++ b/src/mbgl/util/i18n.cpp @@ -1,4 +1,5 @@ -#include "i18n.hpp" +#include <mbgl/util/i18n.hpp> +#include <mbgl/util/utf.hpp> #include <algorithm> #include <map> @@ -65,7 +66,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabics, 0x1400, 0x167F) // DEFINE_IS_IN_UNICODE_BLOCK(Hanunoo, 0x1720, 0x173F) // DEFINE_IS_IN_UNICODE_BLOCK(Buhid, 0x1740, 0x175F) // DEFINE_IS_IN_UNICODE_BLOCK(Tagbanwa, 0x1760, 0x177F) -// DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF) +DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF) // DEFINE_IS_IN_UNICODE_BLOCK(Mongolian, 0x1800, 0x18AF) DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabicsExtended, 0x18B0, 0x18FF) // DEFINE_IS_IN_UNICODE_BLOCK(Limbu, 0x1900, 0x194F) @@ -581,6 +582,38 @@ std::u16string verticalizePunctuation(const std::u16string& input) { char16_t verticalizePunctuation(char16_t chr) { return verticalPunctuation.count(chr) ? verticalPunctuation.at(chr) : 0; } + +bool charInSupportedScript(char16_t chr) { + // This is a rough heuristic: whether we "can render" a script + // actually depends on the properties of the font being used + // and whether differences from the ideal rendering are considered + // semantically significant. + + // Even in Latin script, we "can't render" combinations such as the fi + // ligature, but we don't consider that semantically significant.n false; + if ((chr >= 0x0900 && chr <= 0x0DFF) || + // Main blocks for Indic scripts and Sinhala + (chr >= 0x0F00 && chr <= 0x109F) || + // Main blocks for Tibetan and Myanmar + isInKhmer(chr)) { + // These blocks cover common scripts that require + // complex text shaping, based on unicode script metadata: + // http://www.unicode.org/repos/cldr/trunk/common/properties/scriptMetadata.txt + // where "Web Rank <= 32" "Shaping Required = YES" + return false; + } + return true; +} + +bool isStringInSupportedScript(const std::string& input) { + auto u16string = util::utf8_to_utf16::convert(input); + for (char16_t chr : u16string) { + if (!charInSupportedScript(chr)) { + return false; + } + } + return true; +} } // namespace i18n } // namespace util diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp index b3960c743c..a74215a134 100644 --- a/src/mbgl/util/i18n.hpp +++ b/src/mbgl/util/i18n.hpp @@ -72,6 +72,8 @@ std::u16string verticalizePunctuation(const std::u16string& input); @return The character’s specialized vertical form; 0 if not applicable. */ char16_t verticalizePunctuation(char16_t chr); + +bool isStringInSupportedScript(const std::string& input); } // namespace i18n } // namespace util diff --git a/src/mbgl/util/intersection_tests.cpp b/src/mbgl/util/intersection_tests.cpp index e6ce245c0e..780fce98f9 100644 --- a/src/mbgl/util/intersection_tests.cpp +++ b/src/mbgl/util/intersection_tests.cpp @@ -82,11 +82,16 @@ bool lineIntersectsBufferedLine(const GeometryCoordinates& lineA, const Geometry return false; } +bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius) { + if (polygonContainsPoint(polygon, point)) return true; + if (pointIntersectsBufferedLine(point, polygon, radius)) return true; + return false; +} + bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates& polygon, const GeometryCollection& rings, float radius) { for (auto& ring : rings) { for (auto& point : ring) { - if (polygonContainsPoint(polygon, point)) return true; - if (pointIntersectsBufferedLine(point, polygon, radius)) return true; + if (polygonIntersectsBufferedPoint(polygon, point, radius)) return true; } } return false; diff --git a/src/mbgl/util/intersection_tests.hpp b/src/mbgl/util/intersection_tests.hpp index 5bcb29c767..c105fe4dd0 100644 --- a/src/mbgl/util/intersection_tests.hpp +++ b/src/mbgl/util/intersection_tests.hpp @@ -9,6 +9,7 @@ bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates&, const Geome bool polygonIntersectsBufferedMultiLine(const GeometryCoordinates&, const GeometryCollection&, float radius); bool polygonIntersectsPolygon(const GeometryCoordinates&, const GeometryCoordinates&); bool polygonIntersectsMultiPolygon(const GeometryCoordinates&, const GeometryCollection&); +bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius); } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp index 6a6ed7b250..058cd0d202 100644 --- a/src/mbgl/util/io.cpp +++ b/src/mbgl/util/io.cpp @@ -20,7 +20,7 @@ void write_file(const std::string &filename, const std::string &data) { } std::string read_file(const std::string &filename) { - std::ifstream file(filename); + std::ifstream file(filename, std::ios::binary); if (file.good()) { std::stringstream data; data << file.rdbuf(); @@ -31,7 +31,7 @@ std::string read_file(const std::string &filename) { } optional<std::string> readFile(const std::string &filename) { - std::ifstream file(filename); + std::ifstream file(filename, std::ios::binary); if (file.good()) { std::stringstream data; data << file.rdbuf(); diff --git a/src/mbgl/util/throttler.cpp b/src/mbgl/util/throttler.cpp deleted file mode 100644 index 910810ce2f..0000000000 --- a/src/mbgl/util/throttler.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include <mbgl/util/throttler.hpp> - -namespace mbgl { -namespace util { - -Throttler::Throttler(Duration frequency_, std::function<void()>&& function_) - : frequency(frequency_) - , function(std::move(function_)) - , pendingInvocation(false) - , lastInvocation(TimePoint::min()) -{} - -void Throttler::invoke() { - if (pendingInvocation) { - return; - } - - Duration timeToNextInvocation = lastInvocation == TimePoint::min() - ? Duration::zero() - : (lastInvocation + frequency) - Clock::now(); - - if (timeToNextInvocation <= Duration::zero()) { - lastInvocation = Clock::now(); - function(); - } else { - pendingInvocation = true; - timer.start(timeToNextInvocation, Duration::zero(), [this]{ - pendingInvocation = false; - lastInvocation = Clock::now(); - function(); - }); - } -} - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/throttler.hpp b/src/mbgl/util/throttler.hpp deleted file mode 100644 index 175de7ccaf..0000000000 --- a/src/mbgl/util/throttler.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#include <mbgl/util/chrono.hpp> -#include <mbgl/util/timer.hpp> - -namespace mbgl { -namespace util { - -class Throttler { -public: - Throttler(Duration frequency, std::function<void()>&& function); - - void invoke(); -private: - Duration frequency; - std::function<void()> function; - - Timer timer; - bool pendingInvocation; - TimePoint lastInvocation; -}; - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index 39b562d811..488e6b88ce 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -2,13 +2,18 @@ #include <mbgl/util/constants.hpp> #include <mbgl/util/interpolate.hpp> #include <mbgl/map/transform_state.hpp> +#include <mbgl/util/tile_cover_impl.hpp> +#include <mbgl/util/tile_coordinate.hpp> #include <functional> +#include <list> namespace mbgl { namespace { +using ScanLine = const std::function<void(int32_t x0, int32_t x1, int32_t y)>; + // Taken from polymaps src/Layer.js // https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383 struct edge { @@ -27,8 +32,6 @@ struct edge { } }; -using ScanLine = const std::function<void(int32_t x0, int32_t x1, int32_t y)>; - // scan-line conversion static void scanSpans(edge e0, edge e1, int32_t ymin, int32_t ymax, ScanLine scanLine) { double y0 = ::fmax(ymin, std::floor(e1.y0)); @@ -147,11 +150,11 @@ std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) { { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() }); return tileCover( - TileCoordinate::fromLatLng(z, bounds.northwest()).p, - TileCoordinate::fromLatLng(z, bounds.northeast()).p, - TileCoordinate::fromLatLng(z, bounds.southeast()).p, - TileCoordinate::fromLatLng(z, bounds.southwest()).p, - TileCoordinate::fromLatLng(z, bounds.center()).p, + Projection::project(bounds.northwest(), z), + Projection::project(bounds.northeast(), z), + Projection::project(bounds.southeast(), z), + Projection::project(bounds.southwest(), z), + Projection::project(bounds.center(), z), z); } @@ -169,25 +172,80 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) { z); } +std::vector<UnwrappedTileID> tileCover(const Geometry<double>& geometry, int32_t z) { + std::vector<UnwrappedTileID> result; + TileCover tc(geometry, z, true); + while (tc.hasNext()) { + result.push_back(*tc.next()); + }; + + return result; +} + // Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs // Computes the projected tiles for the lower left and upper right points of the bounds // and uses that to compute the tile cover count -uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_){ - - auto sw = Projection::project(bounds.southwest().wrapped(), zoom, tileSize_); - auto ne = Projection::project(bounds.northeast().wrapped(), zoom, tileSize_); - - auto x1 = floor(sw.x/ tileSize_); - auto x2 = floor((ne.x - 1) / tileSize_); - auto y1 = floor(sw.y/ tileSize_); - auto y2 = floor((ne.y - 1) / tileSize_); - - auto minX = ::fmax(std::min(x1, x2), 0); - auto maxX = std::max(x1, x2); - auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2); - auto maxY = (std::pow(2, zoom) - 1) - ::fmax(std::min(y1, y2), 0); - - return (maxX - minX + 1) * (maxY - minY + 1); +uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom){ + if (zoom == 0) { + return 1; + } + auto sw = Projection::project(bounds.southwest(), zoom); + auto ne = Projection::project(bounds.northeast(), zoom); + auto maxTile = std::pow(2.0, zoom); + auto x1 = floor(sw.x); + auto x2 = ceil(ne.x) - 1; + auto y1 = util::clamp(floor(sw.y), 0.0, maxTile - 1); + auto y2 = util::clamp(floor(ne.y), 0.0, maxTile - 1); + + auto dx = x1 > x2 ? (maxTile - x1) + x2 : x2 - x1; + auto dy = y1 - y2; + return (dx + 1) * (dy + 1); +} + +uint64_t tileCount(const Geometry<double>& geometry, uint8_t z) { + uint64_t tileCount = 0; + + TileCover tc(geometry, z, true); + while (tc.next()) { + tileCount++; + }; + return tileCount; +} + +TileCover::TileCover(const LatLngBounds&bounds_, int32_t z) { + LatLngBounds bounds = LatLngBounds::hull( + { std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() }, + { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() }); + + if (bounds.isEmpty() || + bounds.south() > util::LATITUDE_MAX || + bounds.north() < -util::LATITUDE_MAX) { + bounds = LatLngBounds::world(); + } + + auto sw = Projection::project(bounds.southwest(), z); + auto ne = Projection::project(bounds.northeast(), z); + auto se = Projection::project(bounds.southeast(), z); + auto nw = Projection::project(bounds.northwest(), z); + + Polygon<double> p({ {sw, nw, ne, se, sw} }); + impl = std::make_unique<TileCover::Impl>(z, p, false); +} + +TileCover::TileCover(const Geometry<double>& geom, int32_t z, bool project/* = true*/) + : impl( std::make_unique<TileCover::Impl>(z, geom, project)) { +} + +TileCover::~TileCover() { + +} + +optional<UnwrappedTileID> TileCover::next() { + return impl->next(); +} + +bool TileCover::hasNext() { + return impl->hasNext(); } } // namespace util diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp index b2098b59b8..c953d764d2 100644 --- a/src/mbgl/util/tile_cover.hpp +++ b/src/mbgl/util/tile_cover.hpp @@ -2,9 +2,11 @@ #include <mbgl/tile/tile_id.hpp> #include <mbgl/style/types.hpp> -#include <mbgl/util/tile_coordinate.hpp> +#include <mbgl/util/geometry.hpp> +#include <mbgl/util/optional.hpp> #include <vector> +#include <memory> namespace mbgl { @@ -13,13 +15,31 @@ class LatLngBounds; namespace util { +// Helper class to stream tile-cover results per row +class TileCover { +public: + TileCover(const LatLngBounds&, int32_t z); + // When project == true, projects the geometry points to tile coordinates + TileCover(const Geometry<double>&, int32_t z, bool project = true); + ~TileCover(); + + optional<UnwrappedTileID> next(); + bool hasNext(); + +private: + class Impl; + std::unique_ptr<Impl> impl; +}; + int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize); std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z); std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z); +std::vector<UnwrappedTileID> tileCover(const Geometry<double>&, int32_t z); // Compute only the count of tiles needed for tileCover -uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize); +uint64_t tileCount(const LatLngBounds&, uint8_t z); +uint64_t tileCount(const Geometry<double>&, uint8_t z); } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/tile_cover_impl.cpp b/src/mbgl/util/tile_cover_impl.cpp new file mode 100644 index 0000000000..b3fc07f7dd --- /dev/null +++ b/src/mbgl/util/tile_cover_impl.cpp @@ -0,0 +1,365 @@ +#include <mbgl/util/tile_cover_impl.hpp> +#include <mbgl/util/tile_coordinate.hpp> + +#include <functional> +#include <cmath> +#include <assert.h> +#include <limits.h> +#include <algorithm> + +namespace mbgl { +namespace util { + +using PointList = std::vector<Point<double>>; + +struct TileSpan { + int32_t xmin, xmax; + bool winding; +}; + + +// Find the first local minimum going forward in the list. +void start_list_on_local_minimum(PointList& points) { + auto prev_pt = std::prev(points.end(), 2); + auto pt = points.begin(); + auto next_pt = std::next(pt); + while (pt != points.end()) { + if ((pt->y <= prev_pt->y) && + (pt->y < next_pt->y)) { + break; + } + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } + } + //Re-close linear rings with first_pt = last_pt + if (points.back() == points.front()) { + points.pop_back(); + } + std::rotate(points.begin(), pt, points.end()); + points.push_back(*points.begin()); +} + +//Create a bound towards a local maximum point, starting from pt. +Bound create_bound_towards_maximum(PointList& points, PointList::iterator& pt) { + if (std::distance(pt, points.end()) < 2) { return {}; } + if (std::distance(pt, points.end()) == 2) { + Bound bnd; + if (pt->y < std::next(pt)->y) { + std::copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = true; + } + else { + std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = false; + } + pt = points.end(); + return bnd; + } + const auto begin = pt; + auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt); + auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt); + while (pt != points.end()) { + if ((pt->y >= prev_pt->y) && + (pt->y > next_pt->y )) { + break; + } + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } + } + + Bound bnd; + if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; }; + bnd.points.reserve(static_cast<std::size_t>(std::distance(begin, next_pt))); + std::copy(begin, next_pt, std::back_inserter(bnd.points)); + bnd.winding = true; + return bnd; +} + +//Create a bound towards a local minimum point, starting from pt. +Bound create_bound_towards_minimum(PointList& points, PointList::iterator& pt) { + if (std::distance(pt, points.end()) < 2) { return {}; } + if (std::distance(pt, points.end()) == 2) { + Bound bnd; + if (pt->y < std::next(pt)->y) { + std::copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = true; + } + else { + std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = false; + } + pt = points.end(); + return bnd; + } + auto begin = pt; + auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt); + auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt); + while (pt != points.end()) { + if ((pt->y <= prev_pt->y) && + (pt->y < next_pt->y)) { + break; + } + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } + } + + Bound bnd; + if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; }; + bnd.points.reserve(static_cast<std::size_t>(std::distance(begin, next_pt))); + //For bounds that start at a max, reverse copy so that all bounds start at a min + std::reverse_copy(begin, next_pt, std::back_inserter(bnd.points)); + bnd.winding = false; + return bnd; +} + +//Build a map of bounds and their starting Y tile coordinate. +void build_bounds_map(PointList& points, uint32_t maxTile, BoundsMap& et, bool closed = false) { + if (points.size() < 2) return; + //While traversing closed rings, start the bounds at a local minimum + if (closed) { + start_list_on_local_minimum(points); + } + + auto pointsIter = points.begin(); + while (pointsIter != points.end()) { + Bound to_max = create_bound_towards_maximum(points, pointsIter); + Bound to_min = create_bound_towards_minimum(points, pointsIter); + + if (to_max.points.size() > 0) { + // Projections may result in values beyond the bounds, clamp to max tile coordinates + const auto y = static_cast<uint32_t>(std::floor(clamp(to_max.points.front().y, 0.0, (double)maxTile))); + et[y].push_back(to_max); + } + if (to_min.points.size() > 0) { + const auto y = static_cast<uint32_t>(std::floor(clamp(to_min.points.front().y, 0.0, (double)maxTile))); + et[y].push_back(to_min); + } + } + assert(pointsIter == points.end()); +} + +void update_span(TileSpan& xp, double x) { + xp.xmin = std::min(xp.xmin, static_cast<int32_t>(std::floor(x))); + xp.xmax = std::max(xp.xmax, static_cast<int32_t>(std::ceil(x))); +} + +//Build a vector of X tile-coordinates spanned by each bound. +std::vector<TileSpan> scan_row(uint32_t y, Bounds& aet) { + std::vector<TileSpan> tile_range; + tile_range.reserve(aet.size()); + + for(Bound& b: aet) { + TileSpan xp = { INT_MAX, 0, b.winding }; + double x; + const auto numEdges = b.points.size() - 1; + assert(numEdges >= 1); + while (b.currentPoint < numEdges) { + x = b.interpolate(y); + update_span(xp, x); + + // If this edge ends beyond the current row, find the x-intercept where + // it exits the row + auto& p1 = b.points[b.currentPoint + 1]; + if (p1.y > y+1) { + x = b.interpolate(y+1); + update_span(xp, x); + break; + } else if(b.currentPoint == numEdges - 1) { + // For last edge, consider x-intercept at the end of the edge. + x = p1.x; + update_span(xp, x); + } + b.currentPoint++; + } + tile_range.push_back(xp); + } + // Erase bounds in the active table whose current edge ends inside this row, + // or there are no more edges + auto bound = aet.begin(); + while (bound != aet.end()) { + if ( bound->currentPoint == bound->points.size() - 1 && + bound->points[bound->currentPoint].y <= y+1) { + bound = aet.erase(bound); + } else { + bound++; + } + } + // Sort the X-extents of each crossing bound by x_min, x_max + std::sort(tile_range.begin(), tile_range.end(), [] (TileSpan& a, TileSpan& b) { + return std::tie(a.xmin, a.xmax) < std::tie(b.xmin, b.xmax); + }); + + return tile_range; +} + +struct BuildBoundsMap { + int32_t zoom; + bool project = false; + BuildBoundsMap(int32_t z, bool p): zoom(z), project(p) {} + + void buildTable(const std::vector<Point<double>>& points, BoundsMap& et, bool closed = false) const { + PointList projectedPoints; + if (project) { + projectedPoints.reserve(points.size()); + for(const auto&p : points) { + projectedPoints.push_back( + Projection::project(LatLng{ p.y, p.x }, zoom)); + } + } else { + projectedPoints.insert(projectedPoints.end(), points.begin(), points.end()); + } + build_bounds_map(projectedPoints, 1 << zoom, et, closed); + } + + void buildPolygonTable(const Polygon<double>& polygon, BoundsMap& et) const { + for(const auto&ring : polygon) { + buildTable(ring, et, true); + } + } + BoundsMap operator()(const Point<double>&p) const { + Bound bnd; + auto point = p; + if(project) { + point = Projection::project(LatLng{p.y, p.x}, zoom); + } + bnd.points.insert(bnd.points.end(), 2, point); + bnd.winding = false; + BoundsMap et; + const auto y = static_cast<uint32_t>(std::floor(clamp(point.y, 0.0, (double)(1 << zoom)))); + et[y].push_back(bnd); + return et; + } + + BoundsMap operator()(const MultiPoint<double>& points) const { + BoundsMap et; + for (const Point<double>& p: points) { + Bound bnd; + auto point = p; + if(project) { + point = Projection::project(LatLng{p.y, p.x}, zoom); + } + bnd.points.insert(bnd.points.end(), 2, point); + bnd.winding = false; + const auto y = static_cast<uint32_t>(std::floor(clamp(point.y, 0.0, (double)(1 << zoom)))); + et[y].push_back(bnd); + } + return et; + } + + BoundsMap operator()(const LineString<double>& lines) const { + BoundsMap et; + buildTable(lines, et); + return et; + } + + BoundsMap operator()(const MultiLineString<double>& lines) const { + BoundsMap et; + for(const auto&line : lines) { + buildTable(line, et); + } + return et; + } + + BoundsMap operator()(const Polygon<double>& polygon) const { + BoundsMap et; + buildPolygonTable(polygon, et); + return et; + } + + BoundsMap operator()(const MultiPolygon<double>& polygons) const { + BoundsMap et; + for(const auto& polygon: polygons) { + buildPolygonTable(polygon, et); + } + return et; + } + + BoundsMap operator()(const mapbox::geometry::geometry_collection<double>&) const { + return {}; + } +}; + +TileCover::Impl::Impl(int32_t z, const Geometry<double>& geom, bool project) + : zoom(z) { + ToFeatureType toFeatureType; + isClosed = apply_visitor(toFeatureType, geom) == FeatureType::Polygon; + + BuildBoundsMap toBoundsMap(z, project); + boundsMap = apply_visitor(toBoundsMap, geom); + if (boundsMap.size() == 0) return; + + //Iniitalize the active edge table, and current row span + currentBounds = boundsMap.begin(); + tileY = 0; + nextRow(); + if (tileXSpans.empty()) return; + tileX = tileXSpans.front().first; +} + +void TileCover::Impl::nextRow() { + // Update AET for next row + if (currentBounds != boundsMap.end()) { + if (activeBounds.size() == 0 && currentBounds->first > tileY) { + //For multi-geoms: use the next row with an edge table starting point + tileY = currentBounds->first; + } + if (tileY == currentBounds->first) { + + std::move(currentBounds->second.begin(), currentBounds->second.end(), std::back_inserter(activeBounds)); + currentBounds++; + } + } + //Scan aet and update currenRange with x_min, x_max pairs + auto xps = util::scan_row(tileY, activeBounds); + if (xps.size() == 0) { + return; + } + + auto x_min = xps[0].xmin; + auto x_max = xps[0].xmax; + int32_t nzRule = xps[0].winding ? 1 : -1; + for (size_t i = 1; i < xps.size(); i++) { + auto xp = xps[i]; + if (!(isClosed && nzRule != 0)) { + if (xp.xmin > x_max && xp.xmax >= x_max) { + tileXSpans.emplace(x_min, x_max); + x_min = xp.xmin; + } + } + nzRule += xp.winding ? 1 : -1; + x_max = std::max(x_min, xp.xmax); + } + tileXSpans.emplace(x_min, x_max); +} + +bool TileCover::Impl::hasNext() const { + return (!tileXSpans.empty() && tileX < tileXSpans.front().second && tileY < (1u << zoom)); +} + +optional<UnwrappedTileID> TileCover::Impl::next() { + if (!hasNext()) return {}; + + const auto x = tileX; + const auto y = tileY; + tileX++; + if (tileX >= tileXSpans.front().second) { + tileXSpans.pop(); + if(tileXSpans.empty()) { + tileY++; + nextRow(); + } + if (!tileXSpans.empty()) { + tileX = tileXSpans.front().first; + } + } + return UnwrappedTileID(zoom, x, y); +} + +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/tile_cover_impl.hpp b/src/mbgl/util/tile_cover_impl.hpp new file mode 100644 index 0000000000..7c16718984 --- /dev/null +++ b/src/mbgl/util/tile_cover_impl.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include <mbgl/util/tile_cover.hpp> +#include <mbgl/util/geometry.hpp> +#include <mbgl/util/optional.hpp> + +#include <vector> +#include <map> +#include <queue> + +namespace mbgl { + +class TransformState; +class LatLngBounds; + +namespace util { + +struct Bound; + +using Bounds = std::vector<Bound>; +using BoundsMap = std::map<uint32_t, Bounds>; + +// A chain of points from a local minimum to a local maximum. `winding` indicates +// the direction of the original geometry. +struct Bound { + std::vector<Point<double>> points; + size_t currentPoint = 0; + bool winding = false; + + Bound() = default; + Bound(const Bound& rhs) { + points = rhs.points; + currentPoint = rhs.currentPoint; + winding = rhs.winding; + } + Bound& operator=(Bound&& rhs) { + points = std::move(rhs.points); + currentPoint = rhs.currentPoint; + winding = rhs.winding; + return *this; + } + + // Compute the interpolated x coordinate at y for the current edge + double interpolate(uint32_t y) { + const auto& p0 = points[currentPoint]; + const auto& p1 = points[currentPoint + 1]; + + const auto dx = p1.x - p0.x; + const auto dy = p1.y - p0.y; + auto x = p0.x; + if (dx == 0) { + return x; + } else if (dy == 0){ + return y <= p0.y ? p0.x : p1.x; + } + if (y < p0.y) return x; + if (y > p1.y) return p1.x; + x = (dx / dy) * (y - p0.y) + p0.x; + return x; + } +}; + +class TileCover::Impl { +public: + Impl(int32_t z, const Geometry<double>& geom, bool project = true); + ~Impl() = default; + + optional<UnwrappedTileID> next(); + bool hasNext() const; + +private: + using TileSpans = std::queue<std::pair<int32_t, int32_t>>; + + void nextRow(); + + const int32_t zoom; + bool isClosed; + + BoundsMap boundsMap; + BoundsMap::iterator currentBounds; + // List of bounds that begin at or before `tileY` + Bounds activeBounds; + + TileSpans tileXSpans; + uint32_t tileY; + int32_t tileX; +}; + +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp index 60839357d5..6edcd83bc2 100644 --- a/src/mbgl/util/tiny_sdf.cpp +++ b/src/mbgl/util/tiny_sdf.cpp @@ -95,7 +95,7 @@ AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, do for (uint32_t i = 0; i < size; i++) { double distance = gridOuter[i] - gridInner[i]; - sdf.data[i] = std::max(0l, std::min(255l, std::lround(255.0 - 255.0 * (distance / radius + cutoff)))); + sdf.data[i] = std::max(0l, std::min(255l, ::lround(255.0 - 255.0 * (distance / radius + cutoff)))); } return sdf; diff --git a/src/mbgl/util/token.hpp b/src/mbgl/util/token.hpp index 149661e47e..dea12f9412 100644 --- a/src/mbgl/util/token.hpp +++ b/src/mbgl/util/token.hpp @@ -1,5 +1,7 @@ #pragma once +#include <mbgl/util/optional.hpp> + #include <map> #include <string> #include <algorithm> @@ -25,7 +27,14 @@ std::string replaceTokens(const std::string &source, const Lookup &lookup) { if (pos != end) { for (brace++; brace != end && tokenReservedChars.find(*brace) == std::string::npos; brace++); if (brace != end && *brace == '}') { - result.append(lookup({ pos + 1, brace })); + std::string key { pos + 1, brace }; + if (optional<std::string> replacement = lookup(key)) { + result.append(*replacement); + } else { + result.append("{"); + result.append(key); + result.append("}"); + } pos = brace + 1; } else { result.append(pos, brace); diff --git a/src/mbgl/util/url.cpp b/src/mbgl/util/url.cpp index 1f6dab9639..a4263502ef 100644 --- a/src/mbgl/util/url.cpp +++ b/src/mbgl/util/url.cpp @@ -130,7 +130,7 @@ Path::Path(const std::string& str, const size_t pos, const size_t count) } std::string transformURL(const std::string& tpl, const std::string& str, const URL& url) { - auto result = util::replaceTokens(tpl, [&](const std::string& token) -> std::string { + auto result = util::replaceTokens(tpl, [&](const std::string& token) -> optional<std::string> { if (token == "path") { return str.substr(url.path.first, url.path.second); } else if (token == "domain") { @@ -146,8 +146,9 @@ std::string transformURL(const std::string& tpl, const std::string& str, const U } else if (token == "extension") { const Path path(str, url.path.first, url.path.second); return str.substr(path.extension.first, path.extension.second); + } else { + return {}; } - return ""; }); // Append the query string if it exists. diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp index c67ff9064c..ffab52692b 100644 --- a/test/api/query.test.cpp +++ b/test/api/query.test.cpp @@ -13,6 +13,7 @@ using namespace mbgl; using namespace mbgl::style; +using namespace mbgl::style::expression; namespace { @@ -67,18 +68,19 @@ TEST(Query, QueryRenderedFeaturesFilterLayer) { TEST(Query, QueryRenderedFeaturesFilter) { QueryTest test; + ParsingContext context; auto zz = test.map.pixelForLatLng({ 0, 0 }); - const EqualsFilter eqFilter = { "key1", std::string("value1") }; + const Filter eqFilter(createCompoundExpression("filter-==", createLiteral("key1"), createLiteral("value1"), context)); auto features1 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{}, { eqFilter }}); EXPECT_EQ(features1.size(), 1u); - const IdentifierNotEqualsFilter idNotEqFilter = { std::string("feature1") }; + const Filter idNotEqFilter(createCompoundExpression("!", std::move(*createCompoundExpression("filter-id-==", createLiteral("feature1"), context)), context)); auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{{ "layer4" }}, { idNotEqFilter }}); EXPECT_EQ(features2.size(), 0u); - const GreaterThanFilter gtFilter = { "key2", 1.0 }; + const Filter gtFilter(createCompoundExpression("filter->", createLiteral("key2"), createLiteral(1.0), context)); auto features3 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{ }, { gtFilter }}); EXPECT_EQ(features3.size(), 1u); } @@ -108,16 +110,17 @@ TEST(Query, QuerySourceFeaturesOptionValidation) { TEST(Query, QuerySourceFeaturesFilter) { QueryTest test; + ParsingContext context; - const EqualsFilter eqFilter = { "key1", std::string("value1") }; + const Filter eqFilter(createCompoundExpression("filter-==", createLiteral("key1"), createLiteral("value1"), context)); auto features1 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { eqFilter }}); EXPECT_EQ(features1.size(), 1u); - const IdentifierNotEqualsFilter idNotEqFilter = { std::string("feature1") }; + const Filter idNotEqFilter(createCompoundExpression("!", std::move(*createCompoundExpression("filter-id-==", createLiteral("feature1"), context)), context)); auto features2 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { idNotEqFilter }}); EXPECT_EQ(features2.size(), 0u); - const GreaterThanFilter gtFilter = { "key2", 1.0 }; + const Filter gtFilter(createCompoundExpression("filter->", createLiteral("key2"), createLiteral(1.0), context)); auto features3 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { gtFilter }}); EXPECT_EQ(features3.size(), 1u); } diff --git a/test/fixtures/offline_database/satellite_test.db b/test/fixtures/offline_database/satellite_test.db Binary files differnew file mode 100644 index 0000000000..95dd8617ff --- /dev/null +++ b/test/fixtures/offline_database/satellite_test.db diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp index 8393fd130d..9b28b2e01a 100644 --- a/test/gl/bucket.test.cpp +++ b/test/gl/bucket.test.cpp @@ -110,10 +110,11 @@ TEST(Buckets, SymbolBucket) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; std::vector<SymbolInstance> symbolInstances; gl::Context context; - SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(symbolInstances) }; + SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(symbolInstances) }; ASSERT_FALSE(bucket.hasIconData()); ASSERT_FALSE(bucket.hasTextData()); ASSERT_FALSE(bucket.hasCollisionBoxData()); diff --git a/test/renderer/group_by_layout.test.cpp b/test/renderer/group_by_layout.test.cpp index 958f1bdf24..6cf17e2279 100644 --- a/test/renderer/group_by_layout.test.cpp +++ b/test/renderer/group_by_layout.test.cpp @@ -5,9 +5,11 @@ #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/style/layers/circle_layer.hpp> #include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/style/expression/compound_expression.hpp> using namespace mbgl; using namespace mbgl::style; +using namespace mbgl::style::expression; static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vector<std::unique_ptr<Layer>>& layers) { std::vector<std::unique_ptr<RenderLayer>> result; @@ -39,7 +41,8 @@ TEST(GroupByLayout, UnrelatedFilter) { std::vector<std::unique_ptr<Layer>> layers; layers.push_back(std::make_unique<LineLayer>("a", "source")); layers.push_back(std::make_unique<LineLayer>("b", "source")); - layers[0]->as<LineLayer>()->setFilter(EqualsFilter()); + ParsingContext context; + layers[0]->as<LineLayer>()->setFilter(Filter(createCompoundExpression("filter-has-id", context))); auto result = groupByLayout(toRenderLayers(layers)); ASSERT_EQ(2u, result.size()); } diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index 94daf59c02..656231eebe 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -38,6 +38,10 @@ void writeFile(const char* name, const std::string& data) { mbgl::util::write_file(name, data); } +void copyFile(const char* orig, const char* dest) { + mbgl::util::write_file(dest, mbgl::util::read_file(orig)); +} + } // namespace TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Create)) { @@ -62,7 +66,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(SchemaVersion)) { std::string path("test/fixtures/offline_database/offline.db"); { - mapbox::sqlite::Database db(path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); db.exec("PRAGMA user_version = 1"); } @@ -143,6 +147,36 @@ TEST(OfflineDatabase, PutResource) { EXPECT_EQ("second", *updateGetResult->data); } +TEST(OfflineDatabase, TEST_REQUIRES_WRITE(GetResourceFromOfflineRegion)) { + using namespace mbgl; + + createDir("test/fixtures/offline_database"); + deleteFile("test/fixtures/offline_database/satellite.db"); + copyFile("test/fixtures/offline_database/satellite_test.db", "test/fixtures/offline_database/satellite.db"); + + OfflineDatabase db("test/fixtures/offline_database/satellite.db", mapbox::sqlite::ReadOnly); + + Resource resource = Resource::style("mapbox://styles/mapbox/satellite-v9"); + ASSERT_TRUE(db.get(resource)); +} + +TEST(OfflineDatabase, PutAndGetResource) { + using namespace mbgl; + + OfflineDatabase db(":memory:"); + + Response response1; + response1.data = std::make_shared<std::string>("foobar"); + + Resource resource = Resource::style("mapbox://example.com/style"); + + db.put(resource, response1); + + auto response2 = db.get(resource); + + ASSERT_EQ(*response1.data, *(*response2).data); +} + TEST(OfflineDatabase, PutTile) { using namespace mbgl; @@ -565,40 +599,45 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) { } static int databasePageCount(const std::string& path) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma page_count"); - stmt.run(); - return stmt.get<int>(0); + 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(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma user_version"); - stmt.run(); - return stmt.get<int>(0); + 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(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma journal_mode"); - stmt.run(); - return stmt.get<std::string>(0); + 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(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma synchronous"); - stmt.run(); - return stmt.get<int>(0); + 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(path, mapbox::sqlite::ReadOnly); + 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.prepare(sql.c_str()); + mapbox::sqlite::Statement stmt{ db, sql.c_str() }; + mapbox::sqlite::Query query{ stmt }; std::vector<std::string> columns; - while (stmt.run()) { - columns.push_back(stmt.get<std::string>(1)); + while (query.run()) { + columns.push_back(query.get<std::string>(1)); } return columns; } diff --git a/test/storage/sqlite.test.cpp b/test/storage/sqlite.test.cpp index 36715a2fd0..553736a0b1 100644 --- a/test/storage/sqlite.test.cpp +++ b/test/storage/sqlite.test.cpp @@ -6,33 +6,32 @@ TEST(SQLite, Statement) { using namespace mbgl; - mapbox::sqlite::Database db(":memory:", mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(":memory:", mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); db.exec("CREATE TABLE test (id INTEGER);"); - mapbox::sqlite::Statement stmt1 = db.prepare("INSERT INTO test (id) VALUES (?1);"); - ASSERT_EQ(stmt1.lastInsertRowId(), 0); - ASSERT_EQ(stmt1.changes(), 0u); - stmt1.bind(1, 10); - stmt1.run(); - ASSERT_EQ(stmt1.lastInsertRowId(), 1); - ASSERT_EQ(stmt1.changes(), 1u); + mapbox::sqlite::Statement stmt1{ db, "INSERT INTO test (id) VALUES (?1);" }; + mapbox::sqlite::Query query1{ stmt1 }; + ASSERT_EQ(query1.lastInsertRowId(), 0); + ASSERT_EQ(query1.changes(), 0u); + query1.bind(1, 10); + query1.run(); + ASSERT_EQ(query1.lastInsertRowId(), 1); + ASSERT_EQ(query1.changes(), 1u); - mapbox::sqlite::Statement stmt2 = db.prepare("INSERT INTO test (id) VALUES (?1);"); - ASSERT_EQ(stmt2.lastInsertRowId(), 0); - ASSERT_EQ(stmt2.changes(), 0u); - stmt2.bind(1, 20); - stmt2.run(); - ASSERT_EQ(stmt2.lastInsertRowId(), 2); - ASSERT_EQ(stmt2.changes(), 1u); + mapbox::sqlite::Statement stmt2{ db, "INSERT INTO test (id) VALUES (?1);" }; + mapbox::sqlite::Query query2{ stmt2 }; + ASSERT_EQ(query2.lastInsertRowId(), 0); + ASSERT_EQ(query2.changes(), 0u); + query2.bind(1, 20); + query2.run(); + ASSERT_EQ(query2.lastInsertRowId(), 2); + ASSERT_EQ(query2.changes(), 1u); } -TEST(SQLite, TEST_REQUIRES_WRITE(CantOpenException)) { - try { - // Should throw a CANTOPEN when the database doesn't exist, - // make sure all the backends behave the same way. - mapbox::sqlite::Database("test/fixtures/offline_database/foobar123.db", mapbox::sqlite::ReadOnly); - FAIL(); - } catch (mapbox::sqlite::Exception& ex) { - ASSERT_EQ(ex.code, mapbox::sqlite::Exception::Code::CANTOPEN); - } +TEST(SQLite, TEST_REQUIRES_WRITE(TryOpen)) { + // Should return a CANTOPEN exception when the database doesn't exist, + // make sure all the backends behave the same way. + 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); } diff --git a/test/style/conversion/stringify.test.cpp b/test/style/conversion/stringify.test.cpp index 136f276aaf..c3faf1f838 100644 --- a/test/style/conversion/stringify.test.cpp +++ b/test/style/conversion/stringify.test.cpp @@ -1,5 +1,6 @@ #include <mbgl/test/util.hpp> +#include <mbgl/style/expression/literal.hpp> #include <mbgl/style/conversion/stringify.hpp> #include <mbgl/style/types.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> @@ -75,8 +76,12 @@ TEST(Stringify, Value) { } TEST(Stringify, Filter) { - ASSERT_EQ(stringify(NullFilter()), "null"); - ASSERT_EQ(stringify(EqualsFilter { "a", 1.0 }), "[\"==\",\"a\",1.0]"); + using namespace mbgl::style::expression; + + ASSERT_EQ(stringify(Filter()), "null"); + + ParsingContext context; + ASSERT_EQ(stringify(Filter(createCompoundExpression("filter-==", createLiteral("a"), createLiteral(1.0), context))), "[\"filter-==\",\"a\",1.0]"); } TEST(Stringify, CameraFunction) { diff --git a/test/style/conversion/tileset.test.cpp b/test/style/conversion/tileset.test.cpp index f10aa0e318..f405fb1361 100644 --- a/test/style/conversion/tileset.test.cpp +++ b/test/style/conversion/tileset.test.cpp @@ -66,7 +66,7 @@ TEST(Tileset, BoundsAreClamped) { Error error; mbgl::optional<Tileset> converted = convertJSON<Tileset>(R"JSON({ "tiles": ["http://mytiles"], - "bounds": [-181.0000005,-90,180.00000000000006,90] + "bounds": [-181.0000005,-90.000000006,180.00000000000006,91] })JSON", error); EXPECT_TRUE((bool) converted); EXPECT_EQ(converted->bounds, LatLngBounds::hull({90, -180}, {-90, 180})); diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp index fe5c261be1..709624a50f 100644 --- a/test/style/expression/expression.test.cpp +++ b/test/style/expression/expression.test.cpp @@ -24,11 +24,15 @@ TEST(Expression, IsExpression) { spec["expression_name"].IsObject() && spec["expression_name"].HasMember("values") && spec["expression_name"]["values"].IsObject()); - + const auto& allExpressions = spec["expression_name"]["values"]; - + for(auto& entry : allExpressions.GetObject()) { const std::string name { entry.name.GetString(), entry.name.GetStringLength() }; + if (name == "collator" || name == "line-progress" || name == "resolved-locale" || name == "feature-state") { + // Not yet implemented + continue; + } JSDocument document; document.Parse<0>(R"([")" + name + R"("])"); diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 49edcaef45..c59a73eab1 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -4,7 +4,6 @@ #include <mbgl/test/stub_geometry_tile_feature.hpp> #include <mbgl/style/filter.hpp> -#include <mbgl/style/filter_evaluator.hpp> #include <mbgl/style/conversion/json.hpp> #include <mbgl/style/conversion/filter.hpp> @@ -28,6 +27,24 @@ bool filter(const char * json, return (*filter)(context); } +void invalidFilter(const char * json) { + conversion::Error error; + optional<Filter> filter = conversion::convertJSON<Filter>(json, error); + EXPECT_FALSE(bool(filter)); + EXPECT_NE(error.message, ""); +} + +TEST(Filter, EqualsNull) { + auto f = R"(["==", "foo", null])"; + ASSERT_TRUE(filter(f, {{ "foo", mapbox::geometry::null_value }})); + + ASSERT_FALSE(filter(f, {{ "foo", int64_t(0) }})); + ASSERT_FALSE(filter(f, {{ "foo", int64_t(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", std::string("0") }})); + ASSERT_FALSE(filter(f, {{ "foo", true }})); + ASSERT_FALSE(filter(f, {{ "foo", false }})); + ASSERT_FALSE(filter(f, {{ }})); +} TEST(Filter, EqualsString) { auto f = R"(["==", "foo", "bar"])"; ASSERT_TRUE(filter(f, {{ "foo", std::string("bar") }})); @@ -53,6 +70,12 @@ TEST(Filter, EqualsType) { auto f = R"(["==", "$type", "LineString"])"; ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {})); ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString, {})); + ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {})); + + invalidFilter("[\"==\", \"$type\"]"); + invalidFilter("[\"==\", \"$type\", null]"); + invalidFilter("[\"==\", \"$type\", \"foo\", 1]"); + invalidFilter("[\"==\", \"$type\", \"foo\", \"Point\"]"); } TEST(Filter, InType) { @@ -62,6 +85,14 @@ TEST(Filter, InType) { ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::Polygon)); } +TEST(Filter, InID) { + auto f = R"(["in", "$id", "123", "1234", 1234])"; + ASSERT_FALSE(filter(f)); + ASSERT_TRUE(filter(f, {{}}, { uint64_t(1234) })); + ASSERT_TRUE(filter(f, {{}}, { std::string("1234") })); + ASSERT_FALSE(filter(f, {{}}, { std::string("4321") })); +} + TEST(Filter, Any) { ASSERT_FALSE(filter("[\"any\"]")); ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); @@ -69,6 +100,13 @@ TEST(Filter, Any) { ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } +TEST(Filter, AnyExpression) { + ASSERT_FALSE(filter("[\"any\"]")); + ASSERT_TRUE(filter("[\"any\", [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"any\", [\"==\", [\"get\", \"foo\"], 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"any\", [\"==\", [\"get\", \"foo\"], 0], [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }})); +} + TEST(Filter, All) { ASSERT_TRUE(filter("[\"all\"]", {{}})); ASSERT_TRUE(filter("[\"all\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); @@ -76,6 +114,12 @@ TEST(Filter, All) { ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } +TEST(Filter, AllExpression) { + ASSERT_TRUE(filter("[\"all\", [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", [\"get\", \"foo\"], 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", [\"get\", \"foo\"], 0], [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }})); +} + TEST(Filter, None) { ASSERT_TRUE(filter("[\"none\"]")); ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); @@ -88,6 +132,7 @@ TEST(Filter, Has) { ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }})); ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), false }})); ASSERT_FALSE(filter("[\"has\", \"foo\"]")); + ASSERT_FALSE(filter("[\"has\", \"$id\"]")); } TEST(Filter, NotHas) { @@ -101,7 +146,11 @@ TEST(Filter, ID) { FeatureIdentifier id1 { uint64_t{ 1234 } }; ASSERT_TRUE(filter("[\"==\", \"$id\", 1234]", {{}}, id1)); ASSERT_FALSE(filter("[\"==\", \"$id\", \"1234\"]", {{}}, id1)); - + + FeatureIdentifier id2 { std::string{ "1" } }; + ASSERT_FALSE(filter("[\"<\", \"$id\", \"0\"]", {{}}, id2)); + ASSERT_TRUE(filter("[\"<\", \"$id\", \"1234\"]", {{}}, id2)); + ASSERT_FALSE(filter("[\"==\", \"$id\", 1234]", {{ "id", uint64_t(1234) }})); } @@ -115,6 +164,18 @@ TEST(Filter, PropertyExpression) { ASSERT_FALSE(filter("[\"==\", [\"get\", \"two\"], 4]", {{"two", int64_t(2)}})); } +TEST(Filter, LegacyProperty) { + ASSERT_TRUE(filter("[\"<=\", \"two\", 2]", {{"two", int64_t(2)}})); + ASSERT_FALSE(filter("[\"==\", \"two\", 4]", {{"two", int64_t(2)}})); + + ASSERT_FALSE(filter("[\"<=\", \"two\", \"2\"]", {{"two", int64_t(2)}})); + ASSERT_FALSE(filter("[\"==\", \"bool\", false]", {{"two", true}})); + + ASSERT_TRUE(filter("[\"<=\", \"two\", \"2\"]", {{"two", std::string("2")}})); + ASSERT_FALSE(filter("[\"<\", \"two\", \"1\"]", {{"two", std::string("2")}})); + ASSERT_FALSE(filter("[\"==\", \"two\", 4]", {{"two", std::string("2")}})); +} + TEST(Filter, ZoomExpressionNested) { ASSERT_TRUE(filter(R"(["==", ["get", "two"], ["zoom"]])", {{"two", int64_t(2)}}, {}, FeatureType::Point, {}, 2.0f)); ASSERT_FALSE(filter(R"(["==", ["get", "two"], ["+", ["zoom"], 1]])", {{"two", int64_t(2)}}, {}, FeatureType::Point, {}, 2.0f)); diff --git a/test/style/style_layer.test.cpp b/test/style/style_layer.test.cpp index 77acca2868..624ed088c3 100644 --- a/test/style/style_layer.test.cpp +++ b/test/style/style_layer.test.cpp @@ -211,7 +211,7 @@ TEST(Layer, Observer) { EXPECT_EQ(layer.get(), &layer_); filterChanged = true; }; - layer->setFilter(NullFilter()); + layer->setFilter(Filter()); EXPECT_TRUE(filterChanged); // Notifies observer on visibility change. diff --git a/test/text/cross_tile_symbol_index.test.cpp b/test/text/cross_tile_symbol_index.test.cpp index 794c2b59a1..5d255a6105 100644 --- a/test/text/cross_tile_symbol_index.test.cpp +++ b/test/text/cross_tile_symbol_index.test.cpp @@ -25,12 +25,13 @@ TEST(CrossTileSymbolLayerIndex, addBucket) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; OverscaledTileID mainID(6, 0, 6, 8, 8); std::vector<SymbolInstance> mainInstances; mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit")); mainInstances.push_back(makeSymbolInstance(2000, 2000, u"Toronto")); - SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) }; + SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances) }; mainBucket.bucketInstanceId = ++maxBucketInstanceId; index.addBucket(mainID, mainBucket, maxCrossTileID); @@ -45,7 +46,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) { childInstances.push_back(makeSymbolInstance(2000, 2000, u"Windsor")); childInstances.push_back(makeSymbolInstance(3000, 3000, u"Toronto")); childInstances.push_back(makeSymbolInstance(4001, 4001, u"Toronto")); - SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) }; + SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances) }; childBucket.bucketInstanceId = ++maxBucketInstanceId; index.addBucket(childID, childBucket, maxCrossTileID); @@ -61,7 +62,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) { OverscaledTileID parentID(5, 0, 5, 4, 4); std::vector<SymbolInstance> parentInstances; parentInstances.push_back(makeSymbolInstance(500, 500, u"Detroit")); - SymbolBucket parentBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(parentInstances) }; + SymbolBucket parentBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(parentInstances) }; parentBucket.bucketInstanceId = ++maxBucketInstanceId; index.addBucket(parentID, parentBucket, maxCrossTileID); @@ -77,7 +78,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) { std::vector<SymbolInstance> grandchildInstances; grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Detroit")); grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Windsor")); - SymbolBucket grandchildBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(grandchildInstances) }; + SymbolBucket grandchildBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(grandchildInstances) }; grandchildBucket.bucketInstanceId = ++maxBucketInstanceId; index.addBucket(grandchildID, grandchildBucket, maxCrossTileID); @@ -98,17 +99,18 @@ TEST(CrossTileSymbolLayerIndex, resetIDs) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; OverscaledTileID mainID(6, 0, 6, 8, 8); std::vector<SymbolInstance> mainInstances; mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit")); - SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) }; + SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances) }; mainBucket.bucketInstanceId = ++maxBucketInstanceId; OverscaledTileID childID(7, 0, 7, 16, 16); std::vector<SymbolInstance> childInstances; childInstances.push_back(makeSymbolInstance(2000, 2000, u"Detroit")); - SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) }; + SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances) }; childBucket.bucketInstanceId = ++maxBucketInstanceId; // assigns a new id @@ -137,12 +139,13 @@ TEST(CrossTileSymbolLayerIndex, noDuplicatesWithinZoomLevel) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; OverscaledTileID mainID(6, 0, 6, 8, 8); std::vector<SymbolInstance> mainInstances; mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B - SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) }; + SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances) }; mainBucket.bucketInstanceId = ++maxBucketInstanceId; OverscaledTileID childID(7, 0, 7, 16, 16); @@ -150,7 +153,7 @@ TEST(CrossTileSymbolLayerIndex, noDuplicatesWithinZoomLevel) { childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // A' childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // B' childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // C' - SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) }; + SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances) }; childBucket.bucketInstanceId = ++maxBucketInstanceId; // assigns new ids @@ -174,19 +177,20 @@ TEST(CrossTileSymbolLayerIndex, bucketReplacement) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; OverscaledTileID tileID(6, 0, 6, 8, 8); std::vector<SymbolInstance> firstInstances; firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B - SymbolBucket firstBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(firstInstances) }; + SymbolBucket firstBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(firstInstances) }; firstBucket.bucketInstanceId = ++maxBucketInstanceId; std::vector<SymbolInstance> secondInstances; secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A' secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B' secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // C' - SymbolBucket secondBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(secondInstances) }; + SymbolBucket secondBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(secondInstances) }; secondBucket.bucketInstanceId = ++maxBucketInstanceId; // assigns new ids diff --git a/test/util/tile_cover.test.cpp b/test/util/tile_cover.test.cpp index 933c18b5ea..7defa761af 100644 --- a/test/util/tile_cover.test.cpp +++ b/test/util/tile_cover.test.cpp @@ -2,6 +2,8 @@ #include <mbgl/util/geo.hpp> #include <mbgl/map/transform.hpp> +#include <algorithm> + #include <gtest/gtest.h> using namespace mbgl; @@ -22,8 +24,8 @@ TEST(TileCover, Antarctic) { TEST(TileCover, WorldZ0) { EXPECT_EQ((std::vector<UnwrappedTileID>{ - { 0, 0, 0 }, - }), + { 0, 0, 0 }, + }), util::tileCover(LatLngBounds::world(), 0)); } @@ -37,15 +39,15 @@ TEST(TileCover, Pitch) { transform.setPitch(40.0 * M_PI / 180.0); EXPECT_EQ((std::vector<UnwrappedTileID>{ - { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 } - }), + { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 } + }), util::tileCover(transform.getState(), 2)); } TEST(TileCover, WorldZ1) { EXPECT_EQ((std::vector<UnwrappedTileID>{ - { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 }, - }), + { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 }, + }), util::tileCover(LatLngBounds::world(), 1)); } @@ -59,21 +61,44 @@ TEST(TileCover, SingletonZ1) { util::tileCover(LatLngBounds::singleton({ 0, 0 }), 1)); } +TEST(TileCoverStream, Arctic) { + auto bounds = LatLngBounds::hull({ 84, -180 }, { 70, 180 }); + auto zoom = 3; + util::TileCover tc(bounds, zoom); + auto results = util::tileCover(bounds, zoom); + std::vector<UnwrappedTileID> t; + while(tc.hasNext()) { + t.push_back(*tc.next()); + }; + EXPECT_EQ(t.size(), results.size()); +} + +TEST(TileCoverStream, WorldZ1) { + auto zoom = 1; + util::TileCover tc(LatLngBounds::world(), zoom); + std::vector<UnwrappedTileID> t; + while(tc.hasNext()) { + t.push_back(*tc.next()); + }; + EXPECT_EQ((std::vector<UnwrappedTileID>{ + { 1, 0, 0 }, { 1, 1, 0 }, { 1, 0, 1 }, { 1, 1, 1 }, + }), t); +} + static const LatLngBounds sanFrancisco = LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); TEST(TileCover, SanFranciscoZ0) { EXPECT_EQ((std::vector<UnwrappedTileID>{ - { 0, 0, 0 }, - }), + { 0, 0, 0 }, + }), util::tileCover(sanFrancisco, 0)); } TEST(TileCover, SanFranciscoZ10) { EXPECT_EQ((std::vector<UnwrappedTileID>{ - { 10, 163, 395 }, { 10, 163, 396 }, { 10, 164, 395 }, { 10, 164, 396 }, - - }), + { 10, 163, 395 }, { 10, 163, 396 }, { 10, 164, 395 }, { 10, 164, 396 }, + }), util::tileCover(sanFrancisco, 10)); } @@ -85,11 +110,192 @@ TEST(TileCover, SanFranciscoZ0Wrapped) { util::tileCover(sanFranciscoWrapped, 0)); } +TEST(TileCover, GeomPoint) { + auto point = Point<double>{ -122.5744, 37.6609 }; + + EXPECT_EQ((std::vector<UnwrappedTileID>{ {2 ,0 ,1 } }), + util::tileCover(point, 2)); +} + +TEST(TileCover, GeomMultiPoint) { + auto points = MultiPoint<double>{ { -122.5, 37.76 }, { -122.4, 37.76} }; + + EXPECT_EQ((std::vector<UnwrappedTileID>{ + {20, 167480, 405351}, {20, 167772, 405351} }), + util::tileCover(points, 20)); +} + +TEST(TileCover, GeomLineZ10) { + auto lineCover = util::tileCover(LineString<double>{ + {-121.49368286132812,38.57903714667459}, + {-122.4422836303711,37.773157169570695} + }, 10); + EXPECT_EQ((std::vector<UnwrappedTileID>{ + { 10, 166, 392}, {10, 165, 393}, {10, 166, 393}, + {10, 164, 394}, {10, 165, 394}, {10,163,395}, {10, 164, 395} + }),lineCover); + +} + +TEST(TileCover, WrappedGeomLineZ10) { + auto lineCover = util::tileCover(LineString<double>{ + {-179.93342914581299,38.892101707724315}, + {-180.02394485473633,38.89203490311832} + }, 10); + EXPECT_EQ((std::vector<UnwrappedTileID>{ { 10, -1, 391 }, { 10, 0, 391 } }), + lineCover); + + lineCover = util::tileCover(LineString<double>{ + {179.93342914581299,38.892101707724315}, + {180.02394485473633,38.89203490311832} + }, 10); + EXPECT_EQ((std::vector<UnwrappedTileID>{ { 10, 1023, 391 }, { 10, 1024, 391 } }), + lineCover); +} + +TEST(TileCover, GeomMultiLineString) { + auto geom = MultiLineString<double>{ + { { -122.5, 37.76 }, { -122.4, 37.76} }, + { { -122.5, 37.72 }, { -122.4, 37.72} } }; + + EXPECT_EQ((std::vector<UnwrappedTileID>{ + {14, 2616, 6333}, {14, 2617, 6333}, {14, 2618, 6333}, + {14, 2619, 6333}, {14, 2620, 6333}, {14, 2621, 6333}, + {14, 2616, 6335}, {14, 2617, 6335}, {14, 2618, 6335}, + {14, 2619, 6335}, {14, 2620, 6335}, {14, 2621, 6335}}), + util::tileCover(geom, 14)); +} + +TEST(TileCover, GeomPolygon) { + auto polygon = Polygon<double>{ + { + {5.09765625,53.067626642387374}, + {2.373046875,43.389081939117496}, + {-4.74609375,48.45835188280866}, + {-1.494140625,37.09023980307208}, + {22.587890625,36.24427318493909}, + {31.640625,46.13417004624326}, + {17.841796875,54.7246201949245}, + {5.09765625,53.067626642387374}, + },{ + {19.6875,49.66762782262194}, + {22.8515625,43.51668853502906}, + {13.623046875,45.089035564831036}, + {16.34765625,39.095962936305476}, + {5.185546875,41.244772343082076}, + {8.701171874999998,50.233151832472245}, + {19.6875,49.66762782262194} + } + }; + + auto results = util::tileCover(polygon, 8); + + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 134, 87}), results.end()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 139, 87}), results.end()); + // Should have a hole + EXPECT_EQ(std::find(results.begin(), results.end(), UnwrappedTileID{8, 136, 87}), results.end()); +} + +TEST(TileCover, GeomMultiPolygon) { + auto multiPolygon = MultiPolygon<double>{ + {{ + {5.09765625,53.067626642387374}, + {2.373046875,43.389081939117496}, + {-4.74609375,48.45835188280866}, + {-1.494140625,37.09023980307208}, + {22.587890625,36.24427318493909}, + {31.640625,46.13417004624326}, + {17.841796875,54.7246201949245}, + {5.09765625,53.067626642387374}, + }},{{ + {59.150390625,45.460130637921004}, + {65.126953125,41.11246878918088}, + {69.169921875,47.45780853075031}, + {63.896484375,50.064191736659104}, + {59.150390625,45.460130637921004} + }} + }; + auto results = util::tileCover(multiPolygon, 8); + + EXPECT_EQ(424u, results.size()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 139, 87}), results.end()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 136, 87}), results.end()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 174, 94}), results.end()); +} + +TEST(TileCover, GeomSanFranciscoPoly) { + auto sanFranciscoGeom = Polygon<double>{ + { + {-122.5143814086914,37.779127216982424}, + {-122.50811576843262,37.72721239056709}, + {-122.50313758850099,37.70820178063929}, + {-122.3938751220703,37.707454835665274}, + {-122.37567901611328,37.70663997801684}, + {-122.36297607421874,37.71343018466285}, + {-122.354736328125,37.727280276860036}, + {-122.36469268798828,37.73868429065797}, + {-122.38014221191408,37.75442980295571}, + {-122.38391876220702,37.78753873820529}, + {-122.35919952392578,37.8065289741725}, + {-122.35679626464844,37.820632846207864}, + {-122.3712158203125,37.835276322922695}, + {-122.3818588256836,37.82958198283902}, + {-122.37190246582031,37.80788523279169}, + {-122.38735198974608,37.791337175930686}, + {-122.40966796874999,37.812767557570204}, + {-122.46425628662108,37.807071480609274}, + {-122.46803283691405,37.810326435534755}, + {-122.47901916503906,37.81168262440736}, + {-122.48966217041016,37.78916666399649}, + {-122.50579833984375,37.78781006166096}, + {-122.5143814086914,37.779127216982424} + } + }; + + EXPECT_EQ((std::vector<UnwrappedTileID>{ + { 12, 654, 1582 }, { 12, 655, 1582 }, + { 12, 654, 1583 }, { 12, 655, 1583 }, + { 12, 654, 1584 }, { 12, 655, 1584 } + }), util::tileCover(sanFranciscoGeom, 12)); +} + +TEST(TileCover, GeomInvalid) { + auto point = Point<double>{ -122.5744, 97.6609 }; + EXPECT_THROW(util::tileCover(point, 2), std::domain_error); + + auto badPoly = Polygon<double> { { {1.0, 35.0} } }; + EXPECT_EQ((std::vector<UnwrappedTileID>{ }), util::tileCover(badPoly, 16)); + + //Should handle open polygons. + badPoly = Polygon<double> { { {1.0, 34.2}, {1.0, 34.4}, {0.5, 34.3} } }; + EXPECT_EQ((std::vector<UnwrappedTileID>{ + { 10, 513, 407 }, { 10, 514, 407}, + { 10, 513, 408 }, { 10, 514, 408} + }), util::tileCover(badPoly, 10)); +} + + +TEST(TileCount, World) { + EXPECT_EQ(1u, util::tileCount(LatLngBounds::world(), 0)); + EXPECT_EQ(4u, util::tileCount(LatLngBounds::world(), 1)); +} + TEST(TileCount, SanFranciscoZ10) { - EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10, util::tileSize)); + EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10)); +} + +TEST(TileCount, SanFranciscoWrappedZ10) { + EXPECT_EQ(4u, util::tileCount(sanFranciscoWrapped, 10)); } TEST(TileCount, SanFranciscoZ22) { - EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22, util::tileSize)); + EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22)); } +TEST(TileCount, BoundsCrossingAntimeridian) { + auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); + + EXPECT_EQ(1u, util::tileCount(crossingBounds, 0)); + EXPECT_EQ(4u, util::tileCount(crossingBounds, 3)); + EXPECT_EQ(8u, util::tileCount(crossingBounds, 4)); +} diff --git a/test/util/tile_range.test.cpp b/test/util/tile_range.test.cpp index c4c37c74d7..83ac5096a5 100644 --- a/test/util/tile_range.test.cpp +++ b/test/util/tile_range.test.cpp @@ -52,14 +52,14 @@ TEST(TileRange, ContainsWrappedBounds) { TEST(TileRange, ContainsBoundsCrossingAntimeridian) { { - auto cossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); - auto range = util::TileRange::fromLatLngBounds(cossingBounds, 1); + auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); + auto range = util::TileRange::fromLatLngBounds(crossingBounds, 1); EXPECT_TRUE(range.contains(CanonicalTileID(1, 1, 1))); EXPECT_TRUE(range.contains(CanonicalTileID(1, 0, 0))); } { - auto cossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); - auto range = util::TileRange::fromLatLngBounds(cossingBounds, 6); + auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); + auto range = util::TileRange::fromLatLngBounds(crossingBounds, 6); EXPECT_FALSE(range.contains(CanonicalTileID(6, 55, 34))); EXPECT_FALSE(range.contains(CanonicalTileID(6, 5, 28))); EXPECT_TRUE(range.contains(CanonicalTileID(6, 63, 28))); diff --git a/test/util/token.test.cpp b/test/util/token.test.cpp index a2885dc8dd..d0cf1e31cf 100644 --- a/test/util/token.test.cpp +++ b/test/util/token.test.cpp @@ -47,4 +47,7 @@ TEST(Token, replaceTokens) { if (token == "HØYDE") return "150"; return ""; })); + EXPECT_EQ("{unknown}", mbgl::util::replaceTokens("{unknown}", [](const std::string&) -> optional<std::string> { + return {}; + })); } |