diff --git a/mapbox-gl-native/.clang-format b/mapbox-gl-native/.clang-format new file mode 100644 index 000000000..956884c1b --- /dev/null +++ b/mapbox-gl-native/.clang-format @@ -0,0 +1,11 @@ +--- +Language: Cpp +BasedOnStyle: Google +AccessModifierOffset: -4 +AllowShortFunctionsOnASingleLine: Inline +BinPackArguments: false +BinPackParameters: false +ColumnLimit: 120 +IndentWidth: 4 +SpacesBeforeTrailingComments: 1 +... diff --git a/mapbox-gl-native/.clang-tidy b/mapbox-gl-native/.clang-tidy new file mode 100644 index 000000000..2b9756a07 --- /dev/null +++ b/mapbox-gl-native/.clang-tidy @@ -0,0 +1,42 @@ +Checks: > + -*, + android-*, + boost-*, + bugprone-*, + clang-analyzer-core* + clang-analyzer-cplusplus*, + clang-analyzer-deadcode*, + clang-analyzer-optin.cplusplus*, + clang-analyzer-optin.performance.Padding, + clang-analyzer-security*, + clang-diagnostic-*, + google-*, + misc-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -bugprone-macro-parentheses, + -bugprone-narrowing-conversions, + -google-build-using-namespace, + -google-explicit-constructor, + -google-readability-braces-around-statements, + -google-readability-todo, + -google-runtime-int, + -google-runtime-references, + -misc-non-private-member-variables-in-classes, + -modernize-avoid-c-arrays, + -readability-braces-around-statements, + -readability-else-after-return, + -readability-implicit-bool-conversion, + -readability-magic-numbers, + -readability-named-parameter, + -readability-uppercase-literal-suffix + +WarningsAsErrors: '*' +HeaderFilterRegex: '.*' +FormatStyle: file + +CheckOptions: + - key: performance-unnecessary-value-param.AllowedTypes + value: 'exception_ptr' diff --git a/mapbox-gl-native/.cmake-format b/mapbox-gl-native/.cmake-format new file mode 100644 index 000000000..4ca1e0f0b --- /dev/null +++ b/mapbox-gl-native/.cmake-format @@ -0,0 +1,103 @@ + +# -------------------------- +# General Formatting Options +# -------------------------- +# How wide to allow formatted cmake files +line_width = 140 + +# How many spaces to tab for indent +tab_size = 4 + +# If arglists are longer than this, break them always +max_subargs_per_line = 3 + +# If true, separate flow control names from their parentheses with a space +separate_ctrl_name_with_space = False + +# If true, separate function names from parentheses with a space +separate_fn_name_with_space = False + +# If a statement is wrapped to more than one line, than dangle the closing +# parenthesis on it's own line +dangle_parens = True + +# If the statement spelling length (including space and parenthesis is larger +# than the tab width by more than this amoung, then force reject un-nested +# layouts. +max_prefix_chars = 2 + +# If a candidate layout is wrapped horizontally but it exceeds this many lines, +# then reject the layout. +max_lines_hwrap = 8 + +# What style line endings to use in the output. +line_ending = u'unix' + +# Format command names consistently as 'lower' or 'upper' case +command_case = u'canonical' + +# Format keywords consistently as 'lower' or 'upper' case +keyword_case = u'unchanged' + +# Specify structure for custom cmake functions +additional_commands = { + "add_node_module": { + "kwargs": { + "INSTALL_PATH": "*", + "NAN_VERSION": "*", + "EXCLUDE_NODE_ABIS": "*" + } + }, + "target_compile_definitions": { + "kwargs": { + "PRIVATE": "*", + "PUBLIC": "*" + } + }, + "set_target_properties": { + "kwargs": { + "PROPERTIES": { + "kwargs": { + "INTERFACE_MAPBOX_NAME": "*", + "INTERFACE_MAPBOX_URL": "*", + "INTERFACE_MAPBOX_AUTHOR": "*", + "INTERFACE_MAPBOX_LICENSE": "*" + } + } + } + }, + "target_sources": { + "kwargs": { + "INTERFACE": "*", + "PRIVATE": "*", + "PUBLIC": "*" + } + } +} + +# A list of command names which should always be wrapped +always_wrap = [ + "add_executable", + "add_library", + "set_source_files_properties", + "target_compile_definitions", + "target_include_directories", + "target_link_libraries", + "target_sources" +] + +# Specify the order of wrapping algorithms during successive reflow attempts +algorithm_order = [0, 1, 2, 3, 4] + +# If true, the argument lists which are known to be sortable will be sorted +# lexicographicall +enable_sort = True + +# If true, the parsers may infer whether or not an argument list is sortable +# (without annotation). +autosort = True + +# If a comment line starts with at least this many consecutive hash characters, +# then don't lstrip() them off. This allows for lazy hash rulers where the first +# hash char is not separated by space +hashruler_min_length = 10 diff --git a/mapbox-gl-native/.github/CODEOWNERS b/mapbox-gl-native/.github/CODEOWNERS new file mode 100644 index 000000000..66baad21d --- /dev/null +++ b/mapbox-gl-native/.github/CODEOWNERS @@ -0,0 +1,6 @@ +* @mapbox/gl-native + +# Apple +/platform/ios/ @mapbox/maps-ios-reviewers +/platform/macos/ @mapbox/maps-ios-reviewers +/platform/darwin/ @mapbox/maps-ios-reviewers diff --git a/mapbox-gl-native/.github/ISSUE_TEMPLATE/darwin.md b/mapbox-gl-native/.github/ISSUE_TEMPLATE/darwin.md new file mode 100644 index 000000000..8986f6878 --- /dev/null +++ b/mapbox-gl-native/.github/ISSUE_TEMPLATE/darwin.md @@ -0,0 +1,34 @@ +--- +name: iOS or macOS +about: Mapbox Maps SDK for iOS or macOS + +--- + + +### Steps to reproduce + + 1. + 2. + 3. + +### Expected behavior + + + +### Actual behavior + + + +### Configuration + +**Mapbox SDK versions:** +**iOS/macOS versions:** +**Device/simulator models:** +**Xcode version:** diff --git a/mapbox-gl-native/.github/ISSUE_TEMPLATE/other.md b/mapbox-gl-native/.github/ISSUE_TEMPLATE/other.md new file mode 100644 index 000000000..47a736f01 --- /dev/null +++ b/mapbox-gl-native/.github/ISSUE_TEMPLATE/other.md @@ -0,0 +1,25 @@ +--- +name: Other +about: Another platform or a cross-platform issue + +--- + + +**Platform:** +**Mapbox SDK version:** + +### Steps to trigger behavior + + 1. + 2. + 3. + +### Expected behavior + +### Actual behavior diff --git a/mapbox-gl-native/.github/PULL_REQUEST_TEMPLATE.md b/mapbox-gl-native/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..6c3ae9685 --- /dev/null +++ b/mapbox-gl-native/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +## Launch Checklist + + + + - [ ] briefly describe the changes in this PR + - [ ] include before/after visuals or gifs if this PR includes visual changes + - [ ] write tests for all new functionality + - [ ] document any changes to public APIs + - [ ] tagged `@mapbox/maps-android @mapbox/maps-ios @mapbox/core-sdk` if this PR adds or updates a public API + - [ ] tagged `@mapbox/gl-js` if this PR includes shader changes or needs a js port + - [ ] apply `needs changelog` label if a changelog is needed (remove label when added) diff --git a/mapbox-gl-native/.github/stale.yml b/mapbox-gl-native/.github/stale.yml new file mode 100644 index 000000000..6f5511017 --- /dev/null +++ b/mapbox-gl-native/.github/stale.yml @@ -0,0 +1,28 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +exemptProjects: true +exemptMilestones: true + +staleLabel: archived + +limitPerRun: 1 + +# SEMVER-MAJOR - @tobrun +exemptLabels: + - SEMVER-MAJOR + +pulls: + daysUntilStale: 60 + daysUntilClose: -1 + markComment: false + closeComment: > + This pull request has been automatically detected as stale because it has not had + recent activity and will be archived. Thank you for your contributions. + +issues: + daysUntilStale: 180 + daysUntilClose: -1 + markComment: false + closeComment: > + This issue has been automatically detected as stale because it has not had + recent activity and will be archived. Thank you for your contributions. diff --git a/mapbox-gl-native/.gitignore b/mapbox-gl-native/.gitignore new file mode 100644 index 000000000..b12fb8a38 --- /dev/null +++ b/mapbox-gl-native/.gitignore @@ -0,0 +1,67 @@ +.DS_Store +.transifexrc +*.pyc +*.gcno +*.gcda +*~ +**/actual.png +**/diff.png +offline.db +new_offline.db +xcuserdata +/platform/node/test/actual +/platform/node/test/diff +/mason_packages +/build +/next/build +/lib +/test/node_modules +/test/fixtures/*/*_actual.* +/test/fixtures/api/1.png +/test/fixtures/api/2.png +/test/fixtures/offline_database/offline.db +/test/fixtures/offline_database/offline.db-* +/test/output +/node_modules +/platform/ios/benchmark/assets/glyphs/DIN* +/platform/ios/benchmark/assets/tiles/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6 +/platform/darwin/developer.xcconfig +/platform/macos/macos.xcworkspace/xcshareddata/macos.xcscmblueprint +/platform/ios/ios.xcworkspace/xcshareddata/ios.xcscmblueprint +/documentation +test/fixtures/api/assets.zip +test/fixtures/storage/assets.zip +/.circle-week +/vendor/.cache +buck-out +.buckd + +# Generated metrics results +metrics/*.html + +# Android RenderTestRunner +render-test/android/.* +render-test/android/app/build/ +render-test/android/app/src/main/assets/data.zip +render-test/android/local.properties +render-test/android/app/.* +render-test/android/package.json +render-test/android/app/app.iml +render-test/android/android.iml + +# IOS RenderTestRunner +render-test/ios/codesigning/*.xcent +render-test/ios/fastlane/README.md +test-data/ + +# Generated file from npm/yarn +package-lock.json + +# Visual Studio Code +.vscode +mapbox-gl-native.code-workspace + +# Generated list files from code generation +/scripts/generate-file-lists.list +/scripts/generate-shaders.list +/scripts/generate-style-code.list diff --git a/mapbox-gl-native/.gitmodules b/mapbox-gl-native/.gitmodules new file mode 100644 index 000000000..2c286a1b2 --- /dev/null +++ b/mapbox-gl-native/.gitmodules @@ -0,0 +1,51 @@ +[submodule "mapbox-gl-js"] + path = mapbox-gl-js + url = https://github.com/mapbox/mapbox-gl-js.git +[submodule "vendor/nunicode"] + path = vendor/nunicode + url = https://bitbucket.org/alekseyt/nunicode.git +[submodule "vendor/earcut.hpp"] + path = vendor/earcut.hpp + url = https://github.com/mapbox/earcut.hpp.git +[submodule "vendor/polylabel"] + path = vendor/polylabel + url = https://github.com/mapbox/polylabel.git +[submodule "vendor/protozero"] + path = vendor/protozero + url = https://github.com/mapbox/protozero.git +[submodule "vendor/vector-tile"] + path = vendor/vector-tile + url = https://github.com/mapbox/vector-tile.git +[submodule "vendor/wagyu"] + path = vendor/wagyu + url = https://github.com/mapbox/wagyu.git +[submodule "vendor/unique_resource"] + path = vendor/unique_resource + url = https://github.com/okdshin/unique_resource.git +[submodule "vendor/icu"] + path = vendor/icu + url = https://github.com/unicode-org/icu.git +[submodule "vendor/boost"] + path = vendor/boost + url = https://github.com/mapbox/mapbox-gl-native-boost.git +[submodule "vendor/benchmark"] + path = vendor/benchmark + url = https://github.com/google/benchmark.git +[submodule "vendor/eternal"] + path = vendor/eternal + url = https://github.com/mapbox/eternal.git +[submodule "vendor/mapbox-base"] + path = vendor/mapbox-base + url = https://github.com/mapbox/mapbox-base.git +[submodule "vendor/googletest"] + path = vendor/googletest + url = https://github.com/google/googletest.git +[submodule "vendor/zip-archive"] + path = vendor/zip-archive + url = https://github.com/ZipArchive/ZipArchive.git +[submodule "vendor/cpp-httplib"] + path = vendor/cpp-httplib + url = https://github.com/yhirose/cpp-httplib.git +[submodule "vendor/mapbox-gl-native-curl"] + path = vendor/mapbox-gl-native-curl + url = https://github.com/mapbox/mapbox-gl-native-curl.git diff --git a/mapbox-gl-native/.npmignore b/mapbox-gl-native/.npmignore new file mode 100644 index 000000000..d2490da83 --- /dev/null +++ b/mapbox-gl-native/.npmignore @@ -0,0 +1,32 @@ +Makefile +CmakeLists.txt +*yml +build +cmake +misc +.* +*tgz +benchmark +expression-test +cloudformation +bin +vendor +include +mason_packages +mapbox-gl-js +test +lib +next +src +scripts +render-test +platform/android +platform/darwin +platform/default +platform/glfw +platform/ios +platform/linux +platform/macos +platform/qt +platform/node/symbol-list +platform/node/version-script \ No newline at end of file diff --git a/mapbox-gl-native/.topissuesrc b/mapbox-gl-native/.topissuesrc new file mode 100644 index 000000000..c16804cd2 --- /dev/null +++ b/mapbox-gl-native/.topissuesrc @@ -0,0 +1,17 @@ +{ + "labels": { + "release blocker": 25, + "beta blocker": 20, + "crash": 5, + "build": 5, + "bug": 2 + }, + "reactions": { + "+1": 1, + "-1": -1, + "laugh": 1, + "hooray": 1, + "confused": 1, + "heart": 1 + } +} diff --git a/mapbox-gl-native/.tx/config b/mapbox-gl-native/.tx/config new file mode 100644 index 000000000..3de4c41c4 --- /dev/null +++ b/mapbox-gl-native/.tx/config @@ -0,0 +1,53 @@ +[main] +host = https://www.transifex.com +minimum_perc = 80 + +[mapbox-gl-native.foundationstrings-darwin] +file_filter = platform/darwin/resources/.lproj/Foundation.strings +lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant +source_file = platform/darwin/resources/Base.lproj/Foundation.strings +source_lang = en +type = STRINGS + +[mapbox-gl-native.foundationstringsdict-darwin] +file_filter = platform/darwin/resources/.lproj/Foundation.stringsdict +lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant +source_file = platform/darwin/resources/en.lproj/Foundation.stringsdict +source_lang = en +type = STRINGSDICT + +[mapbox-gl-native.localizablestrings-ios] +file_filter = platform/ios/resources/.lproj/Localizable.strings +lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant +source_file = platform/ios/resources/Base.lproj/Localizable.strings +source_lang = en +type = STRINGS + +[mapbox-gl-native.localizablestringsdict-ios] +file_filter = platform/ios/resources/.lproj/Localizable.stringsdict +lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant +source_file = platform/ios/resources/en.lproj/Localizable.stringsdict +source_lang = en +type = STRINGSDICT + +[mapbox-gl-native.localizablestrings-macos] +file_filter = platform/macos/sdk/.lproj/Localizable.strings +lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant +source_file = platform/macos/sdk/Base.lproj/Localizable.strings +source_lang = en +type = STRINGS + +[mapbox-gl-native.rootstrings-ios] +file_filter = platform/ios/app/Settings.bundle/.lproj/Root.strings +lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant +source_file = platform/ios/app/Settings.bundle/Base.lproj/Root.strings +source_lang = en +type = STRINGS + +[mapbox-gl-native.stringsxml-android] +file_filter = platform/android/MapboxGLAndroidSDK/src/main/res/values-/strings.xml +lang_map = pt_PT: pt-rPT, zh_CN: zh-rCN, zh_TW: zh-rTW, zh_HK: zh-rHK +source_file = platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml +source_lang = en +type = ANDROID + diff --git a/mapbox-gl-native/.ycm_extra_conf.py b/mapbox-gl-native/.ycm_extra_conf.py new file mode 100644 index 000000000..63616a1e9 --- /dev/null +++ b/mapbox-gl-native/.ycm_extra_conf.py @@ -0,0 +1,128 @@ +# Version of .ycm_extra_conf.py sample file adapted +# for the Mapbox GL Native project. +# +# This file is NOT licensed under the GPLv3, which is the license for the rest +# of YouCompleteMe. +# +# Here's the license text for this file: +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to + +import os +import subprocess +import string +import ycm_core + +compilation_database_folders = [ + 'build/linux-x86_64/Debug', + 'build/qt-linux-x86_64/Debug', + 'build/qt4-linux-x86_64/Debug', + 'build/macos/compdb/Debug', +] + +subprocess.call(['make compdb'], shell=True) + +for folder in compilation_database_folders: + if os.path.exists( folder ): + database = ycm_core.CompilationDatabase( folder ) + +SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ] + + +def DirectoryOfThisScript(): + return os.path.dirname( os.path.abspath( __file__ ) ) + + +def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): + if not working_directory: + return list( flags ) + new_flags = [] + make_next_absolute = False + path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith( '/' ): + new_flag = os.path.join( working_directory, flag ) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith( path_flag ): + path = flag[ len( path_flag ): ] + new_flag = path_flag + os.path.join( working_directory, path ) + break + + if new_flag: + new_flags.append( new_flag ) + return new_flags + + +def IsHeaderFile( filename ): + extension = os.path.splitext( filename )[ 1 ] + return extension in [ '.h', '.hxx', '.hpp', '.hh' ] + + +def GetCompilationInfoForFile( filename ): + # The compilation_commands.json file generated by CMake does not have entries + # for header files. So we do our best by asking the db for flags for a + # corresponding source file, if any. If one exists, the flags for that file + # should be good enough. + if IsHeaderFile( filename ): + basename = os.path.splitext( filename )[ 0 ] + for extension in SOURCE_EXTENSIONS: + replacement_file = basename + extension + if os.path.exists( replacement_file ): + compilation_info = database.GetCompilationInfoForFile( + replacement_file ) + if compilation_info.compiler_flags_: + return compilation_info + return None + return database.GetCompilationInfoForFile( filename ) + + +def FlagsForFile( filename, **kwargs ): + if not database: + return None + + # Bear in mind that compilation_info.compiler_flags_ does NOT return a + # python list, but a "list-like" StringVec object + compilation_info = GetCompilationInfoForFile( filename ) + if not compilation_info: + return None + + final_flags = MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_ ) + + return { + 'flags': final_flags, + 'do_cache': True + } diff --git a/mapbox-gl-native/ARCHITECTURE.md b/mapbox-gl-native/ARCHITECTURE.md new file mode 100644 index 000000000..639602020 --- /dev/null +++ b/mapbox-gl-native/ARCHITECTURE.md @@ -0,0 +1,121 @@ +This document aims to outline at a high level the various parts that make up mapbox-gl-native and how they work together. + +# Repository structure + +mapbox-gl-native uses a monolithic that houses both core C++ code and code that wraps the C++ core with SDKs for Android, iOS, macOS, Node.js, and Qt. A "monorepo" allows us to: + + * Make changes to the core API and SDKs simultaneously, ensuring no platform falls behind. + * Ensure that core changes do not inadvertently break SDK tests. + * Centralize discussions about features and defects that affect multiple platforms. + +In the repository, core C++ code is contained in the `include` and `src` directories. The former includes headers that are considered to make up the "public" core C++ API, while the latter includes `.cpp` implementation files and headers that are private to the implementation. Within both directories, files are nested under an `mbgl` directory, which has various subdirectories based on areas of functionality. Both public and private headers therefore can (and should) always be included with the form `#include `. + +Code and build scripts belonging to platform SDKs are contained in the `platform` directory, which has subdirectories for each platform. The `platform/darwin` and `platform/default` directories contain code shared by multiple platform SDKs. + +# Build system + +The mapbox-gl-native build system uses a variety of tools. + +## Make + +GNU Make and a master `Makefile` serves as the coordinating tool for all other build system tools. Most of your interaction with the build system will be via `make` commands. We aim to have a consistent pattern for make targets across platforms: for a given platform, e.g. `ios`, `make ios` builds a reasonable set of end products (though perhaps not all permutations of build options, which can be prohibitively expensive), and `make ios-test` runs a complete test suite for the platform. + +## Git submodules + +Git submodules are used to pull in several dependencies: mason (see below) and several iOS dependencies (though we have plans to phase these out). Initializing these submodules is handled automatically by the necessary `make` targets. + +## npm + +npm is a package manager for Node.js. mapbox-gl-native uses it to pull in several development dependencies that happen to be packaged as node modules -- mainly for testing needs. + +## Mason + +[Mason](https://github.com/mapbox/mason) is Mapbox's own cross platform C/C++ package manager. mapbox-gl-native uses mason packages as a source of precompiled binaries for third-party dependencies such as Boost, RapidJSON, and SQLite, and Mapbox's own C++ modules such as [earcut.hpp](https://github.com/mapbox/earcut.hpp) and [geojson-vt-cpp](https://github.com/mapbox/geojson-vt-cpp). It is also used to obtain a toolchain for Android platform cross-compiliation. + +We track mason dependencies for a given platform in the file `platform//scripts/configure.sh`. The `configure` script at the root handles sourcing this file during the build, running the appropriate mason commands, and writing configuration settings for the subsequent build to a `config.gypi` in the build output directory. + +## gyp + +[gyp](https://gyp.gsrc.io/) is a build system originally created for Chromium and since adopted by Node.js. In mapbox-gl-native it's used to build the core C++ static library, the Node.js platform module, and the shared JNI module for Android. + +## Platform-specific subsystems + +Outside of the core C++ static library, platform SDKs typically rely on platform-native build tooling to complete the job. + +* For iOS and macOS this means Xcode and the xcodebuild command line tool. +* For Android, Gradle and Android Studio. +* For Qt, `qmake`. + +See the relevant platform-specific `README.md` / `INSTALL.md` for details. + +# Major functional components + +## Map +## Style + +The "Style" component of mapbox-gl-native contains an implementation of the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), defining what data to draw, the order to draw it in, and how to style the data when drawing it. + +In addition to supporting styles loaded from a URL, mapbox-gl-native includes a runtime styling API, which allows users to dynamically modify the current style: add and remove layers, modify layer properties, and so on. As appropriate for a C++ API, the runtime styling API API is _strongly typed_: there are subclasses for each layer type, with correctly-typed accessors for each style property. This results in a large API surface area. Fortunately, this is automated, by generating the API – and the regular portion of the implementation – from the style specification. + +The layers API makes a distinction between public API and internal implementation using [the `Impl` idiom](https://github.com/mapbox/mapbox-gl-native/issues/3254) seen elsewhere in the codebase. Here, it takes the form of parallel class hierarchies: + +* `Layer`, `Source`, and their subclasses form the public API. This is the API consumed by SDK bindings. +* `Layer::Impl`, `Source::Impl`, and their subclasses form the internal API. This API is used only by other parts of the core C++ implementation. + +For each subclass of `Layer` or source, there's a corresponding `Impl` subclass. For example, `CircleLayer` and `CircleLayer::Impl`. The base `Layer` class holds a reference to the base `Layer::Impl`, and `CircleLayer` and other subclasses have an `impl()` accessor method that casts this reference to the appropriate subtype. + +## Immutability + +The `Layer::Impl` and `Source::Impl` reference held by `Layer` and `Source` base classes is _immutable_: it's a shared reference to a `const` (read only) pointer. Immutability permits the `Impl` objects to be shared safely between threads when needed -- for example, between the main thread and worker thread that performs computation in the background, or between the main thread and a dedicated renderer thread. See the "Threading" section below for further details on the threading model. + +Immutability is an alternative to several other strategies for safe intra-thread communication. One alternative strategy is to insert locks whenever data is shared between threads and at least one thread may be modifying the data. This strategy is prone to problems such as race conditions (if you forget to use a lock), deadlocks (if locks are acquired in conflicting orders), and poor performance due to lock contention. Another strategy is to copy data whenever it's needed by another thread. For complex structures such as a Mapbox Style, copying can be an expensive operation. With immutability, the approach is to share data without copying, but ensure that any data so shared cannot be modified by any thread, and that the data is not destroyed until the last thread using it relinquishes its reference. + +Immutability is implemented by the `Immutable` template, which acts as a non-nullable shared reference to a `const T`. It has behavior similar to `std::shared_ptr`, but indicates its intent as an immutable reference that is safe to share between threads. + +Immutability is core to the implementation, and yet one of the defining features of Mapbox GL is the ability to manipulate and mutate the style freely at runtime. How can this be possible if everything is immutable? The answer turns on the distinction mentioned in the previous section between public classes such as `Layer` and private implementations such as `Layer::Impl`. In Mapbox GL, the latter is immutable, but the former is not: + +* **Mutable objects**: `Layer`, `Source`, `Image`, `Light` +* **Immutable objects**: `Layer::Impl`, `Source::Impl`, `Image::Impl`, `Light::Impl` + +An instance of a `Layer` subclass such as `CircleLayer` has a reference to an `Immutable`, but is itself mutable -- it has mutating methods such as `setCircleRadius`. Such methods follow a common pattern: + +* Create a new instance of the Impl, copied from the existing Impl. This new instance is temporarily mutable. +* Modify this new instance as needed; e.g. set the radius to a new value. +* "Freeze" the new instance by making it immutable, and then assign that immutable reference to be the new Impl for the `Layer`. + +Two things to note about this process: + +* No existing Impls are modified -- only the newly created copy. +* Only one existing reference to an Impl is modified -- the one held by the `Layer` instance being mutated. Any references to the previous Impl held by other threads remain unchanged. They go on using the previous value for radius until notified by some other means that there has been a change. + +So how do things that are holding references to the previous Impl get notified? The answer is **style diffing**. This is the process by which we determine, from one frame to the next, what parts of the style have changed and therefore what needs to be recalculated in response to those changes in order to draw an updated frame. In parallel to style objects such as `Style`, `Layer`, `Source` and so on, Mapbox GL maintains render objects such as `RenderStyle`, `RenderLayer`, `RenderSource` and so on. These render objects: + +* are mutable +* may live on either the main thread or an independent rendering thread, depending on the SDK +* contain any and all values calculated from the style objects plus state such as the current zoom level and location -- for example, the set of loaded tiles and the buckets calculated from them + +From one frame to the next, these render objects are updated based on the current state of the style objects. In order to permit the render objects to live on a different thread, this state is communicated as immutable Impl references -- in effect, a snapshot of the style state at the time the frame was requested. And it really is a just snapshot -- we don't communicate things like "the radius of this circle layer was changed", "this layer was removed", etc. Instead, that information is reconstructed by `RenderStyle` by comparing the new snapshot to the old snapshot. Immutability allows us to perform this comparison very efficiently: + +* If the "before" and "after" of two immutable references refer to the same object, we know that it hasn't changed. If they're different, we know it has changed in some way. +* We can calculate changes in collections of immutable references (a list of layers or sources) using an efficient [diff algorithm](http://www.xmailserver.org/diff2.pdf). We can further improve this efficiency by making the collections themselves immutable, so that we can avoid running the diff algorithm altogether in the common case where the "before" and "after" collections refer to the same immutable object. + +One final benefit of this approach of diffing immutable objects: we get "smart" updates between two completely independent styles essentially for free. For example, `RenderStyle` doesn't care if the "before" snapshot is from the Mapbox Light style and the "after" snapshot is from the Mapbox Dark style. It will just calculate the difference between the two snapshots, update render data where necessary, and render the next frame. Since those two styles use the same source data and layer IDs, the result will be a fully automatic, seamless transition between light and dark. + +## FileSource +## Layout +## Rendering +## Annotations + +# Threading + +At runtime, mapbox-gl-native uses the following threads: + +* The "main thread" (or other thread on which a `Map` object is created) handles direct `Map` API requests, owns the active `Style` and its associated objects, and renders the map. Since this thread is usually dispatching events triggered by user input, it's important that these duties not require significant computation or perform blocking I/O that would cause UI jank or hangs. +* Many of the tasks that require significant computation are associated with layout and styling of map features: parsing vector tiles, computing text layout, and generating data buffers to be consumed by OpenGL. This work happens on "worker threads" that are spawned by the main thread, four per `Style` object. The `Style` and its associated objects handle dispatching tasks to the workers, typically on a tile-by-tile basis. +* A "FileSource" thread handles network requests for styles, tiles, and other resources, and I/O on the SQLite database used for offline maps and caching. Requests originate from the main thread, are dispatched by a FileSource internally to its thread, and results are returned to the main thread via an asynchronous callback function. (This is implemented with platform-specific hooks into the native message pump for the requesting thread.) + +To minimize data races and invalid memory access, we aim for zero shared memory state between any two threads. Cross-thread communication occurs via one thread requesting the "invocation" of a task on another thread. The parameters for this task should be passed as value objects, so that they are copied, or as ownership-transferring values such as `std::unique_ptr`. (We're [not entirely there yet](https://github.com/mapbox/mapbox-gl-native/issues/667), but that's the goal.) The result (if any) is likewise passed as a value or ownership-transferring parameter to the callback function. + +Invoking a task on another thread itself creates an ownership obligation: the responsibility for the work happening on the other thread. This ownership is represented by the invocation method returning `std::unique_ptr`. Destroying this object indicates that the result of the task is no longer required: the callback is guaranteed not to be called, and the work on the other thread may be aborted (if doing so is convenient). + +All this is implemented by [Thread](https://github.com/mapbox/mapbox-gl-native/blob/master/src/mbgl/util/thread.hpp) and [RunLoop](https://github.com/mapbox/mapbox-gl-native/blob/master/include/mbgl/util/run_loop.hpp). diff --git a/mapbox-gl-native/CHANGELOG.md b/mapbox-gl-native/CHANGELOG.md new file mode 100644 index 000000000..47623a22d --- /dev/null +++ b/mapbox-gl-native/CHANGELOG.md @@ -0,0 +1,515 @@ +# Changelog + +## master + +## maps-v1.6.0 + +### ✨ New features + +- [core] Add support for `Polygon`, `MultiPolygon` geometry types in `distance` expression. ([#16446](https://github.com/mapbox/mapbox-gl-native/pull/16446)) + +- [core] Introduce a new API for controlling the camera ([#16419]https://github.com/mapbox/mapbox-gl-native/pull/16419) + + Functions `Map::getFreeCameraOptions()` and `Map::setFreeCameraOptions(options)` provides an alternative way of accessing and modifying properties of the underlying camera entity. The API allows the user to treat the camera as a more traditional 3D object by modifying its position and orientation directly as opposed to setting the state implicitly by using `CameraOptions`. Both APIs are fully compatible with each other. + +### 🐞 Bug fixes + +- [core][tile mode] Labels priority fixes ([#16432](https://github.com/mapbox/mapbox-gl-native/pull/16432)) + + This change does the following: + + - strictly arranges all the intersecting labels accordingly to the style-defined priorities + - fixes placement order of the variable labels. + Before this change, all variable labels that could potentially intersect tile borders were placed first, breaking the style label placement priority order. Now, all the variable labels, which do not actually intersect the tile borders, are placed accordingly to the style-defined priorities + +## maps-v1.6.0-rc.2 + +### ✨ New features + +- [core] Introduce Source::setMinimumTileUpdateInterval API ([#16416](https://github.com/mapbox/mapbox-gl-native/pull/16416)) + + The `Source::setMinimumTileUpdateInterval(Duration)` method sets the minimum tile update interval, which is used to throttle the tile update network requests. + + The corresponding `Source::getMinimumTileUpdateInterval()` getter is added too. + + Default minimum tile update interval value is `Duration::zero()`. + +- [core] Indroduce `distance` expression. ([#16397](https://github.com/mapbox/mapbox-gl-native/pull/16397)) + + The `distance` expression returns the shortest distance between two geometries. The returned value can be consumed as an input into another expression for changing a paint or layout property or filtering features by distance. + + Currently, the `distance` expression supports `Point`, `MultiPoint`, `LineString`, `MultiLineString` geometry types. + +- [core] Introduce style::Source::setVolatile()/isVolatile() API ([#16422](https://github.com/mapbox/mapbox-gl-native/pull/16422)) + + The `Source::setVolatile(bool)` method sets a flag defining whether or not the fetched tiles for the given source should be stored in the local cache. + + The corresponding `Source::isVolatile()` getter is added too. + + By default, the source is not volatile. + +- [ios, macos] Allow specifying multiple fonts or font families for local font rendering ([#16253](https://github.com/mapbox/mapbox-gl-native/pull/16253)) + + By default, CJK characters are now set in the font specified by the `text-font` layout property. If the named font is not installed on the device or bundled with the application, the characters are set in one of the fallback fonts passed into the `localFontFamily` parameter of `mbgl::Renderer::Renderer()` and `mbgl::MapSnapshotter::MapSnapshotter()`. This parameter can now contain a list of font family names, font display names, and font PostScript names, each name separated by a newline. + +### 🐞 Bug fixes + +- [ios, macos] Fixed error receiving local file URL response ([#16428](https://github.com/mapbox/mapbox-gl-native/pull/16428)) + +- [ios, macos] Corrected metrics of locally rendered fonts ([#16253](https://github.com/mapbox/mapbox-gl-native/pull/16253)) + + CJK characters are now laid out according to the font, so fonts with nonsquare glyphs have the correct kerning. This also fixes an issue where the baseline for CJK characters was too low compared to non-CJK characters. + +## maps-v1.6.0-rc.1 + +### ✨ New features + +- [core] Move logging off the main thread ([#16325](https://github.com/mapbox/mapbox-gl-native/pull/16325)) + +- Add source property to limit parent's tile overscale factor ([#16347](https://github.com/mapbox/mapbox-gl-native/pull/16347)) + + The new property sets a limit for how much parent tile can be overscaled. + +- [core][tile mode] Introduce API to collect placed symbols data ([#16339](https://github.com/mapbox/mapbox-gl-native/pull/16339)) + + The following methods are added to the `Renderer` class in implemented in the Tile map mode: + - `collectPlacedSymbolData()` + enables or disables collecting of the placed symbols data + + - `getPlacedSymbolsData()` + if collecting of the placed symbols data is enabled, returns the reference to the `PlacedSymbolData` vector holding the collected data. + +- [core] Enable circle-sort-key property ([#15875](https://github.com/mapbox/mapbox-gl-native/pull/15875/)) + + Adds support for `circle-sort-key` property, consistent with `symbol-sort-key`. + + Sorts drawing order by sort key both within-tile and cross-tile. + +- [core] Add LocationIndicator layer ([#16340](https://github.com/mapbox/mapbox-gl-native/pull/16340)) + + Adds a new layer type, `location-indicator`, that can be used to add a source-less indicator to the map, comprising raster images and a precision radius in meters. + +- Add generic setter for Layer's 'source' property ([#16406](https://github.com/mapbox/mapbox-gl-native/pull/16406)) + +### 🐞 Bug fixes + +- [core][tile mode] Reduce cut-off labels (part 2) ([#16369](https://github.com/mapbox/mapbox-gl-native/pull/16369)) + + Now, the intersecting symbols are placed across all layers _symbol by symbol_ according to the following rules: + + 1. First we look, which of the tile border(s) the symbol intersects and prioritize the the symbol placement accordingly (high priority -> low priority): `vertical & horizontal -> vertical -> horizontal` + 2. For the symbols that intersect the same tile border(s), assuming the tile border split symbol into several sections, we look at the minimal section length. The symbol with a larger minimal section length is placed first. + 3. For the symbols that intersect the same tile border(s), and have equal minimal section length, we look at the anchor coordinates. + 4. Finally, if all the previous criteria are the same, we look at the symbol key hashes. + +- [core][tile mode] Fix variable placement for labels with the `icon-text-fit` property set ([#16382](https://github.com/mapbox/mapbox-gl-native/pull/16382)) + + The `symbolIntersectsTileEdges()` util in `mbgl::TilePlacement` now considers icon shift for the variable symbols with enabled icon-text-fit setting, thus providing more accurate results. + +- [core] Correctly log a warning instead of crashing when a non-existent image is attempted to be removed. ([#16391](https://github.com/mapbox/mapbox-gl-native/pull/16391)) + +- [core] Fix segfault resulting from an invalid geometry ([#16409](https://github.com/mapbox/mapbox-gl-native/pull/16409)) + +- [macos] Restored support for macOS 10.11–10.14 ([#16412](https://github.com/mapbox/mapbox-gl-native/pull/16412)) + +## maps-v1.5.1 + +### 🐞 Bug fixes + +- [core] Fix assert in gfx resources cleanup ([#16349](https://github.com/mapbox/mapbox-gl-native/pull/16349)) + + Fix a resource leak assertion in `gl::Context::~Context()` that is evaluating false in scenarios where graphics context has been marked as lost. + +- Hillshade bucket fix for mapbox-gl-native-ios #240 ([#16362](https://github.com/mapbox/mapbox-gl-native/pull/16362)) + + When dem tiles are loaded, border in neighboring tiles is updated, too leading to bucket re-upload. if std::move moved indices / vertices previously, they are empty and we get crash. Re-upload requires that only DEM texture is re-uploaded, not the quad vertices and indices. + +## maps-v1.5.0 + +### ✨ New features + +- [core] Add Renderer::clearData() ([#16323](https://github.com/mapbox/mapbox-gl-native/pull/16323)) + + The newly added `Renderer::clearData()` method allows to clear render data and thus save memory and make sure outdated tiles are not shown. It clears data more agressively than `Renderer::reduceMemoryUse()` does, as it clears not only the cache but all orchestration data, including the data used by the currently rendered frame. + +- [android] Add jni binding for styleable snapshotter ([#16286](https://github.com/mapbox/mapbox-gl-native/pull/16286)) + +- [core] Ability to set generic layer properties using setProperty method ([#16324](https://github.com/mapbox/mapbox-gl-native/pull/16324)) + This change enables the following new keys for the `mbgl::Layer::setProperty()` API: + - "filter" invokes `setFilter()` + - "minzoom" invokes `setMinZoom()` + - "maxzoom" invokes `setMaxZoom()` + - "source-layer" invokes `setSourceLayer()` + + The newly-added API is used in the style-conversion code, which made this code much simpler. + +- [android] Expose getLayer, getSource and Observer interface for snapshotter ([#16338](https://github.com/mapbox/mapbox-gl-native/pull/16338)) + +### 🐞 Bug fixes + +- [core] Use `TileCoordinates` instead of `LngLat` for `within` expression calculation ([#16319](https://github.com/mapbox/mapbox-gl-native/pull/16319)) + + Fix the issue that `within` expression evaluates point features inconsistently across zoom levels if the point lies near the boundary of a GeoJSON object ([#16301](https://github.com/mapbox/mapbox-gl-native/issues/16301)) + +- [core][tile mode] Reduce cut-off labels ([#16336](https://github.com/mapbox/mapbox-gl-native/pull/16336)) + + Place tile intersecting labels first, across all the layers. Thus we reduce the amount of label cut-offs in Tile mode. + + Before, labels were arranged within one symbol layer (one bucket),which was not enough for several symbol layers being placed at the same time. + +- [core] Fix issue that `within` expression returns incorrect results for geometries crossing the anti-meridian ([#16330](https://github.com/mapbox/mapbox-gl-native/pull/16330)) + +## maps-v1.4.1 + +### 🐞 Bug fixes + +- [android] Fix wrong method call in map_snapshotter ([#16308](https://github.com/mapbox/mapbox-gl-native/pull/16308)) + + In map_snapshotter, a wrong method call will cause Sanpshotter not works with a style url in Android. This change makes it call the right method to let Snapshotter works. + +## maps-v1.4.0 + +### ✨ New features + +- [android] Add jni binding for line-sort-key and fill-sort-key ([#16256](https://github.com/mapbox/mapbox-gl-native/pull/16256)) + + With this change, android sdk will be able to get sort key for LineLayer and FillLayer. + +- Styleable MapSnapshotter ([#16268](https://github.com/mapbox/mapbox-gl-native/pull/16268)) + + New feature provides means of modifying style of a MapSnapshotter. The new API enables several use-cases, such as: adding route overlays, removing extra information (layers) from a base style, adding custom images that are missing from a style. + +- [core] Improve stability of symbol placement when the map is tilted ([#16287](https://github.com/mapbox/mapbox-gl-native/pull/16287)) + + These changes improve performance and bring more stability to the symbol placement for the tilted view, which is mainly used for navigation scenarios. + +### 🐞 Bug fixes + +- [core] Fix iterators in addRegularDash() ([#16249](https://github.com/mapbox/mapbox-gl-native/pull/16249)) + + Fixes possible crashes when using styles with line patterns. + +- [default] Fix possible crash at RunLoop::wake() ([#16255](https://github.com/mapbox/mapbox-gl-native/pull/16255)) + +- [android] Update toGeoJSON in android_conversion.hpp ([#16243](https://github.com/mapbox/mapbox-gl-native/pull/16243)) + + Before this chage, `toGeoJSON` method in `android_conversion.hpp` could not convert an Object (Map in android) to GeoJSON object. + + But `within` expression needs to accept an Object and then convert it to the GeoJSON object, now `toGeoJSON` method can convert both string and Object to GeoJSON. + +- [core] Fix `within` expression algorithm so that `false` value will be returned when point is on the boundary. Allow using different GeoJSON formats as arguments of `within` expression.([#16232](https://github.com/mapbox/mapbox-gl-native/pull/16232)) + + A valid GeoJSON argument should contain one of the following types: `"Feature"`, `"FeatureCollection"`,`"Polygon"` or `"MultiPolygon"`. + +- [core] [tile mode] placement algorithm must consider icons bounding boxes ([#16277](https://github.com/mapbox/mapbox-gl-native/pull/16277)) + + Tile mode placement algorithm now checks if bounding boxes for both label text and icon are intersecting the edges of the tiles. + + Before, it checked only text bounding boxes and thus label icons might have got cut off. + +- [core] Calculate size of an ambient cache without offline region's resources ([#15622](https://github.com/mapbox/mapbox-gl-native/pull/15622)) + + Resources that belong to an offline region, should not contribute to the amount of space available in the ambient cache. + + - [core][tile mode] Fix assertion at `line-center` placement handling ([#16293](https://github.com/mapbox/mapbox-gl-native/pull/16293)) + + The `Symbol Intersects Tile Edges` placement algorithm should not be applied to the symbols with `line-center` placement. + + - Fixed using of the `in` expression as a layer filter ([#16272](https://github.com/mapbox/mapbox-gl-native/pull/16272)) + + The bug was caused by `mbgl::style::conversion::isExpression()` always returning `false` for the `in` expression. + +### 🧩 Architectural changes + +- Changes to `MapSnapshotter` threading model ([#16268](https://github.com/mapbox/mapbox-gl-native/pull/16268)) + + Snapshotter's threading model has been changed. Previously, `Map` and `HeadlessFrontend` that is responsible for rendering snapshot, were running on a dedicated thread. After [#16268](https://github.com/mapbox/mapbox-gl-native/pull/16268), `Map` object lives on client thread, so that the client can access `Style` object, while `HeadlessFrontend` lives on a dedicated `Snapshotter` thread. + +##### ⚠️ Breaking changes + +- Signature of a `MapSnapshotter`'s constructor has been changed +- Signature for a `MapSnapshotter::snapshot` method has been changed +- Size of an offline regions do not affect ambient cache size ([#15622](https://github.com/mapbox/mapbox-gl-native/pull/15622)) + +##### 📌 Known issues + +- When feature is exactly on the geometry boundary, `within` expression returns inconsistent values for different zoom levels ([#16301](https://github.com/mapbox/mapbox-gl-native/issues/16301)) + +## maps-v1.3.0 (2020.02-relvanillashake) + +### 🐞 Bug fixes + +- [core] Fix offline region download freezing ([#16230](https://github.com/mapbox/mapbox-gl-native/pull/16230)) + + Downloaded resources are put in the buffer and inserted in the database in batches. + + Before this change, the buffer was flushed only at the network response callback and thus it never got flushed if the last required resources were present locally and did not initiate network requests - it caused freezing. + + Now the buffer is flushed every time the remaining resources container gets empty. + +### ✨ New features + +- [core] Add Layer::serialize() method ([#16231](https://github.com/mapbox/mapbox-gl-native/pull/16231)) + + New method allows serialization of a layer into a Value type, including all modifications done via runtime style API. New method is also an enabler for Style object serialization (sources, layers, etc). + +- [android] Add jni binding for min and max pitch ([#16236](https://github.com/mapbox/mapbox-gl-native/pull/16236)) + +- [offline] Offline tool does not hang on 404 error ([#16240](https://github.com/mapbox/mapbox-gl-native/pull/16240)) + + The missing resource gets skipped and teh offline region download continues. + +##### ⚠️ Breaking changes + +- Changes to `mbgl::FileSourceManager::getFileSource()` ([#16238](https://github.com/mapbox/mapbox-gl-native/pull/16238)) + + It returns now `mbgl::PassRefPtr` (previously was `std::shared_ptr`) in order to enforce keeping the strong reference to the returned object. + + Breaking code example: + `auto fs = FileSourceManager::getFileSource(); fs->..` + + Posible fix: + `std::shared_ptr fs = `; + +- The `mbgl::OnlineFileSource` class cannot be used directly ([#16238](https://github.com/mapbox/mapbox-gl-native/pull/16238)) + + Clients must use the parent `mbgl::FileSource` interface instead. + + Breaking code example: + `std::shared_ptr onlineSource = std::static_pointer_cast(FileSourceManager::get()->getFileSource(..));` + + Possible fix: + `std::shared_ptr onlineSource = FileSourceManager::get()->getFileSource(..);` + +## maps-v1.2.1 (2020.02-release-vanillashake) + +### 🐞 Bug fixes + +- [core] Revert shader changes from [#16189](https://github.com/mapbox/mapbox-gl-native/pull/16189), which will cause incorrect label positioning ([#16235](https://github.com/mapbox/mapbox-gl-native/pull/16235)) + + This is a back porting from GL JS [#9333](https://github.com/mapbox/mapbox-gl-js/pull/9333) + +- [ios] Restored support for iOS 9–11 by default ([#16241](https://github.com/mapbox/mapbox-gl-native/pull/16242)) + +## maps-v1.2.0 (2020.02-release-vanillashake) + +### ✨ New features + +- [core] Global settings API ([#16174](https://github.com/mapbox/mapbox-gl-native/pull/16174)) + + Global settings API provides means of managing non-persistent in-process settings. Initial implementation contains support for experimental option for setting thread priorities. + +- Expose READ_ONLY_MODE_KEY property for DatabaseFileSource ([#16183](https://github.com/mapbox/mapbox-gl-native/pull/16183)) + + The `READ_ONLY_MODE_KEY` property is exposed for `DatabaseFileSource`. + + This property allows to re-open the offline database in read-only mode and thus improves the performance for the set-ups that do not require the offline database modifications. + +- [core] Add runtime API for setting tile prefetch delta for a Source ([#16179](https://github.com/mapbox/mapbox-gl-native/pull/16179)) + + The new `Source::setPrefetchZoomDelta(optional)` method allows overriding default tile prefetch setting that is defined by the Map instance. + +- [core] Add support for `within expression`. Implement the use of `within expression` with paint propery and filter expression. ([#16157](https://github.com/mapbox/mapbox-gl-native/pull/16157)) + + The `within expression` enables checking whether a feature is inside a pre-defined geometry set/boundary or not. This `within expression` returns a boolean value, `true` indicates that the feature being evaluated is inside the geometry set. The returned value can be then consumed as input by another expression or used directly by a paint/layer property. + + Support for using `within expression` with layout property will be implemented separately. + +- [core] Add support for using `within expression` with layout property. ([#16194](https://github.com/mapbox/mapbox-gl-native/pull/16194)) + +- [core] Add support for `in expression`. ([#16162](https://github.com/mapbox/mapbox-gl-native/pull/16162)) + + The `in expression` enables checking whether a Number/String/Boolean type item is in a String/Array and returns a boolean value. + +- [core] Add support for using `within expression` with features having `'LineString'` geometry type. ([#16220](https://github.com/mapbox/mapbox-gl-native/pull/16220)) + + `within expression` now supports features with geometry types: `'Point'` or `'LineString'`. + +### 🐞 Bug fixes + +- [core] Don't provide multiple responses with the same data for 304 replies ([#16200](https://github.com/mapbox/mapbox-gl-native/pull/16200)) + + In cases when cached resource is useable, yet don't have an expiration timestamp, we provided data to the requester from the cache and the same data was returned once 304 response was received from the network. + +- [core] Fix potential visual artifact for line-dasharray ([#16202](https://github.com/mapbox/mapbox-gl-native/pull/16202)) + +- Store gfx::DrawScope objects with associated render objects. ([#15395](https://github.com/mapbox/mapbox-gl-native/pull/15395)) + + We used some shared SegmentVectors, e.g. for drawing raster or background tiles. In longer running maps, this lead to resource accumulation. By storing the SegmentVectors and the contained gfx::DrawScope objects, we ensure that resources get released when the associated render objects vanish. + +- [core] Fix sprite sheet merging in Style::Impl::onSpriteLoaded ([#16211](https://github.com/mapbox/mapbox-gl-native/pull/16211)) + + If we get a new sprite sheet from the server, we need to merge current sprite sheet with a new one, while overwriting duplicates and keeping old unique images in a style. + +### 🏁 Performance improvements + + - [core] Loading images to style optimization ([#16187](https://github.com/mapbox/mapbox-gl-native/pull/16187)) + + This change enables attaching images to the style with batches and avoids massive re-allocations. Thus, it improves UI performance especially at start-up time. + +### 🧩 Architectural changes + +##### ⚠️ Breaking changes + + - [core] Loading images to style optimization ([#16187](https://github.com/mapbox/mapbox-gl-native/pull/16187)) + + The `style::Style::getImage()` semantics changed - it now returns `optional`. + +## maps-v1.0.1 (2020.01-release-unicorn) + +### 🐞 Bug fixes + +- [core] Use std::list instead of std::map for factory instance ([#16161](https://github.com/mapbox/mapbox-gl-native/pull/16161)) + + Factory 'get' method can be invoked recursively and stable iterators are required to guarantee safety. + +- [tile mode] Improvements in symbol placement on tile borders ([#16159](https://github.com/mapbox/mapbox-gl-native/pull/16159)) + + In tile mode, the placement order of two symbols crossing a tile border is defined by their anchor Y values. + + Symbols crossing the borders between two neighboring tiles are placed with priority. + + It improves symbol placement stability in the tile map mode. + +## maps-v1.0.0 (2020.01-release-unicorn) + +### ✨ New features + +- [core] Implement stretchable icons ([#16030](https://github.com/mapbox/mapbox-gl-native/pull/16030)) + + [Stretchable icons](https://github.com/mapbox/mapbox-gl-js/issues/8917) allow defining certain areas of an icon that may be stretched, leaving the remaining areas of an icon at a fixed size. + +- [core] Port line-sort-key and fill-sort-key ([#15839](https://github.com/mapbox/mapbox-gl-native/pull/15839)) + + The new feature allows to sort line and fill layer features. Similar to `symbol-sort-key`. + +- [core] Add image sections to format expression ([#15937](https://github.com/mapbox/mapbox-gl-native/pull/15937)) + + The new feature allows to embed images into labels. + +- [core] Introduce OfflineDatabase::runPackDatabaseAutomatically() API ([#15967](https://github.com/mapbox/mapbox-gl-native/pull/15967)) + + The new API allows to enable / disable automatic packing of a database. + +- [core] Decouple offline storage vacuum and the deleteRegion API ([#15899](https://github.com/mapbox/mapbox-gl-native/pull/15899)) + - introduce `OfflineDatabase::pack()` standing for incremental vacuum + - make packing optional at offline region deletion + +- [core] Implement image expression (#15877) + + The [image expression](https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-image) allows determining an image's availability. + +- [core] Add batch conversion of latLngs to/from screenCoords ([#15891](https://github.com/mapbox/mapbox-gl-native/pull/15891)) + + This patch introduces batch conversion between LatLng and ScreenCoordinate in Gl-Native core, so for multiple conversions with single point/latLng previously now it can be done with invoking one function call by passing vector of points/latLngs. + +### 🐞 Bug fixes +- [tile mode] Fix variable symbols placement ([#16141](https://github.com/mapbox/mapbox-gl-native/pull/16141) + + This change allows the variable symbols to cross the tile border only if their anchor is the first anchor from the `text-variable-anchor` list. + +- [core] Use weak scheduler inside mailbox ([#16136](https://github.com/mapbox/mapbox-gl-native/pull/16136)) + + If there are pending requests that are completed after main scheduler is destructed, worker threads may try to push messages to destructed scheduler's queue. + +- [core] Fix a crash in GeoJSON source parsing, caused by `GeoJSONVTData` ownership error ([#16106](https://github.com/mapbox/mapbox-gl-native/pull/16106)) + +- [core] Stable position of labels at tile borders in tile mode ([#16040](https://github.com/mapbox/mapbox-gl-native/pull/16040)) + + These changes allow to avoid cutting-off labels on tile borders if the variable text placement is enabled. + +- [core] Fix really overscaled lines ([#16045](https://github.com/mapbox/mapbox-gl-native/pull/16045)) + + We resample lines after sharp corners in some situations. When tiles were really overscaled, sometimes we didn't have enough precision to represent the resampled vertex because it was too close. Disabling this after a certain point prevents this ([#16018](https://github.com/mapbox/mapbox-gl-native/issues/16018)). + +- [core] Don't use signed int type for anchor segment ([#16008](https://github.com/mapbox/mapbox-gl-native/pull/16008)) + + Erroneous signed to unsigned implicit conversion was used when PlacedSymbol(s) were created in SymbolLayout and signed values were used as indexes in a few other places. + +- [core] Increase padding in CollisionIndex for MapMode::Tile ([#15880](https://github.com/mapbox/mapbox-gl-native/pull/15880)) + + This increases the padding to 1024 for MapMode::Tile which should be enough to completely include all the data two zoom levels past the data source's max zoom level. Beyond that, 1024 should be enough of a padding to make clipped labels unlikely. + +- [core] Blacklist VAO extension for Adreno (TM) 4xx GPUs ([#15978](https://github.com/mapbox/mapbox-gl-native/pull/15978)) + + Blacklist in order to avoid crashes in a driver (Android 5.1.1) + +- [core] Retain thread pool in GeoJSONSource ([#15992](https://github.com/mapbox/mapbox-gl-native/pull/15992)) + + Otherwise, the construction of the `Immutable` in background thread might never yeld. + +- [core] Fix supercluster lambdas capturing ([#15989](https://github.com/mapbox/mapbox-gl-native/pull/15989)) + + Fix wrapping of the parameters in supercluster map/reduce lambdas (Previously, local variables were wrapped by reference). + +- [core] fix rendering of dashed lines with round caps ([#15947](https://github.com/mapbox/mapbox-gl-native/pull/15947)) + + In [#15862](https://github.com/mapbox/mapbox-gl-native/pull/15862), we introduced individual textures for line dash patterns to eliminate atlas overflows. Unfortunately, this broke dashed lines that had round caps (dashed lines with straight caps still rendered correctly). Line pattern textures for round caps were now using 256×15 pixel textures. + +- [core] Fix incorrect resizing of TileCache ([#15943](https://github.com/mapbox/mapbox-gl-native/pull/15943)) + +- [core] Avoid GeoJSON source flickering on style transitions ([#15907](https://github.com/mapbox/mapbox-gl-native/pull/15907)) + + Before this change, all GeoJSON source cleared tile pyramid at the style transition and it caused flickering. + +- [core] Use individual textures for line dash patterns ([#15862](https://github.com/mapbox/mapbox-gl-native/pull/15862)) + + This moves the LineAtlas from a shared texture that contained SDF dash patterns to use individual textures. + +- [core] Fix collisions with icon-text-fit and variable placement ([#15828](https://github.com/mapbox/mapbox-gl-native/pull/15828)) + + When deciding the placement position it was only checking whether there were text collisions. Now, if icon-text-fit is used it now checks whether the icon fits before accepting a placement position. + +- [core] Fix icon-text-fit ([#15634](https://github.com/mapbox/mapbox-gl-native/pull/15634)) + + This fixes rendering by account for the 1px texture padding around icons that were stretched with icon-text-fit. + +### 🏁 Performance improvements + +- [core] Cross tile index performance ([#16127](https://github.com/mapbox/mapbox-gl-native/pull/16127)) + + For overscaled tiles, index only the symbols inside the viewport. + + Find matches only among the buckets that have the same leader Id. + +- [core] Calculate GeoJSON tile geometries in a background thread ([#15953](https://github.com/mapbox/mapbox-gl-native/pull/15953)) + + Call `mapbox::geojsonvt::GeoJSONVT::getTile()` in a background thread, so that the rendering thread is not blocked. + +- [core] Make requests for expired resources lower priority than requests for new resources ([#15950](https://github.com/mapbox/mapbox-gl-native/pull/15950)) + +- [core] Add Scheduler::scheduleAndReplyValue() API ([#15885](https://github.com/mapbox/mapbox-gl-native/pull/15885)) + + This change converts GeoJSON features to tiles for the loaded source description in a background thread and thus unblock the UI thread. + +- [core] Enable incremental vacuum for Offline DB ([#15837](https://github.com/mapbox/mapbox-gl-native/pull/15837)) + + Thus we avoid re-creating the whole database and keeping the backup file as it happens on calling VACUUM. + +- [core] Fix image requests for already obtained images ([#15825](https://github.com/mapbox/mapbox-gl-native/pull/15825)) + + Before this fix, repeated request for an already obtained image was erroneously treated as pending, and it prevented from the tiles load completion. + +### 🧩 Architectural changes + +- [core] Merge style::Layer::set{Layout,Paint}Property ([#15997](https://github.com/mapbox/mapbox-gl-native/pull/15997)) + +- [core] Use expected.hpp from mapbox-base ([#15898](https://github.com/mapbox/mapbox-gl-native/pull/15898)) + +##### ⚠️ Breaking changes + +- [core] Cross tile index performance (#16127) + + Introduces public [mblg::Renderer::render](https://github.com/mapbox/mapbox-gl-native/pull/16127/files#diff-5a977e0401792825d7fcf522d48df11fR34) API break. + +- [core] Refactor DefaultFileSource codebase ([#15768](https://github.com/mapbox/mapbox-gl-native/pull/15768)) + - Adds `FileSourceManager` interface that provides access to `FileSource` instances and means of registering / unregistering `FileSource` factories + - Splits `DefaultFileSource` into smaller parts + - Adds `DatabaseFileSource` interface and it's default implementation + - Removes inter-dependencies between concrete `FileSource` classes + - All sources operate on dedicated thread, except `MainResourceLoader` that acts as a dispatcher and works on thread that requested it. + - Removes `ResourceOptions::withCacheOnlyRequestsSupport` method + +- [core] Remove Map::cycleDebugOptions ([#16005](https://github.com/mapbox/mapbox-gl-native/pull/16005)) + + This function was mostly used by the Android API, which is no longer necessary. diff --git a/mapbox-gl-native/CMakeLists.txt b/mapbox-gl-native/CMakeLists.txt new file mode 100644 index 000000000..ad989ba83 --- /dev/null +++ b/mapbox-gl-native/CMakeLists.txt @@ -0,0 +1,1053 @@ +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +project("Mapbox GL Native" LANGUAGES CXX C) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER Core) + +set( + MEMORYCHECK_COMMAND_OPTIONS + "--leak-check=full --gen-suppressions=all --error-exitcode=1 --suppressions=${PROJECT_SOURCE_DIR}/scripts/valgrind.sup" +) + +include(CTest) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +option(MBGL_WITH_CORE_ONLY "Build only the core bits, no platform code" OFF) +option(MBGL_WITH_COVERAGE "Enable code coverage collection" OFF) +option(MBGL_WITH_QT "Build Mapbox GL Qt bindings" OFF) +option(MBGL_WITH_SANITIZER "Use [address|thread|undefined] here" OFF) +option(MBGL_WITH_RTTI "Compile with runtime type information" OFF) +option(MBGL_WITH_OPENGL "Build with OpenGL renderer" ON) +option(MBGL_WITH_WERROR "Make all compilation warnings errors" ON) + +add_library( + mbgl-compiler-options INTERFACE +) + +set(UBSAN_BLACKLIST ${PROJECT_SOURCE_DIR}/scripts/ubsan.blacklist) + +target_compile_options( + mbgl-compiler-options + INTERFACE + $<$:--coverage> + $<$:-DSANITIZE> + $<$:-fno-omit-frame-pointer> + $<$:-fno-optimize-sibling-calls> + $<$:-fsanitize=address> + $<$:-fsanitize=thread> + $<$:-fsanitize=implicit-conversion, + -fsanitize-blacklist=${UBSAN_BLACKLIST}> + $<$:-fsanitize=undefined, + -fsanitize-blacklist=${UBSAN_BLACKLIST}> + $<$:-fsanitize=unsigned-integer-overflow, + -fsanitize-blacklist=${UBSAN_BLACKLIST}> + $<$:-fsanitize=nullability, + -fsanitize-blacklist=${UBSAN_BLACKLIST}> + $<$:-fsanitize=float-divide-by-zero, + -fsanitize-blacklist=${UBSAN_BLACKLIST}> + $<$,$>>:-fno-rtti> + $<$,$>>:-Wall> + $<$,$>>:-Wshadow> + $<$,$>>:-Wextra> + $<$,$>,$>:-Werror> + $<$,$>:-Wno-error=tautological-constant-compare> + $<$:-Wno-error=maybe-uninitialized> + $<$:-Wno-error=return-type> + $<$:-Wno-error=unknown-pragmas> + $<$:-Wno-error=deprecated-declarations> + $<$:-Wno-error=unused-parameter> + $<$:-Wno-error=unused-property-ivar> +) + +target_link_libraries( + mbgl-compiler-options + INTERFACE + $<$:--coverage> + $<$:-fsanitize=address> + $<$:-fsanitize=thread> + $<$:-fsanitize=undefined> +) + +cmake_policy(SET CMP0063 NEW) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_C_EXTENSIONS OFF) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_VISIBILITY_PRESET hidden) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) + +if(MBGL_WITH_QT) + find_package(Qt5Core REQUIRED) + set(CMAKE_AUTOMOC ON) + set(CMAKE_AUTORCC ON) +endif() + +add_library( + mbgl-core STATIC + ${PROJECT_SOURCE_DIR}/include/mbgl/actor/actor.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/actor/actor_ref.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/actor/aspiring_actor.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/actor/established_actor.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/actor/mailbox.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/actor/message.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/actor/scheduler.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/annotation/annotation.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/backend_scope.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/renderable.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/renderer_backend.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/rendering_stats.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/background_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/circle_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/fill_extrusion_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/fill_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/heatmap_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/hillshade_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/layer_manager.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/line_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/raster_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/symbol_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/map/bound_options.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/map/camera.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/map/change.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/map/map.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/map/map_observer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/map/map_options.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/map/mode.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/map/projection_mode.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/math/clamp.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/math/log2.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/math/minmax.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/math/wrap.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/platform/settings.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/platform/thread.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/query.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_frontend.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_observer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_state.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/database_file_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/file_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/file_source_manager.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/network_status.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/offline.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/online_file_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/resource.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/resource_options.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/resource_transform.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/storage/response.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/color_ramp_property_value.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/color_ramp_property_value.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/constant.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/coordinate.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/custom_geometry_source_options.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/filter.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/function.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/geojson.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/geojson_options.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/get_json_type.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/light.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/position.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/property_value.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/rotation.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/tileset.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion/transition_options.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/conversion_impl.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/assertion.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/at.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/boolean_operator.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/case.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/check_subtype.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/coalesce.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/coercion.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/collator.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/collator_expression.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/comparison.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/compound_expression.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/dsl.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/distance.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/error.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/expression.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/find_zoom_curve.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/format_expression.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/format_section_override.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/formatted.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/get_covering_stops.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/image.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/image_expression.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/in.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/interpolate.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/interpolator.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/is_constant.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/is_expression.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/length.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/let.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/literal.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/match.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/number_format.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/parsing_context.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/step.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/type.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/value.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/within.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/filter.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/image.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layer_properties.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/background_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/circle_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/fill_extrusion_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/fill_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/heatmap_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/hillshade_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/line_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/raster_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/symbol_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/light.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/position.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/property_expression.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/property_value.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/rotation.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/sources/custom_geometry_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/sources/geojson_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/sources/image_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/sources/raster_dem_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/sources/raster_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/sources/vector_source.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/style.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/style_property.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/transition_options.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/types.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/undefined.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/tile/tile_id.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/tile/tile_necessity.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/async_request.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/async_task.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/char_array_buffer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/chrono.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/color.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/compression.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/constants.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/convert.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/enum.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/event.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/exception.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/expected.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/feature.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/font_stack.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/geo.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/geojson.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/geometry.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/ignore.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/image.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/immutable.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/indexed_tuple.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/interpolate.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/logging.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/noncopyable.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/optional.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/pass_types.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/platform.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/premultiply.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/projection.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/range.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/run_loop.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/size.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/string.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/thread.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/tileset.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/timer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/traits.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/type_list.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/unitbezier.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/util.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/variant.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/vectors.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/work_request.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/work_task.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/util/work_task_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/actor/mailbox.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/actor/scheduler.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/algorithm/update_renderables.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/algorithm/update_tile_masks.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/annotation_manager.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/annotation_manager.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/annotation_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/annotation_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/annotation_tile.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/annotation_tile.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/fill_annotation_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/fill_annotation_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/line_annotation_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/line_annotation_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/render_annotation_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/render_annotation_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/shape_annotation_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/shape_annotation_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/symbol_annotation_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/annotation/symbol_annotation_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/geometry/anchor.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/geometry/debug_font_data.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/geometry/dem_data.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/geometry/dem_data.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/geometry/feature_index.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/geometry/feature_index.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/geometry/line_atlas.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/geometry/line_atlas.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/attribute.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/attribute.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/color_mode.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/command_encoder.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/context.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/cull_face_mode.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/debug_group.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/depth_mode.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/draw_mode.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/draw_scope.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/index_buffer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/index_vector.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/offscreen_texture.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/render_pass.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/renderbuffer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/renderer_backend.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/rendering_stats.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/stencil_mode.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/texture.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/types.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/uniform.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/upload_pass.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/vertex_buffer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/vertex_vector.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/background_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/circle_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/fill_extrusion_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/fill_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/heatmap_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/hillshade_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/layer_manager.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/line_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/raster_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/symbol_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/circle_layout.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/clip_lines.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/clip_lines.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/layout.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/merge_lines.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/merge_lines.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/pattern_layout.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/symbol_feature.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/symbol_instance.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/symbol_instance.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/symbol_layout.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/symbol_layout.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/symbol_projection.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layout/symbol_projection.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/map/map.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/map/map_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/map/map_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/map/map_options.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/map/transform.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/map/transform.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/map/transform_state.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/map/transform_state.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/map/zoom_history.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/math/log2.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/platform/settings.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/attributes.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/background_pattern_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/background_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/background_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/circle_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/circle_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/clipping_mask_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/clipping_mask_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/collision_box_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/collision_box_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/collision_circle_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/debug_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/debug_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/fill_extrusion_pattern_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/fill_extrusion_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/fill_extrusion_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/fill_outline_pattern_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/fill_outline_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/fill_pattern_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/fill_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/fill_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/heatmap_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/heatmap_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/heatmap_texture_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/heatmap_texture_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/hillshade_prepare_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/hillshade_prepare_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/hillshade_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/hillshade_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/line_gradient_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/line_pattern_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/line_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/line_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/line_sdf_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/program_parameters.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/program_parameters.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/programs.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/programs.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/raster_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/raster_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/segment.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/symbol_icon_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/symbol_program.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/symbol_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/symbol_sdf_icon_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/symbol_sdf_text_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/symbol_text_and_icon_program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/textures.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/uniforms.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/backend_scope.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/bucket_parameters.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/bucket_parameters.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/circle_bucket.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/circle_bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/debug_bucket.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/debug_bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/fill_bucket.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/fill_bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/heatmap_bucket.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/heatmap_bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/hillshade_bucket.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/hillshade_bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/line_bucket.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/line_bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/raster_bucket.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/raster_bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/symbol_bucket.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/buckets/symbol_bucket.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/cross_faded_property_evaluator.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/cross_faded_property_evaluator.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/data_driven_property_evaluator.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/group_by_layout.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/group_by_layout.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/image_atlas.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/image_atlas.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/image_manager.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/image_manager.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/image_manager_observer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_background_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_background_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_circle_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_circle_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_fill_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_fill_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_heatmap_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_heatmap_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_hillshade_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_hillshade_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_line_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_line_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_raster_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_raster_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_symbol_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_symbol_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/paint_parameters.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/paint_parameters.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/paint_property_binder.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/paint_property_statistics.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/pattern_atlas.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/pattern_atlas.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/possibly_evaluated_property_value.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/property_evaluation_parameters.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/property_evaluator.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_light.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_light.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_orchestrator.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_orchestrator.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_pass.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_source_observer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_static_data.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_static_data.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_tile.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_tile.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/render_tree.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/renderer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/renderer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/renderer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/renderer_state.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_custom_geometry_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_custom_geometry_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_geojson_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_geojson_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_image_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_image_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_raster_dem_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_raster_dem_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_raster_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_raster_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_tile_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_tile_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_vector_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/sources/render_vector_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/source_state.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/source_state.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/style_diff.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/style_diff.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/tile_mask.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/tile_parameters.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/tile_pyramid.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/tile_pyramid.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/tile_render_data.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/tile_render_data.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/transition_parameters.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/update_parameters.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/upload_parameters.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/sprite/sprite_loader.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/sprite/sprite_loader.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/sprite/sprite_loader_observer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/sprite/sprite_parser.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/sprite/sprite_parser.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/asset_file_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/file_source_manager.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/http_file_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/local_file_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/main_resource_loader.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/network_status.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/resource.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/resource_options.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/resource_transform.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/storage/response.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/collection.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/color_ramp_property_value.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/constant.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/coordinate.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/custom_geometry_source_options.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/filter.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/function.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/geojson.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/geojson_options.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/get_json_type.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/json.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/light.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/position.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/property_value.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/rotation.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/stringify.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/tileset.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/conversion/transition_options.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/custom_tile_loader.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/custom_tile_loader.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/assertion.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/at.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/boolean_operator.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/case.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/check_subtype.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/coalesce.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/coercion.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/collator.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/collator_expression.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/comparison.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/compound_expression.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/distance.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/dsl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/dsl_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/expression.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/find_zoom_curve.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/format_expression.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/formatted.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/get_covering_stops.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/image.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/image_expression.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/in.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/interpolate.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/is_constant.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/is_expression.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/length.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/let.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/literal.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/match.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/number_format.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/parsing_context.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/step.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/util.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/util.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/value.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/within.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/filter.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/image.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/image_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/image_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layer_observer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/background_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/background_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/background_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/background_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/background_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/circle_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/circle_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/circle_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/circle_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/circle_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_extrusion_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_extrusion_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/fill_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/heatmap_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/heatmap_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/heatmap_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/heatmap_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/heatmap_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/hillshade_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/hillshade_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/hillshade_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/hillshade_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/hillshade_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/line_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/line_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/line_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/line_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/line_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/raster_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/raster_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/raster_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/raster_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/raster_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/symbol_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/symbol_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/symbol_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/symbol_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/symbol_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layout_property.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/light.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/light_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/light_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/light_observer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/observer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/paint_property.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/parser.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/parser.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/property_expression.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/rapidjson_conversion.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/source_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/source_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/source_observer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/custom_geometry_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/custom_geometry_source_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/custom_geometry_source_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/geojson_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/geojson_source_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/geojson_source_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/image_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/image_source_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/image_source_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/raster_dem_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/raster_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/raster_source_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/raster_source_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/vector_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/vector_source_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/sources/vector_source_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/style.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/style_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/style_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/types.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/bidi.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/check_max_angle.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/check_max_angle.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/collision_feature.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/collision_feature.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/collision_index.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/collision_index.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/cross_tile_symbol_index.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/cross_tile_symbol_index.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/get_anchors.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/get_anchors.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_atlas.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_atlas.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_manager.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_manager.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_manager_observer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_pbf.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_pbf.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/glyph_range.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/language_tag.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/language_tag.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/local_glyph_rasterizer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/placement.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/placement.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/quads.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/quads.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/shaping.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/shaping.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/tagged_string.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/text/tagged_string.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/custom_geometry_tile.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/custom_geometry_tile.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/geojson_tile.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/geojson_tile.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/geojson_tile_data.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/geometry_tile.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/geometry_tile.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/geometry_tile_data.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/geometry_tile_data.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/geometry_tile_worker.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/geometry_tile_worker.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/raster_dem_tile.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/raster_dem_tile.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/raster_dem_tile_worker.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/raster_dem_tile_worker.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/raster_tile.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/raster_tile.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/raster_tile_worker.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/raster_tile_worker.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_cache.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_cache.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_id_hash.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_id_io.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_loader.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_loader_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/tile_observer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/vector_tile.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/vector_tile.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/vector_tile_data.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/tile/vector_tile_data.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/camera.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/bounding_volumes.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/bounding_volumes.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/chrono.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/color.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/constants.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/convert.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/dtoa.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/dtoa.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/event.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/font_stack.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/geo.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/geojson_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/geometry_util.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/geometry_util.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/grid_index.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/grid_index.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/hash.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/http_header.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/http_header.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/http_timeout.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/http_timeout.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/i18n.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/i18n.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/interpolate.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/intersection_tests.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/intersection_tests.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/io.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/io.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/literal.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/logging.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/longest_common_subsequence.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/mapbox.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/mapbox.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/mat2.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/mat2.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/mat3.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/mat3.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/mat4.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/mat4.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/math.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/premultiply.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/quaternion.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/rapidjson.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/rapidjson.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/rect.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/std.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/stopwatch.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/stopwatch.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/string.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/thread.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/thread_local.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/thread_pool.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/thread_pool.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/tile_coordinate.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/tile_cover.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/tile_cover.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/tile_cover_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/tile_cover_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/tile_range.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/tiny_sdf.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/tiny_sdf.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/token.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/url.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/url.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/utf.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/version.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/version.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/util/work_request.cpp +) + +if(MBGL_WITH_OPENGL) + message("-- Configuring GL-Native with OpenGL renderer backend") + target_compile_definitions( + mbgl-core + PRIVATE MBGL_RENDER_BACKEND_OPENGL=1 + ) + target_sources( + mbgl-core + PRIVATE + ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/backend.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gl/custom_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gl/custom_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gl/renderable_resource.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gl/renderer_backend.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/layermanager/location_indicator_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/platform/gl_functions.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/location_indicator_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/attribute.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/attribute.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/command_encoder.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/command_encoder.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/context.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/context.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/custom_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/custom_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/custom_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/custom_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/debugging_extension.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/debugging_extension.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/defines.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/draw_scope_resource.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/enum.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/enum.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/extension.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/framebuffer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/index_buffer_resource.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/index_buffer_resource.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/object.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/object.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/offscreen_texture.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/offscreen_texture.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/program.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_custom_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_custom_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_pass.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_pass.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/renderbuffer_resource.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/renderer_backend.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/state.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/texture.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/texture.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/texture_resource.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/texture_resource.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/types.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/uniform.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/uniform.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/upload_pass.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/upload_pass.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/value.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/value.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/vertex_array.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/vertex_array.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/vertex_array_extension.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/vertex_buffer_resource.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/vertex_buffer_resource.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/location_indicator_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/platform/gl_functions.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/background.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/background_pattern.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/circle.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/clipping_mask.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/collision_box.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/collision_circle.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/debug.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/fill.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/fill_extrusion.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/fill_extrusion_pattern.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/fill_outline.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/fill_outline_pattern.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/fill_pattern.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/heatmap.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/heatmap_texture.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/hillshade.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/hillshade_prepare.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/line.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/line_gradient.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/line_pattern.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/line_sdf.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/preludes.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/raster.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/shader_source.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/shader_source.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/shaders.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/shaders.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/symbol_icon.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/symbol_sdf_icon.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/symbol_sdf_text.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/programs/gl/symbol_text_and_icon.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_location_indicator_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/render_location_indicator_layer.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/location_indicator_layer_properties.hpp + ) +endif() + +if(EXISTS ${PROJECT_SOURCE_DIR}/.git/HEAD) + execute_process( + COMMAND + git + rev-parse + --short=8 + HEAD + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + OUTPUT_VARIABLE MBGL_VERSION_REV + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +else() + set(MBGL_VERSION_REV 00000000) +endif() + +set_source_files_properties( + ${PROJECT_SOURCE_DIR}/src/mbgl/util/version.cpp + PROPERTIES + COMPILE_DEFINITIONS + MBGL_VERSION_REV="${MBGL_VERSION_REV}" +) + +target_include_directories( + mbgl-core + PRIVATE ${PROJECT_SOURCE_DIR}/src +) + +target_include_directories( + mbgl-core + PUBLIC ${PROJECT_SOURCE_DIR}/include +) + +include(${PROJECT_SOURCE_DIR}/vendor/boost.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/csscolorparser.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/earcut.hpp.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/eternal.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/mapbox-base.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/parsedate.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/polylabel.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/protozero.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/unique_resource.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/vector-tile.cmake) +include(${PROJECT_SOURCE_DIR}/vendor/wagyu.cmake) + +target_link_libraries( + mbgl-core + PRIVATE + Mapbox::Base::Extras::kdbush.hpp + Mapbox::Base::supercluster.hpp + Mapbox::Base::shelf-pack-cpp + Mapbox::Base::geojson-vt-cpp + Mapbox::Base::cheap-ruler-cpp + mbgl-compiler-options + mbgl-vendor-boost + mbgl-vendor-csscolorparser + mbgl-vendor-earcut.hpp + mbgl-vendor-eternal + mbgl-vendor-parsedate + mbgl-vendor-polylabel + mbgl-vendor-protozero + mbgl-vendor-unique_resource + mbgl-vendor-vector-tile + mbgl-vendor-wagyu + PUBLIC + Mapbox::Base + Mapbox::Base::Extras::expected-lite + Mapbox::Base::Extras::rapidjson + Mapbox::Base::geojson.hpp + Mapbox::Base::geometry.hpp + Mapbox::Base::optional + Mapbox::Base::variant +) + +set_target_properties( + mbgl-core + PROPERTIES + INTERFACE_MAPBOX_NAME "Mapbox GL Native" + INTERFACE_MAPBOX_URL "https://github.com/mapbox/mapbox-gl-native" + INTERFACE_MAPBOX_AUTHOR "Mapbox" + INTERFACE_MAPBOX_LICENSE ${PROJECT_SOURCE_DIR}/LICENSE.md +) + +set_property(TARGET mbgl-core PROPERTY FOLDER Core) + +add_library( + Mapbox::Map ALIAS mbgl-core +) + +if(MBGL_WITH_PLATFORM_EXTRAS) + include(${MBGL_WITH_PLATFORM_EXTRAS}) +endif() + +if(MBGL_WITH_CORE_ONLY) + return() +endif() + +include(${PROJECT_SOURCE_DIR}/scripts/license.cmake) + +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/internal/internal.cmake) + set(MBGL_PUBLIC_BUILD TRUE) +endif() + +if(MBGL_WITH_QT) + include(${PROJECT_SOURCE_DIR}/platform/qt/qt.cmake) +elseif(CMAKE_SYSTEM_NAME STREQUAL Android) + include(${PROJECT_SOURCE_DIR}/platform/android/android.cmake) +elseif(CMAKE_SYSTEM_NAME STREQUAL iOS) + include(${PROJECT_SOURCE_DIR}/platform/ios/ios.cmake) +elseif(CMAKE_SYSTEM_NAME STREQUAL Linux) + include(${PROJECT_SOURCE_DIR}/platform/linux/linux.cmake) +elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin) + include(${PROJECT_SOURCE_DIR}/platform/macos/macos.cmake) +else() + message(FATAL_ERROR "Unsupported target platform: " ${CMAKE_SYSTEM_NAME}) +endif() + +add_subdirectory(${PROJECT_SOURCE_DIR}/test) +add_subdirectory(${PROJECT_SOURCE_DIR}/benchmark) +add_subdirectory(${PROJECT_SOURCE_DIR}/render-test) + +if(EXISTS ${PROJECT_SOURCE_DIR}/internal/internal.cmake) + include(${PROJECT_SOURCE_DIR}/internal/internal.cmake) +endif() diff --git a/mapbox-gl-native/CODE-OF-CONDUCT.md b/mapbox-gl-native/CODE-OF-CONDUCT.md new file mode 100644 index 000000000..7fcca0c82 --- /dev/null +++ b/mapbox-gl-native/CODE-OF-CONDUCT.md @@ -0,0 +1,5 @@ +# Code of conduct + +Everyone is invited to participate in Mapbox’s open source projects and public discussions: we want to create a welcoming and friendly environment. Harassment of participants or other unethical and unprofessional behavior will not be tolerated in our spaces. The [Contributor Covenant](http://contributor-covenant.org) applies to all projects under the Mapbox organization and we ask that you please read [the full text](http://contributor-covenant.org/version/1/2/0/). + +You can learn more about our open source philosophy on [mapbox.com](https://www.mapbox.com/about/open/). diff --git a/mapbox-gl-native/CONTRIBUTING.md b/mapbox-gl-native/CONTRIBUTING.md new file mode 100644 index 000000000..d9b1f03f8 --- /dev/null +++ b/mapbox-gl-native/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# Contributing + +If you have a usage question for a product built on Mapbox GL (such as our iOS or Android SDKs), please visit https://www.mapbox.com/help/. + +If you want to contribute code: + +1. Please familiarize yourself with the [install process](INSTALL.md). + +1. Ensure that existing [pull requests](https://github.com/mapbox/mapbox-gl-native/pulls) and [issues](https://github.com/mapbox/mapbox-gl-native/issues) don’t already cover your contribution or question. + +1. Pull requests are gladly accepted. If there are any changes that developers using one of the GL SDKs should be aware of, please update the **master** section of the relevant changelog(s): + * [Mapbox Maps SDK for Android](platform/android/CHANGELOG.md) + * [Mapbox Maps SDK for iOS](platform/ios/CHANGELOG.md) + * [Mapbox Maps SDK for macOS](platform/macos/CHANGELOG.md) + * [node-mapbox-gl-native](platform/node/CHANGELOG.md) + +1. Prefix your commit messages with the platform(s) your changes affect: `[core]`, `[android]`, `[ios]`, `[macos]`, `[node]`, or `[qt]`. + +Please note the special instructions for contributing new source code files, asset files, or user-facing strings to the [iOS SDK](platform/ios/DEVELOPING.md#contributing) or [macOS SDK](platform/macos/DEVELOPING.md#contributing). + +### Github issue labels + +Our labeling system is: + + * **minimalistic:** Labels’ usefulness are inversely proportional to how many we have. + * **objective:** Labels should be objective enough that any two people would agree on a labeling decision. + * **useful:** Labels should track state or capture semantic meaning that would otherwise be hard to search for. + +We’ve color-coded our labels by facet to make them easier to use: + + * type (blue) + * platform (black) + * actionable status (red) + * non-actionable status (grey) + * importance / urgency (green) + * topic / project / misc (yellow) diff --git a/mapbox-gl-native/DEVELOPING.md b/mapbox-gl-native/DEVELOPING.md new file mode 100644 index 000000000..3dea88712 --- /dev/null +++ b/mapbox-gl-native/DEVELOPING.md @@ -0,0 +1,11 @@ +# Developing + +This is been rewritten to include: + +- How to build for the platforms we support. +- Acceptance criteria for code contributions (style, static asserts) +- How to run the unit tests. +- How to run the benchmarks. +- How to run the render tests. +- How to rebaseline baselines metrics. +- How to use GL Native as a 3rd party library in your project. diff --git a/mapbox-gl-native/LICENSE.mbgl-core.md b/mapbox-gl-native/LICENSE.mbgl-core.md new file mode 100644 index 000000000..66c94e08d --- /dev/null +++ b/mapbox-gl-native/LICENSE.mbgl-core.md @@ -0,0 +1,661 @@ +### [Mapbox GL Native](https://github.com/mapbox/mapbox-gl-native) by Mapbox + +``` +mapbox-gl-native Copyright (c) 2014-2020 Mapbox. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR +PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +--- + +### [kdbush.hpp](https://github.com/mourner/kdbush.hpp) by Vladimir Agafonkin + +``` +Copyright (c) 2016, Vladimir Agafonkin + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + +--- + +### [supercluster.hpp](https://github.com/mapbox/supercluster.hpp) by Mapbox + +``` +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + +--- + +### [shelf-pack-cpp](https://github.com/mapbox/shelf-pack-cpp) by Mapbox + +``` +ISC License + +Copyright (c) 2017, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +``` + +--- + +### [geojson-vt-cpp](https://github.com/mapbox/geojson-vt-cpp) by Mapbox + +``` +ISC License + +Copyright (c) 2015, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +``` + +--- + +### [cheap-ruler-cpp](https://github.com/mapbox/cheap-ruler-cpp) by Mapbox + +``` +ISC License + +Copyright (c) 2017, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +``` + +--- + +### [Boost C++ Libraries](https://www.boost.org) by Boost authors + +``` +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +--- + +### [csscolorparser](https://github.com/mapbox/css-color-parser-cpp) by Dean McNamee and Konstantin Käfer + +``` +(c) Dean McNamee , 2012. +(c) Konstantin Käfer , 2014. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +``` + +--- + +### [earcut.hpp](https://github.com/mapbox/earcut.hpp) by Mapbox + +``` +ISC License + +Copyright (c) 2015, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +``` + +--- + +### [eternal](https://github.com/mapbox/eternal) by Mapbox + +``` +ISC License + +Copyright (c) 2018, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +``` + +--- + +### [parsedate](https://curl.haxx.se) by Daniel Stenberg and others + +``` +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2020, Daniel Stenberg, , and many +contributors, see the THANKS file. + +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. + +``` + +--- + +### [polylabel](https://github.com/mapbox/polylabel) by Mapbox + +``` +ISC License +Copyright (c) 2016 Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA +OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +``` + +--- + +### [protozero](https://github.com/mapbox/protozero) by Mapbox + +``` +protozero copyright (c) Mapbox. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR +PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +--- + +### [unique_resource](https://github.com/okdshin/unique_resource) by Shintarou Okada + +``` +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +--- + +### [vector-tile](https://github.com/mapbox/vector-tile) by Mapbox + +``` +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + +--- + +### [wagyu](https://github.com/mapbox/wagyu.git) by Angus Johnson and Mapbox + +``` +Parts of the code in the Wagyu Library are derived from the version of the +Clipper Library by Angus Johnson listed below. + +Author : Angus Johnson +Version : 6.4.0 +Date : 2 July 2015 +Website : http://www.angusj.com + +Copyright for portions of the derived code in the Wagyu library are held +by Angus Johnson, 2010-2015. All other copyright for the Wagyu Library are held by +Mapbox, 2016. This code is published in accordance with, and retains the same license +as the Clipper Library by Angus Johnson. + +Copyright (c) 2010-2015, Angus Johnson +Copyright (c) 2016, Mapbox + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +--- + +### [mapbox-base](https://github.com/mapbox/mapbox-base) by Mapbox + +``` +Copyright (c) MapBox +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +- Neither the name "MapBox" nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES +LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` + +--- + +### [expected-lite](https://github.com/martinmoene/expected-lite) by Martin Moene + +``` +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +--- + +### [RapidJSON](https://rapidjson.org) by THL A29 Limited, a Tencent company, and Milo Yip + +``` +Tencent is pleased to support the open source community by making RapidJSON available. + +Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. + +If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License. +If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. To avoid the problematic JSON license in your own projects, it's sufficient to exclude the bin/jsonchecker/ directory, as it's the only code under the JSON license. +A copy of the MIT License is included in this file. + +Other dependencies and licenses: + +Open Source Software Licensed Under the BSD License: +-------------------------------------------------------------------- + +The msinttypes r29 +Copyright (c) 2006-2013 Alexander Chemeris +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Open Source Software Licensed Under the JSON License: +-------------------------------------------------------------------- + +json.org +Copyright (c) 2002 JSON.org +All Rights Reserved. + +JSON_checker +Copyright (c) 2002 JSON.org +All Rights Reserved. + + +Terms of the JSON License: +--------------------------------------------------- + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Terms of the MIT License: +-------------------------------------------------------------------- + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +--- + +### [geojson.hpp](https://github.com/mapbox/geojson-cpp) by Mapbox + +``` +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + +--- + +### [geometry.hpp](https://github.com/mapbox/geometry.hpp) by Mapbox + +``` +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +``` + +--- + +### [Optional](https://github.com/akrzemi1/Optional) by Andrzej Krzemienski + +``` +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +--- + +### [variant](https://github.com/mapbox/variant) by Mapbox + +``` +Copyright (c) MapBox +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +- Neither the name "MapBox" nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES +LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` + +--- + diff --git a/mapbox-gl-native/LICENSE.md b/mapbox-gl-native/LICENSE.md new file mode 100644 index 000000000..96fd0919d --- /dev/null +++ b/mapbox-gl-native/LICENSE.md @@ -0,0 +1,24 @@ +mapbox-gl-native Copyright (c) 2014-2020 Mapbox. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mapbox-gl-native/README.md b/mapbox-gl-native/README.md new file mode 100644 index 000000000..ab256f46a --- /dev/null +++ b/mapbox-gl-native/README.md @@ -0,0 +1,34 @@ +# Mapbox GL Native + +[![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) [![Coverage Status](https://codecov.io/gh/mapbox/mapbox-gl-native/branch/master/graph/badge.svg)](https://codecov.io/gh/mapbox/mapbox-gl-native) + +A C++ library that powers customizable vector maps in native applications on multiple platforms by taking stylesheets that conform to the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/), applying them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://github.com/mapbox/vector-tile-spec/), and rendering them using OpenGL or Metal. + +To embed interactive maps into a native application using a platform-specific language, install the Mapbox Maps SDK: + +* [Mapbox Maps SDK for Android](https://github.com/mapbox/mapbox-gl-native-android/) (Java, Kotlin) +* [Mapbox Maps SDK for iOS](https://github.com/mapbox/mapbox-gl-native-ios/blob/master/platform/ios/) (Objective-C, Swift, Interface Builder) +* [Mapbox Maps SDK for macOS](https://github.com/mapbox/mapbox-gl-native-ios/tree/master/platform/macos/) (Objective-C, Swift, Interface Builder, AppleScript) + +[Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) is the WebGL-based counterpart to Mapbox GL Native that is designed for use on the Web. + +## [Developing](DEVELOPING.md) + +We use [CMake](https://cmake.org/cmake/help/latest/) to build Mapbox GL Native +for various platforms, including Linux, Android, iOS, macOS and Windows. The +following command, executed from the root of this repository tree, will build +Mapbox GL Native targeting your host architecture given that you have all the +dependencies installed and run the example app. + +``` +$ git submodule update --init --recursive +$ cmake . -B build +$ cmake --build build +$ MAPBOX_ACCESS_TOKEN=my_access_token_here ./build/platform/glfw/mbgl-glfw +``` + +## License + +Mapbox GL Native is licensed under the [2-Clause BSD license](LICENSE.md). The licenses of its dependencies are tracked via [FOSSA](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fmapbox-gl-native): + +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fmapbox-gl-native.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fmapbox-gl-native) diff --git a/mapbox-gl-native/appveyor.yml b/mapbox-gl-native/appveyor.yml new file mode 100644 index 000000000..16e712709 --- /dev/null +++ b/mapbox-gl-native/appveyor.yml @@ -0,0 +1,88 @@ +platform: x64 + +image: +- Visual Studio 2015 +- Visual Studio 2017 + +environment: + PYTHON: C:\python37 + + matrix: + - configuration: Release + - configuration: Debug + +test: off + +clone_depth: 1 + +for: +- + matrix: + only: + - image: Visual Studio 2015 + + environment: + LLVM_VERSION: 5.0.1 + LLVM_HASH: 981543611D719624ACB29A2CFFD6A479CFF36E8AB5EE8A57D8ECA4F9C4C6956F + VCVARSALL: 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat' + QT_PREFIX: 'C:\Qt\latest\msvc2015_64\lib\cmake' + +- + matrix: + only: + - image: Visual Studio 2017 + + environment: + LLVM_VERSION: 7.0.0 + LLVM_HASH: 74b197a3959b0408adf0824be01db8dddfa2f9a967f4085af3fad900ed5fdbf6 + VCVARSALL: 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat' + QT_PREFIX: 'C:\Qt\latest\msvc2017_64\lib\cmake' + +cache: + - '%APPVEYOR_BUILD_FOLDER%\.git\modules' + - '%APPVEYOR_BUILD_FOLDER%\LLVM-%LLVM_VERSION%-win64.exe' + - '%APPVEYOR_BUILD_FOLDER%\cmake-3.10.1-win64-x64.zip' + - C:\clcache + +install: + - set PATH=%PYTHON%\Scripts;%PYTHON%;%PATH% + - pip install clcache + - git config --system core.longpaths true + - git submodule sync + - git submodule update --init + - git submodule foreach git submodule update --init + - ps: | + if (!(Test-Path "LLVM-$env:LLVM_VERSION-win64.exe")) { + appveyor DownloadFile "https://releases.llvm.org/$env:LLVM_VERSION/LLVM-$env:LLVM_VERSION-win64.exe" + } + scripts\check-sha256.ps1 LLVM-$env:LLVM_VERSION-win64.exe "$env:LLVM_HASH" + Start-Process -FilePath "LLVM-$env:LLVM_VERSION-win64.exe" -ArgumentList '/S',"/D=C:\LLVM-$env:LLVM_VERSION" -Wait + - ps: | + if (!(Test-Path cmake-3.10.1-win64-x64.zip)) { + appveyor DownloadFile https://cmake.org/files/v3.10/cmake-3.10.1-win64-x64.zip + } + scripts\check-sha256.ps1 cmake-3.10.1-win64-x64.zip 8251F70C85B58F3CA1F24E4A3B0637E2D609B5E4A341D00B70E02E89244D5029 + Start-Process -FilePath '7z' -ArgumentList 'x','cmake-3.10.1-win64-x64.zip','-oC:\' -Wait + +before_build: + - set PATH=C:\LLVM-%LLVM_VERSION%\bin;%PATH% + - set PATH=C:\cmake-3.10.1-win64-x64\bin;%PATH% + - set CLCACHE_DIR=c:\clcache + - set CLCACHE_CL=clang-cl + - clcache -z + - set CC=clcache + - set CXX=clcache + - set CTEST_OUTPUT_ON_FAILURE=1 + - mkdir %APPVEYOR_BUILD_FOLDER%\build + - cd %APPVEYOR_BUILD_FOLDER%\build + +build_script: + - call "%VCVARSALL%" amd64 + - cmake -G "Ninja" + -DCMAKE_BUILD_TYPE=%CONFIGURATION% + -DCMAKE_MAKE_PROGRAM="%APPVEYOR_BUILD_FOLDER%\platform\qt\ninja.exe" + -DCMAKE_PREFIX_PATH=%QT_PREFIX% + -DMBGL_WITH_QT=ON + .. + - cmake --build . --target qmapboxgl -- -j %NUMBER_OF_PROCESSORS% + - clcache -s diff --git a/mapbox-gl-native/benchmark/CMakeLists.txt b/mapbox-gl-native/benchmark/CMakeLists.txt new file mode 100644 index 000000000..ab21fe330 --- /dev/null +++ b/mapbox-gl-native/benchmark/CMakeLists.txt @@ -0,0 +1,50 @@ +add_library( + mbgl-benchmark STATIC EXCLUDE_FROM_ALL + ${PROJECT_SOURCE_DIR}/benchmark/api/query.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/api/render.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/function/camera_function.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/function/composite_function.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/function/source_function.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/parse/filter.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/parse/tile_mask.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/parse/vector_tile.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/src/mbgl/benchmark/benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/storage/offline_database.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/util/dtoa.benchmark.cpp + ${PROJECT_SOURCE_DIR}/benchmark/util/tilecover.benchmark.cpp +) + +target_include_directories( + mbgl-benchmark + PRIVATE ${PROJECT_SOURCE_DIR}/benchmark/src ${PROJECT_SOURCE_DIR}/platform/default/include ${PROJECT_SOURCE_DIR}/src +) + +target_include_directories( + mbgl-benchmark + PUBLIC ${PROJECT_SOURCE_DIR}/benchmark/include ${PROJECT_SOURCE_DIR}/include +) + +include(${PROJECT_SOURCE_DIR}/vendor/benchmark.cmake) + +if(CMAKE_SYSTEM_NAME STREQUAL iOS) + set_target_properties(mbgl-vendor-benchmark PROPERTIES XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "${IOS_DEPLOYMENT_TARGET}") + set_target_properties(mbgl-vendor-benchmark PROPERTIES XCODE_ATTRIBUTE_ENABLE_BITCODE "YES") + set_target_properties(mbgl-vendor-benchmark PROPERTIES XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE bitcode) + set_target_properties(mbgl-vendor-benchmark PROPERTIES XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH $<$:YES>) + + set_target_properties(mbgl-benchmark PROPERTIES XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "${IOS_DEPLOYMENT_TARGET}") + set_target_properties(mbgl-benchmark PROPERTIES XCODE_ATTRIBUTE_ENABLE_BITCODE "YES") + set_target_properties(mbgl-benchmark PROPERTIES XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE bitcode) + set_target_properties(mbgl-benchmark PROPERTIES XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH $<$:YES>) +endif() + +# Needed for testing private classes +get_target_property(MBGL_CORE_PRIVATE_LIBRARIES mbgl-core LINK_LIBRARIES) + +target_link_libraries( + mbgl-benchmark + PRIVATE ${MBGL_CORE_PRIVATE_LIBRARIES} mbgl-vendor-benchmark mbgl-compiler-options + PUBLIC mbgl-core +) + +set_property(TARGET mbgl-benchmark PROPERTY FOLDER Core) diff --git a/mapbox-gl-native/benchmark/android/README.md b/mapbox-gl-native/benchmark/android/README.md new file mode 100644 index 000000000..fb809d3da --- /dev/null +++ b/mapbox-gl-native/benchmark/android/README.md @@ -0,0 +1,3 @@ +# BenchmarkRunner + +This app is a purely native application that can run **mbgl-benchmark-runner** on Android devices. diff --git a/mapbox-gl-native/benchmark/android/app/build.gradle b/mapbox-gl-native/benchmark/android/app/build.gradle new file mode 100644 index 000000000..d33d19d66 --- /dev/null +++ b/mapbox-gl-native/benchmark/android/app/build.gradle @@ -0,0 +1,52 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + + defaultConfig { + applicationId = 'com.mapbox.mapboxsdk.maps.benchmark_runner' + minSdkVersion 14 + targetSdkVersion 28 + def abi = 'all' + if (project.hasProperty('mapbox.abis')) { + abi = project.getProperty('mapbox.abis') + } + ndk { + if (abi != 'all') { + abiFilters abi.split(' ') + } else { + abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64' + } + } + externalNativeBuild { + cmake { + arguments '-DANDROID_CCACHE=ccache' + arguments '-DANDROID_STL=c++_static' + targets 'mbgl-benchmark-runner' + } + } + android { + testBuildType 'release' + } + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + } + externalNativeBuild { + cmake { + version '3.10.2' + path '../../../CMakeLists.txt' + } + } + buildTypes { + release { + signingConfig signingConfigs.debug + } + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + androidTestImplementation 'androidx.test:rules:1.2.0' +} diff --git a/mapbox-gl-native/benchmark/android/app/src/androidTest/java/android/app/NativeActivityTest.java b/mapbox-gl-native/benchmark/android/app/src/androidTest/java/android/app/NativeActivityTest.java new file mode 100644 index 000000000..795ca4973 --- /dev/null +++ b/mapbox-gl-native/benchmark/android/app/src/androidTest/java/android/app/NativeActivityTest.java @@ -0,0 +1,32 @@ +package android.app; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.Assert; +import org.junit.runner.RunWith; +import android.util.Log; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class NativeActivityTest { + + @Rule + public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(NativeActivity.class, false, false); + + @Test(timeout = 1200000L) + public void runBenchmark() throws Exception { + Log.v("Benchmark", "Start the benchmark"); + mActivityTestRule.launchActivity(null); + while (TestState.running) { + Log.v("Benchmark", "Benchmark is running..."); + Thread.sleep(1000L); + } + Log.v("Benchmark", "All benchmarks are finished!"); + mActivityTestRule.finishActivity(); + Assert.assertTrue("Benchmark was successfully finished", TestState.testResult); + } +} diff --git a/mapbox-gl-native/benchmark/android/app/src/androidTest/java/android/app/TestState.java b/mapbox-gl-native/benchmark/android/app/src/androidTest/java/android/app/TestState.java new file mode 100644 index 000000000..b7d4b7354 --- /dev/null +++ b/mapbox-gl-native/benchmark/android/app/src/androidTest/java/android/app/TestState.java @@ -0,0 +1,6 @@ +package android.app; + +public class TestState { + static boolean running = true; + static boolean testResult = true; +} diff --git a/mapbox-gl-native/benchmark/android/app/src/main/AndroidManifest.xml b/mapbox-gl-native/benchmark/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..33cfade1e --- /dev/null +++ b/mapbox-gl-native/benchmark/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/mapbox-gl-native/benchmark/android/app/src/main/assets/to_zip.txt b/mapbox-gl-native/benchmark/android/app/src/main/assets/to_zip.txt new file mode 100644 index 000000000..2fa7604f6 --- /dev/null +++ b/mapbox-gl-native/benchmark/android/app/src/main/assets/to_zip.txt @@ -0,0 +1,3 @@ +benchmark/results/ +benchmark/fixtures/ +test/fixtures/api/assets/streets/ diff --git a/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..cde69bccc Binary files /dev/null and b/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..c133a0cbd Binary files /dev/null and b/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..bfa42f0e7 Binary files /dev/null and b/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..324e72cdd Binary files /dev/null and b/mapbox-gl-native/benchmark/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/mapbox-gl-native/benchmark/android/app/src/main/res/values/strings.xml b/mapbox-gl-native/benchmark/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..39be3fc37 --- /dev/null +++ b/mapbox-gl-native/benchmark/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + BenchmarkRunner + diff --git a/mapbox-gl-native/benchmark/android/build.gradle b/mapbox-gl-native/benchmark/android/build.gradle new file mode 100644 index 000000000..de7242684 --- /dev/null +++ b/mapbox-gl-native/benchmark/android/build.gradle @@ -0,0 +1,21 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.5.3' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/mapbox-gl-native/benchmark/android/gradle.properties b/mapbox-gl-native/benchmark/android/gradle.properties new file mode 100644 index 000000000..6ed0f8f96 --- /dev/null +++ b/mapbox-gl-native/benchmark/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536m diff --git a/mapbox-gl-native/benchmark/android/gradle/wrapper/gradle-wrapper.jar b/mapbox-gl-native/benchmark/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..8c0fb64a8 Binary files /dev/null and b/mapbox-gl-native/benchmark/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/mapbox-gl-native/benchmark/android/gradle/wrapper/gradle-wrapper.properties b/mapbox-gl-native/benchmark/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..48c45760f --- /dev/null +++ b/mapbox-gl-native/benchmark/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Feb 05 19:39:12 IST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/mapbox-gl-native/benchmark/android/gradlew b/mapbox-gl-native/benchmark/android/gradlew new file mode 100755 index 000000000..91a7e269e --- /dev/null +++ b/mapbox-gl-native/benchmark/android/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/mapbox-gl-native/benchmark/android/gradlew.bat b/mapbox-gl-native/benchmark/android/gradlew.bat new file mode 100644 index 000000000..aec99730b --- /dev/null +++ b/mapbox-gl-native/benchmark/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mapbox-gl-native/benchmark/android/settings.gradle b/mapbox-gl-native/benchmark/android/settings.gradle new file mode 100644 index 000000000..573abcb32 --- /dev/null +++ b/mapbox-gl-native/benchmark/android/settings.gradle @@ -0,0 +1,2 @@ +include ':app' + diff --git a/mapbox-gl-native/benchmark/api/query.benchmark.cpp b/mapbox-gl-native/benchmark/api/query.benchmark.cpp new file mode 100644 index 000000000..714ca8a1a --- /dev/null +++ b/mapbox-gl-native/benchmark/api/query.benchmark.cpp @@ -0,0 +1,92 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace mbgl; + +namespace { + +class QueryBenchmark { +public: + QueryBenchmark() { + NetworkStatus::Set(NetworkStatus::Status::Offline); + + map.getStyle().loadJSON(util::read_file("benchmark/fixtures/api/style.json")); + map.jumpTo(CameraOptions().withCenter(LatLng { 40.726989, -73.992857 }).withZoom(15.0)); // Manhattan + map.getStyle().addImage(std::make_unique("test-icon", + decodeImage(util::read_file("benchmark/fixtures/api/default_marker.png")), 1.0)); + + frontend.render(map); + } + + util::RunLoop loop; + HeadlessFrontend frontend { { 1000, 1000 }, 1 }; + Map map { frontend, MapObserver::nullObserver(), + MapOptions().withMapMode(MapMode::Static).withSize(frontend.getSize()), + ResourceOptions().withCachePath("benchmark/fixtures/api/cache.db").withAssetPath(".").withAccessToken("foobar") }; + ScreenBox box{{ 0, 0 }, { 1000, 1000 }}; +}; + +} // end namespace + +static void API_queryPixelsForLatLngs(::benchmark::State& state) { + std::vector points; + int count = 10000; + for (int i = 0; i < count; ++i) { + points.emplace_back(1, 1); + } + QueryBenchmark bench; + while (state.KeepRunning()) { + (void)bench.map.pixelsForLatLngs(points); + } +} + +static void API_queryLatLngsForPixels(::benchmark::State& state) { + std::vector points; + int count = 10000; + for (int i = 0; i < count; ++i) { + points.emplace_back(1, 1); + } + QueryBenchmark bench; + while (state.KeepRunning()) { + (void)bench.map.latLngsForPixels(points); + } +} + +static void API_queryRenderedFeaturesAll(::benchmark::State& state) { + QueryBenchmark bench; + + while (state.KeepRunning()) { + bench.frontend.getRenderer()->queryRenderedFeatures(bench.box, {}); + } +} + +static void API_queryRenderedFeaturesLayerFromLowDensity(::benchmark::State& state) { + QueryBenchmark bench; + + while (state.KeepRunning()) { + bench.frontend.getRenderer()->queryRenderedFeatures(bench.box, {{{ "testlayer" }}, {}}); + } +} + +static void API_queryRenderedFeaturesLayerFromHighDensity(::benchmark::State& state) { + QueryBenchmark bench; + + while (state.KeepRunning()) { + bench.frontend.getRenderer()->queryRenderedFeatures(bench.box, {{{"road-street" }}, {}}); + } +} +BENCHMARK(API_queryPixelsForLatLngs); +BENCHMARK(API_queryLatLngsForPixels); +BENCHMARK(API_queryRenderedFeaturesAll)->Iterations(50); +BENCHMARK(API_queryRenderedFeaturesLayerFromLowDensity); +BENCHMARK(API_queryRenderedFeaturesLayerFromHighDensity); diff --git a/mapbox-gl-native/benchmark/api/render.benchmark.cpp b/mapbox-gl-native/benchmark/api/render.benchmark.cpp new file mode 100644 index 000000000..14cfdd67f --- /dev/null +++ b/mapbox-gl-native/benchmark/api/render.benchmark.cpp @@ -0,0 +1,159 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace mbgl; + +namespace { + +static std::string cachePath { "benchmark/fixtures/api/cache.db" }; +constexpr double pixelRatio { 1.0 }; +constexpr Size size { 1000, 1000 }; + +class RenderBenchmark { +public: + RenderBenchmark() { + NetworkStatus::Set(NetworkStatus::Status::Offline); + } + + util::RunLoop loop; +}; + +void prepare(Map& map, optional json = nullopt) { + map.getStyle().loadJSON(json ? *json : util::read_file("benchmark/fixtures/api/style.json")); + map.jumpTo(CameraOptions().withCenter(LatLng { 40.726989, -73.992857 }).withZoom(15.0)); // Manhattan + + auto image = decodeImage(util::read_file("benchmark/fixtures/api/default_marker.png")); + map.getStyle().addImage(std::make_unique("test-icon", std::move(image), 1.0)); +} + +void prepare_map2(Map& map, optional json = nullopt) { + map.getStyle().loadJSON(json ? *json : util::read_file("benchmark/fixtures/api/style.json")); + map.jumpTo(CameraOptions().withCenter(LatLng{41.379800, 2.176810}).withZoom(15.0)); // Barcelona + + auto image = decodeImage(util::read_file("benchmark/fixtures/api/default_marker.png")); + map.getStyle().addImage(std::make_unique("test-icon", std::move(image), 1.0)); +} + +} // end namespace + +static void API_renderStill_reuse_map(::benchmark::State& state) { + RenderBenchmark bench; + HeadlessFrontend frontend { size, pixelRatio }; + Map map { frontend, MapObserver::nullObserver(), + MapOptions().withMapMode(MapMode::Static).withSize(size).withPixelRatio(pixelRatio), + ResourceOptions().withCachePath(cachePath).withAccessToken("foobar") }; + prepare(map); + + for (auto _ : state) { + frontend.render(map); + } +} + +static void API_renderStill_reuse_map_formatted_labels(::benchmark::State& state) { + RenderBenchmark bench; + HeadlessFrontend frontend { size, pixelRatio }; + Map map { frontend, MapObserver::nullObserver(), + MapOptions().withMapMode(MapMode::Static).withSize(size).withPixelRatio(pixelRatio), + ResourceOptions().withCachePath(cachePath).withAccessToken("foobar") }; + prepare(map, util::read_file("benchmark/fixtures/api/style_formatted_labels.json")); + + for (auto _ : state) { + frontend.render(map); + } +} + +static void API_renderStill_reuse_map_switch_styles(::benchmark::State& state) { + RenderBenchmark bench; + HeadlessFrontend frontend { size, pixelRatio }; + Map map { frontend, MapObserver::nullObserver(), + MapOptions().withMapMode(MapMode::Static).withSize(size).withPixelRatio(pixelRatio), + ResourceOptions().withCachePath(cachePath).withAccessToken("foobar") }; + + for (auto _ : state) { + prepare(map, { "{}" }); + frontend.render(map); + prepare(map); + frontend.render(map); + } +} + +static void API_renderStill_recreate_map(::benchmark::State& state) { + RenderBenchmark bench; + + for (auto _ : state) { + HeadlessFrontend frontend { size, pixelRatio }; + Map map { frontend, MapObserver::nullObserver(), + MapOptions().withMapMode(MapMode::Static).withSize(size).withPixelRatio(pixelRatio), + ResourceOptions().withCachePath(cachePath).withAccessToken("foobar") }; + prepare(map); + frontend.render(map); + } +} + +static void API_renderStill_recreate_map_2(::benchmark::State& state) { + RenderBenchmark bench; + + for (auto _ : state) { + HeadlessFrontend frontend{size, pixelRatio}; + Map map{frontend, + MapObserver::nullObserver(), + MapOptions().withMapMode(MapMode::Static).withSize(size).withPixelRatio(pixelRatio), + ResourceOptions().withCachePath(cachePath).withAccessToken("foobar")}; + prepare_map2(map); + frontend.render(map); + } +} + +static void API_renderStill_multiple_sources(::benchmark::State& state) { + using namespace mbgl::style; + RenderBenchmark bench; + HeadlessFrontend frontend{size, pixelRatio}; + Map map{frontend, + MapObserver::nullObserver(), + MapOptions().withMapMode(MapMode::Static).withSize(size).withPixelRatio(pixelRatio), + ResourceOptions().withCachePath(cachePath).withAccessToken("foobar")}; + prepare(map); + auto& style = map.getStyle(); + const int kSourcesCount = 50; + const int kLayersCount = 50; + for (int i = 0; i < kSourcesCount; ++i) { + std::ostringstream sourceOss; + sourceOss << "GeoJSONSource" << i; + std::string sourceId{sourceOss.str()}; + auto source = std::make_unique(sourceId); + style.addSource(std::move(source)); + for (int j = 0; j < kLayersCount; ++j) { + std::ostringstream layerOss; + layerOss << sourceId << '#' << j; + auto layer = std::make_unique(layerOss.str(), sourceId); + style.addLayer(std::move(layer)); + } + } + + for (auto _ : state) { + frontend.render(map); + } +} + +BENCHMARK(API_renderStill_reuse_map)->Unit(benchmark::kMillisecond)->Iterations(50); +BENCHMARK(API_renderStill_reuse_map_formatted_labels)->Unit(benchmark::kMillisecond)->Iterations(50); +BENCHMARK(API_renderStill_reuse_map_switch_styles)->Unit(benchmark::kMillisecond)->Iterations(50); +BENCHMARK(API_renderStill_recreate_map)->Unit(benchmark::kMillisecond)->Iterations(50); +BENCHMARK(API_renderStill_recreate_map_2)->Unit(benchmark::kMillisecond)->Iterations(50); +BENCHMARK(API_renderStill_multiple_sources)->Unit(benchmark::kMillisecond)->Iterations(50); diff --git a/mapbox-gl-native/benchmark/fixtures/api/cache.db b/mapbox-gl-native/benchmark/fixtures/api/cache.db new file mode 100644 index 000000000..7043eb6fd Binary files /dev/null and b/mapbox-gl-native/benchmark/fixtures/api/cache.db differ diff --git a/mapbox-gl-native/benchmark/fixtures/api/default_marker.png b/mapbox-gl-native/benchmark/fixtures/api/default_marker.png new file mode 100644 index 000000000..b112096c1 Binary files /dev/null and b/mapbox-gl-native/benchmark/fixtures/api/default_marker.png differ diff --git a/mapbox-gl-native/benchmark/fixtures/api/style.json b/mapbox-gl-native/benchmark/fixtures/api/style.json new file mode 100644 index 000000000..cf88aeb6e --- /dev/null +++ b/mapbox-gl-native/benchmark/fixtures/api/style.json @@ -0,0 +1,3993 @@ +{ + "version": 8, + "name": "Mapbox Streets", + "metadata": { + "mapbox:autocomposite": true, + "mapbox:type": "default", + "mapbox:groups": { + "1444934828655.3389": { "name": "Aeroways", "collapsed": true }, + "1444933322393.2852": { "name": "POI labels (scalerank 1)", "collapsed": true }, + "1444855786460.0557": { "name": "Roads", "collapsed": true }, + "1444933575858.6992": { "name": "Highway shields", "collapsed": true }, + "1444934295202.7542": { "name": "Admin boundaries", "collapsed": true }, + "1444856151690.9143": { "name": "State labels", "collapsed": true }, + "1444933721429.3076": { "name": "Road labels", "collapsed": true }, + "1444933358918.2366": { "name": "POI labels (scalerank 2)", "collapsed": true }, + "1444933808272.805": { "name": "Water labels", "collapsed": true }, + "1444933372896.5967": { "name": "POI labels (scalerank 3)", "collapsed": true }, + "1444855799204.86": { "name": "Bridges", "collapsed": true }, + "1444856087950.3635": { "name": "Marine labels", "collapsed": true }, + "1456969573402.7817": { "name": "Hillshading", "collapsed": true }, + "1444862510685.128": { "name": "City labels", "collapsed": true }, + "1444855769305.6016": { "name": "Tunnels", "collapsed": true }, + "1456970288113.8113": { "name": "Landcover", "collapsed": true }, + "1444856144497.7825": { "name": "Country labels", "collapsed": true }, + "1444933456003.5437": { "name": "POI labels (scalerank 4)", "collapsed": true } + } + }, + "sources": { + "composite": { "url": "mapbox://mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7", "type": "vector" } + }, + "sprite": "mapbox://sprites/mapbox/streets-v10", + "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf", + "visibility": "public", + "layers": [ + { + "id": "background", + "type": "background", + "layout": {}, + "paint": { + "background-color": { "base": 1, "stops": [ [ 11, "hsl(35, 32%, 91%)" ], [ 13, "hsl(35, 12%, 89%)" ] ] } + } + }, + { + "id": "landcover_snow", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "filter": [ "==", "class", "snow" ], + "layout": {}, + "paint": { "fill-color": "hsl(0, 0%, 100%)", "fill-opacity": 0.2, "fill-antialias": false } + }, + { + "id": "landcover_wood", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "maxzoom": 14, + "filter": [ "==", "class", "wood" ], + "layout": {}, + "paint": { + "fill-color": "hsl(75, 62%, 81%)", + "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "landcover_scrub", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "maxzoom": 14, + "filter": [ "==", "class", "scrub" ], + "layout": {}, + "paint": { + "fill-color": "hsl(75, 62%, 81%)", + "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "landcover_grass", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "maxzoom": 14, + "filter": [ "==", "class", "grass" ], + "layout": {}, + "paint": { + "fill-color": "hsl(75, 62%, 81%)", + "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "landcover_crop", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "maxzoom": 14, + "filter": [ "==", "class", "crop" ], + "layout": {}, + "paint": { + "fill-color": "hsl(75, 62%, 81%)", + "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "national_park", + "type": "fill", + "source": "composite", + "source-layer": "landuse_overlay", + "filter": [ "==", "class", "national_park" ], + "layout": {}, + "paint": { + "fill-color": "hsl(100, 58%, 76%)", + "fill-opacity": { "base": 1, "stops": [ [ 5, 0 ], [ 6, 0.5 ] ] } + } + }, + { + "id": "hospital", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "hospital" ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15.5, "hsl(340, 37%, 87%)" ], [ 16, "hsl(340, 63%, 89%)" ] ] } + } + }, + { + "id": "school", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "school" ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15.5, "hsl(50, 47%, 81%)" ], [ 16, "hsl(50, 63%, 84%)" ] ] } + } + }, + { + "id": "park", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "park" ], + "layout": {}, + "paint": { + "fill-color": "hsl(100, 58%, 76%)", + "fill-opacity": { "base": 1, "stops": [ [ 5, 0 ], [ 6, 1 ] ] } + } + }, + { + "id": "pitch", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "pitch" ], + "layout": {}, + "paint": { "fill-color": "hsl(100, 57%, 72%)" } + }, + { + "id": "pitch-line", + "type": "line", + "source": "composite", + "source-layer": "landuse", + "minzoom": 15, + "filter": [ "==", "class", "pitch" ], + "layout": { "line-join": "miter" }, + "paint": { "line-color": "hsl(75, 57%, 84%)" } + }, + { + "id": "cemetery", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "cemetery" ], + "layout": {}, + "paint": { "fill-color": "hsl(75, 37%, 81%)" } + }, + { + "id": "industrial", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "industrial" ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15.5, "hsl(230, 15%, 86%)" ], [ 16, "hsl(230, 29%, 89%)" ] ] } + } + }, + { + "id": "sand", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "sand" ], + "layout": {}, + "paint": { "fill-color": "hsl(60, 46%, 87%)" } + }, + { + "id": "hillshade_highlight_bright", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 94 ], + "layout": {}, + "paint": { + "fill-color": "hsl(0, 0%, 100%)", + "fill-opacity": { "stops": [ [ 14, 0.12 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_highlight_med", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 90 ], + "layout": {}, + "paint": { + "fill-color": "hsl(0, 0%, 100%)", + "fill-opacity": { "stops": [ [ 14, 0.12 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_shadow_faint", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 89 ], + "layout": {}, + "paint": { + "fill-color": "hsl(56, 59%, 22%)", + "fill-opacity": { "stops": [ [ 14, 0.05 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_shadow_med", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 78 ], + "layout": {}, + "paint": { + "fill-color": "hsl(56, 59%, 22%)", + "fill-opacity": { "stops": [ [ 14, 0.05 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_shadow_dark", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 67 ], + "layout": {}, + "paint": { + "fill-color": "hsl(56, 59%, 22%)", + "fill-opacity": { "stops": [ [ 14, 0.06 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_shadow_extreme", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 56 ], + "layout": {}, + "paint": { + "fill-color": "hsl(56, 59%, 22%)", + "fill-opacity": { "stops": [ [ 14, 0.06 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "waterway-river-canal", + "type": "line", + "source": "composite", + "source-layer": "waterway", + "minzoom": 8, + "filter": [ "in", "class", "canal", "river" ], + "layout": { + "line-cap": { "base": 1, "stops": [ [ 0, "butt" ], [ 11, "round" ] ] }, + "line-join": "round" + }, + "paint": { + "line-color": "hsl(205, 87%, 76%)", + "line-width": { "base": 1.3, "stops": [ [ 8.5, 0.1 ], [ 20, 8 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 8, 0 ], [ 8.5, 1 ] ] } + } + }, + { + "id": "waterway-small", + "type": "line", + "source": "composite", + "source-layer": "waterway", + "minzoom": 13, + "filter": [ "!in", "class", "canal", "river" ], + "layout": { "line-join": "round", "line-cap": "round" }, + "paint": { + "line-color": "hsl(205, 87%, 76%)", + "line-width": { "base": 1.35, "stops": [ [ 13.5, 0.1 ], [ 20, 3 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 13, 0 ], [ 13.5, 1 ] ] } + } + }, + { + "id": "water-shadow", + "type": "fill", + "source": "composite", + "source-layer": "water", + "layout": {}, + "paint": { + "fill-color": "hsl(215, 84%, 69%)", + "fill-translate": { "base": 1.2, "stops": [ [ 7, [ 0, 0 ] ], [ 16, [ -1, -1 ] ] ] }, + "fill-translate-anchor": "viewport", + "fill-opacity": 1 + } + }, + { "id": "water", "ref": "water-shadow", "paint": { "fill-color": "hsl(196, 80%, 70%)" } }, + { + "id": "barrier_line-land-polygon", + "type": "fill", + "source": "composite", + "source-layer": "barrier_line", + "filter": [ "all", [ "==", "$type", "Polygon" ], [ "==", "class", "land" ] ], + "layout": {}, + "paint": { "fill-color": "hsl(35, 12%, 89%)" } + }, + { + "id": "barrier_line-land-line", + "type": "line", + "source": "composite", + "source-layer": "barrier_line", + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "class", "land" ] ], + "layout": { "line-cap": "round" }, + "paint": { + "line-width": { "base": 1.99, "stops": [ [ 14, 0.75 ], [ 20, 40 ] ] }, + "line-color": "hsl(35, 12%, 89%)" + } + }, + { + "id": "aeroway-polygon", + "type": "fill", + "metadata": { "mapbox:group": "1444934828655.3389" }, + "source": "composite", + "source-layer": "aeroway", + "minzoom": 11, + "filter": [ "all", [ "==", "$type", "Polygon" ], [ "!=", "type", "apron" ] ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15, "hsl(230, 23%, 82%)" ], [ 16, "hsl(230, 37%, 84%)" ] ] }, + "fill-opacity": { "base": 1, "stops": [ [ 11, 0 ], [ 11.5, 1 ] ] } + } + }, + { + "id": "aeroway-runway", + "type": "line", + "metadata": { "mapbox:group": "1444934828655.3389" }, + "source": "composite", + "source-layer": "aeroway", + "minzoom": 9, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "runway" ] ], + "layout": {}, + "paint": { + "line-color": { "base": 1, "stops": [ [ 15, "hsl(230, 23%, 82%)" ], [ 16, "hsl(230, 37%, 84%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 9, 1 ], [ 18, 80 ] ] } + } + }, + { + "id": "aeroway-taxiway", + "type": "line", + "metadata": { "mapbox:group": "1444934828655.3389" }, + "source": "composite", + "source-layer": "aeroway", + "minzoom": 9, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "taxiway" ] ], + "layout": {}, + "paint": { + "line-color": { "base": 1, "stops": [ [ 15, "hsl(230, 23%, 82%)" ], [ 16, "hsl(230, 37%, 84%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 10, 0.5 ], [ 18, 20 ] ] } + } + }, + { + "id": "building-line", + "type": "line", + "source": "composite", + "source-layer": "building", + "minzoom": 15, + "filter": [ "all", [ "!=", "type", "building:part" ], [ "==", "underground", "false" ] ], + "layout": {}, + "paint": { + "line-color": "hsl(35, 6%, 79%)", + "line-width": { "base": 1.5, "stops": [ [ 15, 0.75 ], [ 20, 3 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 15.5, 0 ], [ 16, 1 ] ] } + } + }, + { + "id": "building", + "type": "fill", + "source": "composite", + "source-layer": "building", + "minzoom": 15, + "filter": [ "all", [ "!=", "type", "building:part" ], [ "==", "underground", "false" ] ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15, "hsl(35, 11%, 88%)" ], [ 16, "hsl(35, 8%, 85%)" ] ] }, + "fill-opacity": { "base": 1, "stops": [ [ 15.5, 0 ], [ 16, 1 ] ] }, + "fill-outline-color": "hsl(35, 6%, 79%)" + } + }, + { + "id": "tunnel-street-low", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "tunnel-street_limited-low", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "tunnel-service-link-track-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "service", "track" ], + [ "==", "structure", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 19%, 75%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-street_limited-case", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 19%, 75%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-dasharray": [ 3, 3 ], + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "tunnel-street-case", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 19%, 75%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-dasharray": [ 3, 3 ], + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "tunnel-secondary-tertiary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "in", "class", "secondary", "tertiary" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.2, "stops": [ [ 10, 0.75 ], [ 18, 2 ] ] }, + "line-dasharray": [ 3, 3 ], + "line-gap-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-color": "hsl(230, 19%, 75%)" + } + }, + { + "id": "tunnel-primary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "primary" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-dasharray": [ 3, 3 ], + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(230, 19%, 75%)" + } + }, + { + "id": "tunnel-trunk_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "tunnel" ], [ "==", "type", "trunk_link" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-motorway_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "motorway_link" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-trunk-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "tunnel" ], [ "==", "type", "trunk" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-opacity": 1, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-motorway-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "motorway" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-opacity": 1, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-construction", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "construction" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-join": "miter" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "line-dasharray": { + "base": 1, + "stops": [ + [ 14, [ 0.4, 0.8 ] ], + [ 15, [ 0.3, 0.6 ] ], + [ 16, [ 0.2, 0.3 ] ], + [ 17, [ 0.2, 0.25 ] ], + [ 18, [ 0.15, 0.15 ] ] + ] + } + } + }, + { + "id": "tunnel-path", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "path" ], + [ "==", "structure", "tunnel" ], + [ "!=", "type", "steps" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] }, + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ] + }, + "line-color": "hsl(35, 26%, 95%)", + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "tunnel-steps", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "tunnel" ], [ "==", "type", "steps" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 16, 1.6 ], [ 18, 6 ] ] }, + "line-color": "hsl(35, 26%, 95%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 0.3, 0.3 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "tunnel-trunk_link", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-trunk_link-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(46, 77%, 78%)", + "line-opacity": 1, + "line-dasharray": [ 1, 0 ] + } + }, + { + "id": "tunnel-motorway_link", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-motorway_link-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(26, 100%, 78%)", + "line-opacity": 1, + "line-dasharray": [ 1, 0 ] + } + }, + { + "id": "tunnel-pedestrian", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "pedestrian" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": { "base": 1, "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.5, 0.4 ] ], [ 16, [ 1, 0.2 ] ] ] } + } + }, + { + "id": "tunnel-service-link-track", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-service-link-track-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": [ 1, 0 ] + } + }, + { + "id": "tunnel-street_limited", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(35, 14%, 93%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "tunnel-street", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "tunnel-secondary-tertiary", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-secondary-tertiary-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": [ 1, 0 ], + "line-blur": 0 + } + }, + { + "id": "tunnel-primary", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-primary-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": [ 1, 0 ], + "line-blur": 0 + } + }, + { + "id": "tunnel-oneway-arrows-blue-minor", + "type": "symbol", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "path", "pedestrian", "service", "track" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 17, "oneway-small" ], [ 18, "oneway-large" ] ] }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "tunnel-oneway-arrows-blue-major", + "type": "symbol", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "primary", "secondary", "street", "street_limited", "tertiary" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-small" ], [ 17, "oneway-large" ] ] }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "tunnel-trunk", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "trunk" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(46, 77%, 78%)" + } + }, + { + "id": "tunnel-motorway", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-motorway-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-opacity": 1, + "line-color": "hsl(26, 100%, 78%)", + "line-blur": 0 + } + }, + { + "id": "tunnel-oneway-arrows-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "motorway", "motorway_link", "trunk" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "tunnel" ], + [ "!in", "type", "primary_link", "secondary_link", "tertiary_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-white-small" ], [ 17, "oneway-white-large" ] ] }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "ferry", + "type": "line", + "source": "composite", + "source-layer": "road", + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "ferry" ] ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": { "base": 1, "stops": [ [ 15, "hsl(205, 73%, 63%)" ], [ 17, "hsl(230, 73%, 63%)" ] ] }, + "line-opacity": 1, + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] }, + "line-dasharray": { "base": 1, "stops": [ [ 12, [ 1, 0 ] ], [ 13, [ 12, 4 ] ] ] } + } + }, + { + "id": "ferry_auto", + "type": "line", + "source": "composite", + "source-layer": "road", + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "ferry_auto" ] ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": { "base": 1, "stops": [ [ 15, "hsl(205, 73%, 63%)" ], [ 17, "hsl(230, 73%, 63%)" ] ] }, + "line-opacity": 1, + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] } + } + }, + { + "id": "road-path-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "path" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!in", "type", "crossing", "sidewalk", "steps" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 18, 7 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-color": "hsl(230, 17%, 82%)", + "line-blur": 0, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 0.75 ] ] } + } + }, + { + "id": "road-steps-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "!in", "structure", "bridge", "tunnel" ], [ "==", "type", "steps" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 17, 4.6 ], [ 18, 7 ] ] }, + "line-color": "hsl(230, 17%, 82%)", + "line-dasharray": [ 1, 0 ], + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 0.75 ] ] } + } + }, + { + "id": "road-sidewalk-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "!in", "structure", "bridge", "tunnel" ], + [ "in", "type", "crossing", "sidewalk" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 18, 7 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-color": "hsl(230, 17%, 82%)", + "line-blur": 0, + "line-opacity": { "base": 1, "stops": [ [ 16, 0 ], [ 16.25, 0.75 ] ] } + } + }, + { + "id": "turning-features-outline", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ "all", [ "==", "$type", "Point" ], [ "in", "class", "turning_circle", "turning_loop" ] ], + "layout": { + "icon-image": "turning-circle-outline", + "icon-size": { "base": 1.5, "stops": [ [ 14, 0.122 ], [ 18, 0.969 ], [ 20, 1 ] ] }, + "icon-allow-overlap": true, + "icon-ignore-placement": true, + "icon-padding": 0, + "icon-rotation-alignment": "map" + }, + "paint": {} + }, + { + "id": "road-pedestrian-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "pedestrian" ], [ "==", "structure", "none" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 2 ], [ 18, 14.5 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": 0, + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-street-low", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street" ], [ "==", "structure", "none" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11, 0 ], [ 11.25, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "road-street_limited-low", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "none" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11, 0 ], [ 11.25, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "road-service-link-track-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "service", "track" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] } + } + }, + { + "id": "road-street_limited-case", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-street-case", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-secondary-tertiary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "secondary", "tertiary" ], + [ "!in", "structure", "bridge", "tunnel" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.2, "stops": [ [ 10, 0.75 ], [ 18, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 9.99, 0 ], [ 10, 1 ] ] } + } + }, + { + "id": "road-primary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "primary" ], [ "!in", "structure", "bridge", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 9.99, 0 ], [ 10, 1 ] ] } + } + }, + { + "id": "road-motorway_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "motorway_link" ], [ "!in", "structure", "bridge", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] } + } + }, + { + "id": "road-trunk_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "!in", "structure", "bridge", "tunnel" ], [ "==", "type", "trunk_link" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] } + } + }, + { + "id": "road-trunk-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "trunk" ], [ "!in", "structure", "bridge", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 6, 0 ], [ 6.1, 1 ] ] } + } + }, + { + "id": "road-motorway-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "motorway" ], [ "!in", "structure", "bridge", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "road-construction", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "construction" ], [ "==", "structure", "none" ] ] + ], + "layout": { "line-join": "miter" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "line-dasharray": { + "base": 1, + "stops": [ + [ 14, [ 0.4, 0.8 ] ], + [ 15, [ 0.3, 0.6 ] ], + [ 16, [ 0.2, 0.3 ] ], + [ 17, [ 0.2, 0.25 ] ], + [ 18, [ 0.15, 0.15 ] ] + ] + } + } + }, + { + "id": "road-sidewalks", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-sidewalk-bg", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 16, 0 ], [ 16.25, 1 ] ] } + } + }, + { + "id": "road-path", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-path-bg", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "road-steps", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-steps-bg", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 16, 1.6 ], [ 18, 6 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 0.3, 0.3 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "road-trunk_link", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-trunk_link-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(46, 85%, 67%)", + "line-opacity": 1 + } + }, + { + "id": "road-motorway_link", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-motorway_link-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(26, 100%, 68%)", + "line-opacity": 1 + } + }, + { + "id": "road-pedestrian", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-pedestrian-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": { "base": 1, "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.5, 0.4 ] ], [ 16, [ 1, 0.2 ] ] ] } + } + }, + { + "id": "road-pedestrian-polygon-fill", + "type": "fill", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + [ "==", "$type", "Polygon" ], + [ "all", [ "in", "class", "path", "pedestrian" ], [ "==", "structure", "none" ] ] + ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 16, "hsl(230, 16%, 94%)" ], [ 16.25, "hsl(230, 50%, 98%)" ] ] }, + "fill-outline-color": "hsl(230, 26%, 88%)", + "fill-opacity": 1 + } + }, + { + "id": "road-pedestrian-polygon-pattern", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-pedestrian-polygon-fill", + "paint": { + "fill-color": "hsl(0, 0%, 100%)", + "fill-outline-color": "hsl(35, 10%, 83%)", + "fill-pattern": "pedestrian-polygon", + "fill-opacity": { "base": 1, "stops": [ [ 16, 0 ], [ 16.25, 1 ] ] } + } + }, + { + "id": "road-polygon", + "type": "fill", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + [ "==", "$type", "Polygon" ], + [ + "all", + [ "!in", "class", "motorway", "path", "pedestrian", "trunk" ], + [ "!in", "structure", "bridge", "tunnel" ] + ] + ], + "layout": {}, + "paint": { "fill-color": "hsl(0, 0%, 100%)", "fill-outline-color": "#d6d9e6" } + }, + { + "id": "road-service-link-track", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-service-link-track-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "road-street_limited", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(35, 14%, 93%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-street", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-secondary-tertiary", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-secondary-tertiary-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-color": { "base": 1, "stops": [ [ 5, "hsl(35, 32%, 91%)" ], [ 8, "hsl(0, 0%, 100%)" ] ] }, + "line-opacity": { "base": 1.2, "stops": [ [ 5, 0 ], [ 5.5, 1 ] ] } + } + }, + { + "id": "road-primary", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-primary-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": { "base": 1, "stops": [ [ 5, "hsl(35, 32%, 91%)" ], [ 7, "hsl(0, 0%, 100%)" ] ] }, + "line-opacity": 1 + } + }, + { + "id": "road-oneway-arrows-blue-minor", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "path", "pedestrian", "service", "track" ], + [ "==", "oneway", "true" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 17, "oneway-small" ], [ 18, "oneway-large" ] ] }, + "icon-rotation-alignment": "map", + "icon-padding": 2, + "symbol-spacing": 200 + }, + "paint": {} + }, + { + "id": "road-oneway-arrows-blue-major", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "primary", "secondary", "street", "street_limited", "tertiary" ], + [ "==", "oneway", "true" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-small" ], [ 17, "oneway-large" ] ] }, + "icon-rotation-alignment": "map", + "icon-padding": 2, + "symbol-spacing": 200 + }, + "paint": {} + }, + { + "id": "road-trunk", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-trunk-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": { + "base": 1, + "stops": [ [ 6, "hsl(0, 0%, 100%)" ], [ 6.1, "hsl(46, 80%, 60%)" ], [ 9, "hsl(46, 85%, 67%)" ] ] + } + } + }, + { + "id": "road-motorway", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-motorway-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": { "base": 1, "stops": [ [ 8, "hsl(26, 87%, 62%)" ], [ 9, "hsl(26, 100%, 68%)" ] ] } + } + }, + { + "id": "road-rail", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "major_rail", "minor_rail" ], + [ "!in", "structure", "bridge", "tunnel" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] } + } + }, + { + "id": "road-rail-tracks", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-rail", + "paint": { + "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 14, 4 ], [ 20, 8 ] ] }, + "line-dasharray": [ 0.1, 15 ], + "line-opacity": { "base": 1, "stops": [ [ 13.75, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "level-crossings", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ "all", [ "==", "$type", "Point" ], [ "==", "class", "level_crossing" ] ], + "layout": { "icon-size": 1, "icon-image": "level-crossing", "icon-allow-overlap": true }, + "paint": {} + }, + { + "id": "road-oneway-arrows-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "motorway", "motorway_link", "trunk" ], + [ "==", "oneway", "true" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!in", "type", "primary_link", "secondary_link", "tertiary_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-white-small" ], [ 17, "oneway-white-large" ] ] }, + "icon-padding": 2, + "symbol-spacing": 200 + }, + "paint": {} + }, + { + "id": "turning-features", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ "all", [ "==", "$type", "Point" ], [ "in", "class", "turning_circle", "turning_loop" ] ], + "layout": { + "icon-image": "turning-circle", + "icon-size": { "base": 1.5, "stops": [ [ 14, 0.095 ], [ 18, 1 ] ] }, + "icon-allow-overlap": true, + "icon-ignore-placement": true, + "icon-padding": 0, + "icon-rotation-alignment": "map" + }, + "paint": {} + }, + { + "id": "bridge-path-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "path" ], + [ "==", "structure", "bridge" ], + [ "!=", "type", "steps" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 18, 7 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-color": "hsl(230, 17%, 82%)", + "line-blur": 0, + "line-opacity": { "base": 1, "stops": [ [ 15, 0 ], [ 15.25, 1 ] ] } + } + }, + { + "id": "bridge-steps-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "bridge" ], [ "==", "type", "steps" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 17, 4.6 ], [ 18, 7 ] ] }, + "line-color": "hsl(230, 17%, 82%)", + "line-dasharray": [ 1, 0 ], + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 0.75 ] ] } + } + }, + { + "id": "bridge-pedestrian-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "pedestrian" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 2 ], [ 18, 14.5 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": 0, + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "bridge-street-low", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "bridge-street_limited-low", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "bridge-service-link-track-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "service", "track" ], + [ "==", "structure", "bridge" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] } + } + }, + { + "id": "bridge-street_limited-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] } + } + }, + { + "id": "bridge-street-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] } + } + }, + { + "id": "bridge-secondary-tertiary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "in", "class", "secondary", "tertiary" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.2, "stops": [ [ 10, 0.75 ], [ 18, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-translate": [ 0, 0 ] + } + }, + { + "id": "bridge-primary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "primary" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-translate": [ 0, 0 ] + } + }, + { + "id": "bridge-trunk_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ], + [ "==", "type", "trunk_link" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] } + } + }, + { + "id": "bridge-motorway_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway_link" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": 1 + } + }, + { + "id": "bridge-trunk-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "trunk" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "bridge-motorway-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "bridge-construction", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "construction" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "miter" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "line-dasharray": { + "base": 1, + "stops": [ + [ 14, [ 0.4, 0.8 ] ], + [ 15, [ 0.3, 0.6 ] ], + [ 16, [ 0.2, 0.3 ] ], + [ 17, [ 0.2, 0.25 ] ], + [ 18, [ 0.15, 0.15 ] ] + ] + } + } + }, + { + "id": "bridge-path", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "path" ], + [ "==", "structure", "bridge" ], + [ "!=", "type", "steps" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "bridge-steps", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-steps-bg", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 16, 1.6 ], [ 18, 6 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 0.3, 0.3 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "bridge-trunk_link", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ], + [ "==", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(46, 85%, 67%)" + } + }, + { + "id": "bridge-motorway_link", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway_link" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(26, 100%, 68%)" + } + }, + { + "id": "bridge-pedestrian", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-pedestrian-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": { "base": 1, "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.5, 0.4 ] ], [ 16, [ 1, 0.2 ] ] ] } + } + }, + { + "id": "bridge-service-link-track", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "service", "track" ], + [ "==", "structure", "bridge" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "bridge-street_limited", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(35, 14%, 93%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "bridge-street", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "bridge-secondary-tertiary", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "bridge" ], [ "in", "type", "secondary", "tertiary" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "base": 1.2, "stops": [ [ 5, 0 ], [ 5.5, 1 ] ] } + } + }, + { + "id": "bridge-primary", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "bridge" ], [ "==", "type", "primary" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1 + } + }, + { + "id": "bridge-oneway-arrows-blue-minor", + "type": "symbol", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "path", "pedestrian", "service", "track" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 17, "oneway-small" ], [ 18, "oneway-large" ] ] }, + "symbol-spacing": 200, + "icon-rotation-alignment": "map", + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "bridge-oneway-arrows-blue-major", + "type": "symbol", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "primary", "secondary", "street", "street_limited", "tertiary" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-small" ], [ 17, "oneway-large" ] ] }, + "symbol-spacing": 200, + "icon-rotation-alignment": "map", + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "bridge-trunk", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "trunk" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(46, 85%, 67%)" + } + }, + { + "id": "bridge-motorway", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(26, 100%, 68%)" + } + }, + { + "id": "bridge-rail", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "in", "class", "major_rail", "minor_rail" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] } + } + }, + { + "id": "bridge-rail-tracks", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-rail", + "paint": { + "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 14, 4 ], [ 20, 8 ] ] }, + "line-dasharray": [ 0.1, 15 ], + "line-opacity": { "base": 1, "stops": [ [ 13.75, 0 ], [ 20, 1 ] ] } + } + }, + { + "id": "bridge-trunk_link-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ], + [ "==", "type", "trunk_link" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] } + } + }, + { + "id": "bridge-motorway_link-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway_link" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": 1 + } + }, + { + "id": "bridge-trunk-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "trunk" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "bridge-motorway-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "bridge-trunk_link-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ], + [ "==", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(46, 85%, 67%)" + } + }, + { + "id": "bridge-motorway_link-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway_link" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(26, 100%, 68%)" + } + }, + { + "id": "bridge-trunk-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "trunk" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(46, 85%, 67%)" + } + }, + { + "id": "bridge-motorway-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(26, 100%, 68%)" + } + }, + { + "id": "bridge-oneway-arrows-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "motorway", "motorway_link", "trunk" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "bridge" ], + [ "!in", "type", "primary_link", "secondary_link", "tertiary_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-white-small" ], [ 17, "oneway-white-large" ] ] }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "aerialway", + "type": "line", + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "class", "aerialway" ] ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": "hsl(230, 10%, 74%)", + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] } + } + }, + { + "id": "admin-3-4-boundaries-bg", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "filter": [ "all", [ ">=", "admin_level", 3 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "bevel" }, + "paint": { + "line-color": { "base": 1, "stops": [ [ 8, "hsl(35, 12%, 89%)" ], [ 16, "hsl(230, 49%, 90%)" ] ] }, + "line-width": { "base": 1, "stops": [ [ 7, 3.75 ], [ 12, 5.5 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 7, 0 ], [ 8, 0.75 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-translate": [ 0, 0 ], + "line-blur": { "base": 1, "stops": [ [ 3, 0 ], [ 8, 3 ] ] } + } + }, + { + "id": "admin-2-boundaries-bg", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ "all", [ "==", "admin_level", 2 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "miter" }, + "paint": { + "line-width": { "base": 1, "stops": [ [ 3, 3.5 ], [ 10, 8 ] ] }, + "line-color": { "base": 1, "stops": [ [ 6, "hsl(35, 12%, 89%)" ], [ 8, "hsl(230, 49%, 90%)" ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 3, 0 ], [ 4, 0.5 ] ] }, + "line-translate": [ 0, 0 ], + "line-blur": { "base": 1, "stops": [ [ 3, 0 ], [ 10, 2 ] ] } + } + }, + { + "id": "admin-3-4-boundaries", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "filter": [ "all", [ ">=", "admin_level", 3 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "round", "line-cap": "round" }, + "paint": { + "line-dasharray": { "base": 1, "stops": [ [ 6, [ 2, 0 ] ], [ 7, [ 2, 2, 6, 2 ] ] ] }, + "line-width": { "base": 1, "stops": [ [ 7, 0.75 ], [ 12, 1.5 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 2, 0 ], [ 3, 1 ] ] }, + "line-color": { "base": 1, "stops": [ [ 3, "hsl(230, 14%, 77%)" ], [ 7, "hsl(230, 8%, 62%)" ] ] } + } + }, + { + "id": "admin-2-boundaries", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ "all", [ "==", "admin_level", 2 ], [ "==", "disputed", 0 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "round", "line-cap": "round" }, + "paint": { + "line-color": "hsl(230, 8%, 51%)", + "line-width": { "base": 1, "stops": [ [ 3, 0.5 ], [ 10, 2 ] ] } + } + }, + { + "id": "admin-2-boundaries-dispute", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ "all", [ "==", "admin_level", 2 ], [ "==", "disputed", 1 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "round" }, + "paint": { + "line-dasharray": [ 1.5, 1.5 ], + "line-color": "hsl(230, 8%, 51%)", + "line-width": { "base": 1, "stops": [ [ 3, 0.5 ], [ 10, 2 ] ] } + } + }, + { + "id": "housenum-label", + "type": "symbol", + "source": "composite", + "source-layer": "housenum_label", + "minzoom": 17, + "layout": { + "text-field": "{house_num}", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-padding": 4, + "text-max-width": 7, + "text-size": 9.5 + }, + "paint": { + "text-color": "hsl(35, 2%, 69%)", + "text-halo-color": "hsl(35, 8%, 85%)", + "text-halo-width": 0.5, + "text-halo-blur": 0 + } + }, + { + "id": "waterway-label", + "type": "symbol", + "source": "composite", + "source-layer": "waterway_label", + "minzoom": 12, + "filter": [ "in", "class", "canal", "river" ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-max-angle": 30, + "text-size": { "base": 1, "stops": [ [ 13, 12 ], [ 18, 16 ] ] } + }, + "paint": { + "text-halo-width": 0.5, + "text-halo-color": "hsl(196, 80%, 70%)", + "text-color": "hsl(230, 48%, 44%)", + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-scalerank4-l15", + "type": "symbol", + "metadata": { "mapbox:group": "1444933456003.5437" }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 17, + "filter": [ + "all", + [ ">=", "localrank", 15 ], + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 4 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-scalerank4-l1", + "type": "symbol", + "metadata": { "mapbox:group": "1444933456003.5437" }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 15, + "filter": [ + "all", + [ "<=", "localrank", 14 ], + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 4 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 1, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-parks_scalerank4", + "type": "symbol", + "metadata": { "mapbox:group": "1444933456003.5437" }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 15, + "filter": [ + "all", + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 4 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 1, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(100, 100%, 20%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-scalerank3", + "type": "symbol", + "metadata": { "mapbox:group": "1444933372896.5967" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 3 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 1, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-parks-scalerank3", + "type": "symbol", + "metadata": { "mapbox:group": "1444933372896.5967" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 3 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(100, 100%, 20%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "road-label-small", + "type": "symbol", + "metadata": { "mapbox:group": "1444933721429.3076" }, + "source": "composite", + "source-layer": "road_label", + "minzoom": 15, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "!in", + "class", + "link", + "motorway", + "pedestrian", + "primary", + "secondary", + "street", + "street_limited", + "tertiary", + "trunk" + ] + ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 15, 10 ], [ 20, 13 ] ] }, + "text-max-angle": 30, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-padding": 1, + "text-rotation-alignment": "map", + "text-field": "{name_en}", + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "text-halo-blur": 1 + } + }, + { + "id": "road-label-medium", + "type": "symbol", + "metadata": { "mapbox:group": "1444933721429.3076" }, + "source": "composite", + "source-layer": "road_label", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "in", "class", "link", "pedestrian", "street", "street_limited" ] + ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 11, 10 ], [ 20, 14 ] ] }, + "text-max-angle": 30, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-padding": 1, + "text-rotation-alignment": "map", + "text-field": "{name_en}", + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "road-label-large", + "type": "symbol", + "metadata": { "mapbox:group": "1444933721429.3076" }, + "source": "composite", + "source-layer": "road_label", + "filter": [ "in", "class", "motorway", "primary", "secondary", "tertiary", "trunk" ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 9, 10 ], [ 20, 16 ] ] }, + "text-max-angle": 30, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-padding": 1, + "text-rotation-alignment": "map", + "text-field": "{name_en}", + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsla(0, 0%, 100%, 0.75)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "road-shields-black", + "type": "symbol", + "metadata": { "mapbox:group": "1444933575858.6992" }, + "source": "composite", + "source-layer": "road_label", + "filter": [ + "all", + [ "<=", "reflen", 6 ], + [ + "!in", + "shield", + "at-expressway", + "at-motorway", + "at-state-b", + "bg-motorway", + "bg-national", + "ch-main", + "ch-motorway", + "cz-motorway", + "cz-road", + "de-motorway", + "e-road", + "fi-main", + "gr-motorway", + "gr-national", + "hr-motorway", + "hr-state", + "hu-main", + "hu-motorway", + "nz-state", + "pl-expressway", + "pl-motorway", + "pl-national", + "ro-county", + "ro-motorway", + "ro-national", + "rs-motorway", + "rs-state-1b", + "se-main", + "si-expressway", + "si-motorway", + "sk-highway", + "sk-road", + "us-interstate", + "us-interstate-business", + "us-interstate-duplex", + "us-interstate-truck", + "za-metropolitan", + "za-national", + "za-provincial", + "za-regional" + ] + ], + "layout": { + "text-size": 9, + "icon-image": "{shield}-{reflen}", + "icon-rotation-alignment": "viewport", + "text-max-angle": 38, + "symbol-spacing": { "base": 1, "stops": [ [ 11, 150 ], [ 14, 200 ] ] }, + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "symbol-placement": { "base": 1, "stops": [ [ 10, "point" ], [ 11, "line" ] ] }, + "text-padding": 2, + "text-rotation-alignment": "viewport", + "text-field": "{ref}", + "text-letter-spacing": 0.05, + "icon-padding": 2 + }, + "paint": { + "text-color": "hsl(0, 0%, 7%)", + "icon-halo-color": "rgba(0, 0, 0, 1)", + "icon-halo-width": 1, + "text-opacity": 1, + "icon-color": "white", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0 + } + }, + { + "id": "road-shields-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444933575858.6992" }, + "source": "composite", + "source-layer": "road_label", + "filter": [ + "all", + [ "<=", "reflen", 6 ], + [ + "in", + "shield", + "at-expressway", + "at-motorway", + "at-state-b", + "bg-motorway", + "bg-national", + "ch-main", + "ch-motorway", + "cz-motorway", + "cz-road", + "de-motorway", + "e-road", + "fi-main", + "gr-motorway", + "gr-national", + "hr-motorway", + "hr-state", + "hu-main", + "hu-motorway", + "nz-state", + "pl-expressway", + "pl-motorway", + "pl-national", + "ro-county", + "ro-motorway", + "ro-national", + "rs-motorway", + "rs-state-1b", + "se-main", + "si-expressway", + "si-motorway", + "sk-highway", + "sk-road", + "us-interstate", + "us-interstate-business", + "us-interstate-duplex", + "us-interstate-truck", + "za-metropolitan", + "za-national", + "za-provincial", + "za-regional" + ] + ], + "layout": { + "text-size": 9, + "icon-image": "{shield}-{reflen}", + "icon-rotation-alignment": "viewport", + "text-max-angle": 38, + "symbol-spacing": { "base": 1, "stops": [ [ 11, 150 ], [ 14, 200 ] ] }, + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "symbol-placement": { "base": 1, "stops": [ [ 10, "point" ], [ 11, "line" ] ] }, + "text-padding": 2, + "text-rotation-alignment": "viewport", + "text-field": "{ref}", + "text-letter-spacing": 0.05, + "icon-padding": 2 + }, + "paint": { + "text-color": "hsl(0, 0%, 100%)", + "icon-halo-color": "rgba(0, 0, 0, 1)", + "icon-halo-width": 1, + "text-opacity": 1, + "icon-color": "white", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0 + } + }, + { + "id": "motorway-junction", + "type": "symbol", + "metadata": { "mapbox:group": "1444933575858.6992" }, + "source": "composite", + "source-layer": "motorway_junction", + "minzoom": 14, + "filter": [ "all", [ ">", "reflen", 0 ], [ "<=", "reflen", 9 ] ], + "layout": { + "text-field": "{ref}", + "text-size": 9, + "icon-image": "motorway-exit-{reflen}", + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ] + }, + "paint": { "text-color": "hsl(0, 0%, 100%)", "text-translate": [ 0, 0 ] } + }, + { + "id": "poi-scalerank2", + "type": "symbol", + "metadata": { "mapbox:group": "1444933358918.2366" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 2 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 14, 11 ], [ 20, 14 ] ] }, + "icon-image": { "stops": [ [ 14, "{maki}-11" ], [ 15, "{maki}-15" ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-parks-scalerank2", + "type": "symbol", + "metadata": { "mapbox:group": "1444933358918.2366" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 2 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 14, 11 ], [ 20, 14 ] ] }, + "icon-image": { "stops": [ [ 14, "{maki}-11" ], [ 15, "{maki}-15" ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(100, 100%, 20%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "rail-label", + "type": "symbol", + "source": "composite", + "source-layer": "rail_station_label", + "minzoom": 12, + "filter": [ "!=", "maki", "entrance" ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{network}", + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-offset": [ 0, 0.85 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": { "base": 1, "stops": [ [ 0, "" ], [ 13, "{name_en}" ] ] }, + "text-letter-spacing": 0.01, + "icon-padding": 0, + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(230, 48%, 44%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "icon-halo-width": 4, + "icon-halo-color": "#fff", + "text-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "text-halo-blur": 0.5 + } + }, + { + "id": "water-label-sm", + "type": "symbol", + "metadata": { "mapbox:group": "1444933808272.805" }, + "source": "composite", + "source-layer": "water_label", + "minzoom": 15, + "filter": [ "<=", "area", 10000 ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-max-width": 7, + "text-size": { "base": 1, "stops": [ [ 16, 13 ], [ 20, 16 ] ] } + }, + "paint": { "text-color": "hsl(230, 48%, 44%)" } + }, + { + "id": "water-label", + "type": "symbol", + "metadata": { "mapbox:group": "1444933808272.805" }, + "source": "composite", + "source-layer": "water_label", + "minzoom": 5, + "filter": [ ">", "area", 10000 ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-max-width": 7, + "text-size": { "base": 1, "stops": [ [ 13, 13 ], [ 18, 18 ] ] } + }, + "paint": { "text-color": "hsl(230, 48%, 44%)" } + }, + { + "id": "place-residential", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "maxzoom": 18, + "filter": [ + "all", + [ "in", "$type", "LineString", "Point", "Polygon" ], + [ "all", [ "<=", "localrank", 10 ], [ "==", "type", "residential" ] ] + ], + "layout": { + "text-line-height": 1.2, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 14 ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0 ], + "text-rotation-alignment": "viewport", + "text-field": "{name_en}", + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-parks-scalerank1", + "type": "symbol", + "metadata": { "mapbox:group": "1444933322393.2852" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "<=", "scalerank", 1 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 14 ] ] }, + "icon-image": { "stops": [ [ 13, "{maki}-11" ], [ 14, "{maki}-15" ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(100, 100%, 20%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-scalerank1", + "type": "symbol", + "metadata": { "mapbox:group": "1444933322393.2852" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "<=", "scalerank", 1 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 14 ] ] }, + "icon-image": { "stops": [ [ 13, "{maki}-11" ], [ 14, "{maki}-15" ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "airport-label", + "type": "symbol", + "source": "composite", + "source-layer": "airport_label", + "minzoom": 9, + "filter": [ "<=", "scalerank", 2 ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 10, 12 ], [ 18, 18 ] ] }, + "icon-image": { "stops": [ [ 12, "{maki}-11" ], [ 13, "{maki}-15" ] ] }, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.75 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": { "stops": [ [ 11, "{ref}" ], [ 12, "{name_en}" ] ] }, + "text-letter-spacing": 0.01, + "text-max-width": 9 + }, + "paint": { + "text-color": "hsl(230, 48%, 44%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "place-islet-archipelago-aboriginal", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "maxzoom": 16, + "filter": [ "in", "type", "aboriginal_lands", "archipelago", "islet" ], + "layout": { + "text-line-height": 1.2, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 16 ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0 ], + "text-rotation-alignment": "viewport", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(230, 29%, 35%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "place-neighbourhood", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 10, + "maxzoom": 16, + "filter": [ "==", "type", "neighbourhood" ], + "layout": { + "text-field": "{name_en}", + "text-transform": "uppercase", + "text-letter-spacing": 0.1, + "text-max-width": 7, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-padding": 3, + "text-size": { "base": 1, "stops": [ [ 12, 11 ], [ 16, 16 ] ] } + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "text-color": "hsl(230, 29%, 35%)", + "text-halo-blur": 0.5 + } + }, + { + "id": "place-suburb", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 10, + "maxzoom": 16, + "filter": [ "==", "type", "suburb" ], + "layout": { + "text-field": "{name_en}", + "text-transform": "uppercase", + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-letter-spacing": 0.15, + "text-max-width": 7, + "text-padding": 3, + "text-size": { "base": 1, "stops": [ [ 11, 11 ], [ 15, 18 ] ] } + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "text-color": "hsl(230, 29%, 35%)", + "text-halo-blur": 0.5 + } + }, + { + "id": "place-hamlet", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 10, + "maxzoom": 16, + "filter": [ "==", "type", "hamlet" ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 12, 11.5 ], [ 15, 16 ] ] } + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "text-color": "hsl(0, 0%, 0%)" + } + }, + { + "id": "place-village", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 8, + "maxzoom": 15, + "filter": [ "==", "type", "village" ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-max-width": 7, + "text-size": { "base": 1, "stops": [ [ 10, 11.5 ], [ 16, 18 ] ] } + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "text-color": "hsl(0, 0%, 0%)" + } + }, + { + "id": "place-town", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 6, + "maxzoom": 15, + "filter": [ "==", "type", "town" ], + "layout": { + "icon-image": "dot-9", + "text-font": { + "base": 1, + "stops": [ + [ 11, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 12, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7, [ 0, -0.15 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7, + "text-size": { "base": 1, "stops": [ [ 7, 11.5 ], [ 15, 20 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] } + } + }, + { + "id": "place-island", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "maxzoom": 16, + "filter": [ "==", "type", "island" ], + "layout": { + "text-line-height": 1.2, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 16 ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0 ], + "text-rotation-alignment": "viewport", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(230, 29%, 35%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "place-city-sm", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "maxzoom": 14, + "filter": [ "all", [ "!in", "scalerank", 0, 1, 2, 3, 4, 5 ], [ "==", "type", "city" ] ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 6, 12 ], [ 14, 22 ] ] }, + "icon-image": "dot-9", + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, -0.2 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] } + } + }, + { + "id": "place-city-md-s", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "maxzoom": 14, + "filter": [ + "all", + [ "in", "ldir", "E", "S", "SE", "SW" ], + [ "in", "scalerank", 3, 4, 5 ], + [ "==", "type", "city" ] + ], + "layout": { + "text-field": "{name_en}", + "icon-image": "dot-10", + "text-anchor": { "base": 1, "stops": [ [ 7, "top" ], [ 8, "center" ] ] }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, 0.1 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-size": { "base": 0.9, "stops": [ [ 5, 12 ], [ 12, 22 ] ] } + }, + "paint": { + "text-halo-width": 1, + "text-halo-color": "hsl(0, 0%, 100%)", + "text-color": "hsl(0, 0%, 0%)", + "text-halo-blur": 1, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] } + } + }, + { + "id": "place-city-md-n", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "maxzoom": 14, + "filter": [ + "all", + [ "in", "ldir", "N", "NE", "NW", "W" ], + [ "in", "scalerank", 3, 4, 5 ], + [ "==", "type", "city" ] + ], + "layout": { + "icon-image": "dot-10", + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, -0.25 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7, + "text-size": { "base": 0.9, "stops": [ [ 5, 12 ], [ 12, 22 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] }, + "text-halo-blur": 1 + } + }, + { + "id": "place-city-lg-s", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 1, + "maxzoom": 14, + "filter": [ + "all", + [ "in", "ldir", "E", "S", "SE", "SW" ], + [ "<=", "scalerank", 2 ], + [ "==", "type", "city" ] + ], + "layout": { + "icon-image": "dot-11", + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, 0.15 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "top" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7, + "text-size": { "base": 0.9, "stops": [ [ 4, 12 ], [ 10, 22 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] }, + "text-halo-blur": 1 + } + }, + { + "id": "place-city-lg-n", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 1, + "maxzoom": 14, + "filter": [ + "all", + [ "in", "ldir", "N", "NE", "NW", "W" ], + [ "<=", "scalerank", 2 ], + [ "==", "type", "city" ] + ], + "layout": { + "icon-image": "dot-11", + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, -0.25 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7, + "text-size": { "base": 0.9, "stops": [ [ 4, 12 ], [ 10, 22 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-opacity": 1, + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] }, + "text-halo-blur": 1 + } + }, + { + "id": "marine-label-sm-ln", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 3, + "maxzoom": 10, + "filter": [ "all", [ "==", "$type", "LineString" ], [ ">=", "labelrank", 4 ] ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 3, 12 ], [ 6, 16 ] ] }, + "symbol-spacing": { "base": 1, "stops": [ [ 4, 100 ], [ 6, 400 ] ] }, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-field": "{name_en}", + "text-letter-spacing": 0.1, + "text-max-width": 5 + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-sm-pt", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 3, + "maxzoom": 10, + "filter": [ "all", [ "==", "$type", "Point" ], [ ">=", "labelrank", 4 ] ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 5, + "text-letter-spacing": 0.1, + "text-line-height": 1.5, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 3, 12 ], [ 6, 16 ] ] } + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-md-ln", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 2, + "maxzoom": 8, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "in", "labelrank", 2, 3 ] ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1.1, "stops": [ [ 2, 12 ], [ 5, 20 ] ] }, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-field": "{name_en}", + "text-letter-spacing": 0.15, + "text-max-width": 5 + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-md-pt", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 2, + "maxzoom": 8, + "filter": [ "all", [ "==", "$type", "Point" ], [ "in", "labelrank", 2, 3 ] ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 5, + "text-letter-spacing": 0.15, + "text-line-height": 1.5, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-size": { "base": 1.1, "stops": [ [ 2, 14 ], [ 5, 20 ] ] } + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-lg-ln", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 1, + "maxzoom": 4, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "labelrank", 1 ] ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 4, + "text-letter-spacing": 0.25, + "text-line-height": 1.1, + "symbol-placement": "line", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 1, 14 ], [ 4, 30 ] ] } + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-lg-pt", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 1, + "maxzoom": 4, + "filter": [ "all", [ "==", "$type", "Point" ], [ "==", "labelrank", 1 ] ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 4, + "text-letter-spacing": 0.25, + "text-line-height": 1.5, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 1, 14 ], [ 4, 30 ] ] } + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "state-label-sm", + "type": "symbol", + "metadata": { "mapbox:group": "1444856151690.9143" }, + "source": "composite", + "source-layer": "state_label", + "minzoom": 3, + "maxzoom": 9, + "filter": [ "<", "area", 20000 ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 6, 10 ], [ 9, 14 ] ] }, + "text-transform": "uppercase", + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "text-field": { "base": 1, "stops": [ [ 0, "{abbr}" ], [ 6, "{name_en}" ] ] }, + "text-letter-spacing": 0.15, + "text-max-width": 5 + }, + "paint": { + "text-opacity": 1, + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "state-label-md", + "type": "symbol", + "metadata": { "mapbox:group": "1444856151690.9143" }, + "source": "composite", + "source-layer": "state_label", + "minzoom": 3, + "maxzoom": 8, + "filter": [ "all", [ "<", "area", 80000 ], [ ">=", "area", 20000 ] ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 5, 10 ], [ 8, 16 ] ] }, + "text-transform": "uppercase", + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "text-field": { "base": 1, "stops": [ [ 0, "{abbr}" ], [ 5, "{name_en}" ] ] }, + "text-letter-spacing": 0.15, + "text-max-width": 6 + }, + "paint": { + "text-opacity": 1, + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "state-label-lg", + "type": "symbol", + "metadata": { "mapbox:group": "1444856151690.9143" }, + "source": "composite", + "source-layer": "state_label", + "minzoom": 3, + "maxzoom": 7, + "filter": [ ">=", "area", 80000 ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 4, 10 ], [ 7, 18 ] ] }, + "text-transform": "uppercase", + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "text-padding": 1, + "text-field": { "base": 1, "stops": [ [ 0, "{abbr}" ], [ 4, "{name_en}" ] ] }, + "text-letter-spacing": 0.15, + "text-max-width": 6 + }, + "paint": { + "text-opacity": 1, + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "country-label-sm", + "type": "symbol", + "metadata": { "mapbox:group": "1444856144497.7825" }, + "source": "composite", + "source-layer": "country_label", + "minzoom": 1, + "maxzoom": 10, + "filter": [ ">=", "scalerank", 5 ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 6, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-size": { "base": 0.9, "stops": [ [ 5, 14 ], [ 9, 22 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": { "base": 1, "stops": [ [ 2, "rgba(255,255,255,0.75)" ], [ 3, "hsl(0, 0%, 100%)" ] ] }, + "text-halo-width": 1.25 + } + }, + { + "id": "country-label-md", + "type": "symbol", + "metadata": { "mapbox:group": "1444856144497.7825" }, + "source": "composite", + "source-layer": "country_label", + "minzoom": 1, + "maxzoom": 8, + "filter": [ "in", "scalerank", 3, 4 ], + "layout": { + "text-field": { "base": 1, "stops": [ [ 0, "{code}" ], [ 2, "{name_en}" ] ] }, + "text-max-width": 6, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 3, 10 ], [ 8, 24 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": { "base": 1, "stops": [ [ 2, "rgba(255,255,255,0.75)" ], [ 3, "hsl(0, 0%, 100%)" ] ] }, + "text-halo-width": 1.25 + } + }, + { + "id": "country-label-lg", + "type": "symbol", + "metadata": { "mapbox:group": "1444856144497.7825" }, + "source": "composite", + "source-layer": "country_label", + "minzoom": 1, + "maxzoom": 7, + "filter": [ "in", "scalerank", 1, 2 ], + "layout": { + "text-field": "{name_en}", + "text-max-width": { "base": 1, "stops": [ [ 0, 5 ], [ 3, 6 ] ] }, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 1, 10 ], [ 6, 24 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": { "base": 1, "stops": [ [ 2, "rgba(255,255,255,0.75)" ], [ 3, "hsl(0, 0%, 100%)" ] ] }, + "text-halo-width": 1.25 + } + } + ] +} \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/fixtures/api/style_formatted_labels.json b/mapbox-gl-native/benchmark/fixtures/api/style_formatted_labels.json new file mode 100644 index 000000000..c62777038 --- /dev/null +++ b/mapbox-gl-native/benchmark/fixtures/api/style_formatted_labels.json @@ -0,0 +1,3993 @@ +{ + "version": 8, + "name": "Mapbox Streets", + "metadata": { + "mapbox:autocomposite": true, + "mapbox:type": "default", + "mapbox:groups": { + "1444934828655.3389": { "name": "Aeroways", "collapsed": true }, + "1444933322393.2852": { "name": "POI labels (scalerank 1)", "collapsed": true }, + "1444855786460.0557": { "name": "Roads", "collapsed": true }, + "1444933575858.6992": { "name": "Highway shields", "collapsed": true }, + "1444934295202.7542": { "name": "Admin boundaries", "collapsed": true }, + "1444856151690.9143": { "name": "State labels", "collapsed": true }, + "1444933721429.3076": { "name": "Road labels", "collapsed": true }, + "1444933358918.2366": { "name": "POI labels (scalerank 2)", "collapsed": true }, + "1444933808272.805": { "name": "Water labels", "collapsed": true }, + "1444933372896.5967": { "name": "POI labels (scalerank 3)", "collapsed": true }, + "1444855799204.86": { "name": "Bridges", "collapsed": true }, + "1444856087950.3635": { "name": "Marine labels", "collapsed": true }, + "1456969573402.7817": { "name": "Hillshading", "collapsed": true }, + "1444862510685.128": { "name": "City labels", "collapsed": true }, + "1444855769305.6016": { "name": "Tunnels", "collapsed": true }, + "1456970288113.8113": { "name": "Landcover", "collapsed": true }, + "1444856144497.7825": { "name": "Country labels", "collapsed": true }, + "1444933456003.5437": { "name": "POI labels (scalerank 4)", "collapsed": true } + } + }, + "sources": { + "composite": { "url": "mapbox://mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7", "type": "vector" } + }, + "sprite": "mapbox://sprites/mapbox/streets-v10", + "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf", + "visibility": "public", + "layers": [ + { + "id": "background", + "type": "background", + "layout": {}, + "paint": { + "background-color": { "base": 1, "stops": [ [ 11, "hsl(35, 32%, 91%)" ], [ 13, "hsl(35, 12%, 89%)" ] ] } + } + }, + { + "id": "landcover_snow", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "filter": [ "==", "class", "snow" ], + "layout": {}, + "paint": { "fill-color": "hsl(0, 0%, 100%)", "fill-opacity": 0.2, "fill-antialias": false } + }, + { + "id": "landcover_wood", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "maxzoom": 14, + "filter": [ "==", "class", "wood" ], + "layout": {}, + "paint": { + "fill-color": "hsl(75, 62%, 81%)", + "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "landcover_scrub", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "maxzoom": 14, + "filter": [ "==", "class", "scrub" ], + "layout": {}, + "paint": { + "fill-color": "hsl(75, 62%, 81%)", + "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "landcover_grass", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "maxzoom": 14, + "filter": [ "==", "class", "grass" ], + "layout": {}, + "paint": { + "fill-color": "hsl(75, 62%, 81%)", + "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "landcover_crop", + "type": "fill", + "metadata": { "mapbox:group": "1456970288113.8113" }, + "source": "composite", + "source-layer": "landcover", + "maxzoom": 14, + "filter": [ "==", "class", "crop" ], + "layout": {}, + "paint": { + "fill-color": "hsl(75, 62%, 81%)", + "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "national_park", + "type": "fill", + "source": "composite", + "source-layer": "landuse_overlay", + "filter": [ "==", "class", "national_park" ], + "layout": {}, + "paint": { + "fill-color": "hsl(100, 58%, 76%)", + "fill-opacity": { "base": 1, "stops": [ [ 5, 0 ], [ 6, 0.5 ] ] } + } + }, + { + "id": "hospital", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "hospital" ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15.5, "hsl(340, 37%, 87%)" ], [ 16, "hsl(340, 63%, 89%)" ] ] } + } + }, + { + "id": "school", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "school" ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15.5, "hsl(50, 47%, 81%)" ], [ 16, "hsl(50, 63%, 84%)" ] ] } + } + }, + { + "id": "park", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "park" ], + "layout": {}, + "paint": { + "fill-color": "hsl(100, 58%, 76%)", + "fill-opacity": { "base": 1, "stops": [ [ 5, 0 ], [ 6, 1 ] ] } + } + }, + { + "id": "pitch", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "pitch" ], + "layout": {}, + "paint": { "fill-color": "hsl(100, 57%, 72%)" } + }, + { + "id": "pitch-line", + "type": "line", + "source": "composite", + "source-layer": "landuse", + "minzoom": 15, + "filter": [ "==", "class", "pitch" ], + "layout": { "line-join": "miter" }, + "paint": { "line-color": "hsl(75, 57%, 84%)" } + }, + { + "id": "cemetery", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "cemetery" ], + "layout": {}, + "paint": { "fill-color": "hsl(75, 37%, 81%)" } + }, + { + "id": "industrial", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "industrial" ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15.5, "hsl(230, 15%, 86%)" ], [ 16, "hsl(230, 29%, 89%)" ] ] } + } + }, + { + "id": "sand", + "type": "fill", + "source": "composite", + "source-layer": "landuse", + "filter": [ "==", "class", "sand" ], + "layout": {}, + "paint": { "fill-color": "hsl(60, 46%, 87%)" } + }, + { + "id": "hillshade_highlight_bright", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 94 ], + "layout": {}, + "paint": { + "fill-color": "hsl(0, 0%, 100%)", + "fill-opacity": { "stops": [ [ 14, 0.12 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_highlight_med", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 90 ], + "layout": {}, + "paint": { + "fill-color": "hsl(0, 0%, 100%)", + "fill-opacity": { "stops": [ [ 14, 0.12 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_shadow_faint", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 89 ], + "layout": {}, + "paint": { + "fill-color": "hsl(56, 59%, 22%)", + "fill-opacity": { "stops": [ [ 14, 0.05 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_shadow_med", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 78 ], + "layout": {}, + "paint": { + "fill-color": "hsl(56, 59%, 22%)", + "fill-opacity": { "stops": [ [ 14, 0.05 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_shadow_dark", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 67 ], + "layout": {}, + "paint": { + "fill-color": "hsl(56, 59%, 22%)", + "fill-opacity": { "stops": [ [ 14, 0.06 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "hillshade_shadow_extreme", + "type": "fill", + "metadata": { "mapbox:group": "1456969573402.7817" }, + "source": "composite", + "source-layer": "hillshade", + "maxzoom": 16, + "filter": [ "==", "level", 56 ], + "layout": {}, + "paint": { + "fill-color": "hsl(56, 59%, 22%)", + "fill-opacity": { "stops": [ [ 14, 0.06 ], [ 16, 0 ] ] }, + "fill-antialias": false + } + }, + { + "id": "waterway-river-canal", + "type": "line", + "source": "composite", + "source-layer": "waterway", + "minzoom": 8, + "filter": [ "in", "class", "canal", "river" ], + "layout": { + "line-cap": { "base": 1, "stops": [ [ 0, "butt" ], [ 11, "round" ] ] }, + "line-join": "round" + }, + "paint": { + "line-color": "hsl(205, 87%, 76%)", + "line-width": { "base": 1.3, "stops": [ [ 8.5, 0.1 ], [ 20, 8 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 8, 0 ], [ 8.5, 1 ] ] } + } + }, + { + "id": "waterway-small", + "type": "line", + "source": "composite", + "source-layer": "waterway", + "minzoom": 13, + "filter": [ "!in", "class", "canal", "river" ], + "layout": { "line-join": "round", "line-cap": "round" }, + "paint": { + "line-color": "hsl(205, 87%, 76%)", + "line-width": { "base": 1.35, "stops": [ [ 13.5, 0.1 ], [ 20, 3 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 13, 0 ], [ 13.5, 1 ] ] } + } + }, + { + "id": "water-shadow", + "type": "fill", + "source": "composite", + "source-layer": "water", + "layout": {}, + "paint": { + "fill-color": "hsl(215, 84%, 69%)", + "fill-translate": { "base": 1.2, "stops": [ [ 7, [ 0, 0 ] ], [ 16, [ -1, -1 ] ] ] }, + "fill-translate-anchor": "viewport", + "fill-opacity": 1 + } + }, + { "id": "water", "ref": "water-shadow", "paint": { "fill-color": "hsl(196, 80%, 70%)" } }, + { + "id": "barrier_line-land-polygon", + "type": "fill", + "source": "composite", + "source-layer": "barrier_line", + "filter": [ "all", [ "==", "$type", "Polygon" ], [ "==", "class", "land" ] ], + "layout": {}, + "paint": { "fill-color": "hsl(35, 12%, 89%)" } + }, + { + "id": "barrier_line-land-line", + "type": "line", + "source": "composite", + "source-layer": "barrier_line", + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "class", "land" ] ], + "layout": { "line-cap": "round" }, + "paint": { + "line-width": { "base": 1.99, "stops": [ [ 14, 0.75 ], [ 20, 40 ] ] }, + "line-color": "hsl(35, 12%, 89%)" + } + }, + { + "id": "aeroway-polygon", + "type": "fill", + "metadata": { "mapbox:group": "1444934828655.3389" }, + "source": "composite", + "source-layer": "aeroway", + "minzoom": 11, + "filter": [ "all", [ "==", "$type", "Polygon" ], [ "!=", "type", "apron" ] ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15, "hsl(230, 23%, 82%)" ], [ 16, "hsl(230, 37%, 84%)" ] ] }, + "fill-opacity": { "base": 1, "stops": [ [ 11, 0 ], [ 11.5, 1 ] ] } + } + }, + { + "id": "aeroway-runway", + "type": "line", + "metadata": { "mapbox:group": "1444934828655.3389" }, + "source": "composite", + "source-layer": "aeroway", + "minzoom": 9, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "runway" ] ], + "layout": {}, + "paint": { + "line-color": { "base": 1, "stops": [ [ 15, "hsl(230, 23%, 82%)" ], [ 16, "hsl(230, 37%, 84%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 9, 1 ], [ 18, 80 ] ] } + } + }, + { + "id": "aeroway-taxiway", + "type": "line", + "metadata": { "mapbox:group": "1444934828655.3389" }, + "source": "composite", + "source-layer": "aeroway", + "minzoom": 9, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "taxiway" ] ], + "layout": {}, + "paint": { + "line-color": { "base": 1, "stops": [ [ 15, "hsl(230, 23%, 82%)" ], [ 16, "hsl(230, 37%, 84%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 10, 0.5 ], [ 18, 20 ] ] } + } + }, + { + "id": "building-line", + "type": "line", + "source": "composite", + "source-layer": "building", + "minzoom": 15, + "filter": [ "all", [ "!=", "type", "building:part" ], [ "==", "underground", "false" ] ], + "layout": {}, + "paint": { + "line-color": "hsl(35, 6%, 79%)", + "line-width": { "base": 1.5, "stops": [ [ 15, 0.75 ], [ 20, 3 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 15.5, 0 ], [ 16, 1 ] ] } + } + }, + { + "id": "building", + "type": "fill", + "source": "composite", + "source-layer": "building", + "minzoom": 15, + "filter": [ "all", [ "!=", "type", "building:part" ], [ "==", "underground", "false" ] ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 15, "hsl(35, 11%, 88%)" ], [ 16, "hsl(35, 8%, 85%)" ] ] }, + "fill-opacity": { "base": 1, "stops": [ [ 15.5, 0 ], [ 16, 1 ] ] }, + "fill-outline-color": "hsl(35, 6%, 79%)" + } + }, + { + "id": "tunnel-street-low", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "tunnel-street_limited-low", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "tunnel-service-link-track-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "service", "track" ], + [ "==", "structure", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 19%, 75%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-street_limited-case", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 19%, 75%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-dasharray": [ 3, 3 ], + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "tunnel-street-case", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 19%, 75%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-dasharray": [ 3, 3 ], + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "tunnel-secondary-tertiary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "in", "class", "secondary", "tertiary" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.2, "stops": [ [ 10, 0.75 ], [ 18, 2 ] ] }, + "line-dasharray": [ 3, 3 ], + "line-gap-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-color": "hsl(230, 19%, 75%)" + } + }, + { + "id": "tunnel-primary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "primary" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-dasharray": [ 3, 3 ], + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(230, 19%, 75%)" + } + }, + { + "id": "tunnel-trunk_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "tunnel" ], [ "==", "type", "trunk_link" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-motorway_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "motorway_link" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-trunk-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "tunnel" ], [ "==", "type", "trunk" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-opacity": 1, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-motorway-case", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "motorway" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-opacity": 1, + "line-dasharray": [ 3, 3 ] + } + }, + { + "id": "tunnel-construction", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "construction" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-join": "miter" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "line-dasharray": { + "base": 1, + "stops": [ + [ 14, [ 0.4, 0.8 ] ], + [ 15, [ 0.3, 0.6 ] ], + [ 16, [ 0.2, 0.3 ] ], + [ 17, [ 0.2, 0.25 ] ], + [ 18, [ 0.15, 0.15 ] ] + ] + } + } + }, + { + "id": "tunnel-path", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "path" ], + [ "==", "structure", "tunnel" ], + [ "!=", "type", "steps" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] }, + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ] + }, + "line-color": "hsl(35, 26%, 95%)", + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "tunnel-steps", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "tunnel" ], [ "==", "type", "steps" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 16, 1.6 ], [ 18, 6 ] ] }, + "line-color": "hsl(35, 26%, 95%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 0.3, 0.3 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "tunnel-trunk_link", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-trunk_link-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(46, 77%, 78%)", + "line-opacity": 1, + "line-dasharray": [ 1, 0 ] + } + }, + { + "id": "tunnel-motorway_link", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-motorway_link-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(26, 100%, 78%)", + "line-opacity": 1, + "line-dasharray": [ 1, 0 ] + } + }, + { + "id": "tunnel-pedestrian", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "pedestrian" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": { "base": 1, "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.5, 0.4 ] ], [ 16, [ 1, 0.2 ] ] ] } + } + }, + { + "id": "tunnel-service-link-track", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-service-link-track-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": [ 1, 0 ] + } + }, + { + "id": "tunnel-street_limited", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(35, 14%, 93%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "tunnel-street", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "tunnel-secondary-tertiary", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-secondary-tertiary-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": [ 1, 0 ], + "line-blur": 0 + } + }, + { + "id": "tunnel-primary", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-primary-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": [ 1, 0 ], + "line-blur": 0 + } + }, + { + "id": "tunnel-oneway-arrows-blue-minor", + "type": "symbol", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "path", "pedestrian", "service", "track" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 17, "oneway-small" ], [ 18, "oneway-large" ] ] }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "tunnel-oneway-arrows-blue-major", + "type": "symbol", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "primary", "secondary", "street", "street_limited", "tertiary" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-small" ], [ 17, "oneway-large" ] ] }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "tunnel-trunk", + "type": "line", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "trunk" ], [ "==", "structure", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(46, 77%, 78%)" + } + }, + { + "id": "tunnel-motorway", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "ref": "tunnel-motorway-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-opacity": 1, + "line-color": "hsl(26, 100%, 78%)", + "line-blur": 0 + } + }, + { + "id": "tunnel-oneway-arrows-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444855769305.6016" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "motorway", "motorway_link", "trunk" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "tunnel" ], + [ "!in", "type", "primary_link", "secondary_link", "tertiary_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-white-small" ], [ 17, "oneway-white-large" ] ] }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "ferry", + "type": "line", + "source": "composite", + "source-layer": "road", + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "ferry" ] ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": { "base": 1, "stops": [ [ 15, "hsl(205, 73%, 63%)" ], [ 17, "hsl(230, 73%, 63%)" ] ] }, + "line-opacity": 1, + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] }, + "line-dasharray": { "base": 1, "stops": [ [ 12, [ 1, 0 ] ], [ 13, [ 12, 4 ] ] ] } + } + }, + { + "id": "ferry_auto", + "type": "line", + "source": "composite", + "source-layer": "road", + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "ferry_auto" ] ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": { "base": 1, "stops": [ [ 15, "hsl(205, 73%, 63%)" ], [ 17, "hsl(230, 73%, 63%)" ] ] }, + "line-opacity": 1, + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] } + } + }, + { + "id": "road-path-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "path" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!in", "type", "crossing", "sidewalk", "steps" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 18, 7 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-color": "hsl(230, 17%, 82%)", + "line-blur": 0, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 0.75 ] ] } + } + }, + { + "id": "road-steps-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "!in", "structure", "bridge", "tunnel" ], [ "==", "type", "steps" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 17, 4.6 ], [ 18, 7 ] ] }, + "line-color": "hsl(230, 17%, 82%)", + "line-dasharray": [ 1, 0 ], + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 0.75 ] ] } + } + }, + { + "id": "road-sidewalk-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "!in", "structure", "bridge", "tunnel" ], + [ "in", "type", "crossing", "sidewalk" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 18, 7 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-color": "hsl(230, 17%, 82%)", + "line-blur": 0, + "line-opacity": { "base": 1, "stops": [ [ 16, 0 ], [ 16.25, 0.75 ] ] } + } + }, + { + "id": "turning-features-outline", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ "all", [ "==", "$type", "Point" ], [ "in", "class", "turning_circle", "turning_loop" ] ], + "layout": { + "icon-image": "turning-circle-outline", + "icon-size": { "base": 1.5, "stops": [ [ 14, 0.122 ], [ 18, 0.969 ], [ 20, 1 ] ] }, + "icon-allow-overlap": true, + "icon-ignore-placement": true, + "icon-padding": 0, + "icon-rotation-alignment": "map" + }, + "paint": {} + }, + { + "id": "road-pedestrian-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "pedestrian" ], [ "==", "structure", "none" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 2 ], [ 18, 14.5 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": 0, + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-street-low", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street" ], [ "==", "structure", "none" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11, 0 ], [ 11.25, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "road-street_limited-low", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "none" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11, 0 ], [ 11.25, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "road-service-link-track-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "service", "track" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] } + } + }, + { + "id": "road-street_limited-case", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-street-case", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-secondary-tertiary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "secondary", "tertiary" ], + [ "!in", "structure", "bridge", "tunnel" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.2, "stops": [ [ 10, 0.75 ], [ 18, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 9.99, 0 ], [ 10, 1 ] ] } + } + }, + { + "id": "road-primary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "primary" ], [ "!in", "structure", "bridge", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 9.99, 0 ], [ 10, 1 ] ] } + } + }, + { + "id": "road-motorway_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "motorway_link" ], [ "!in", "structure", "bridge", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] } + } + }, + { + "id": "road-trunk_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "!in", "structure", "bridge", "tunnel" ], [ "==", "type", "trunk_link" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] } + } + }, + { + "id": "road-trunk-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "trunk" ], [ "!in", "structure", "bridge", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 6, 0 ], [ 6.1, 1 ] ] } + } + }, + { + "id": "road-motorway-case", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "motorway" ], [ "!in", "structure", "bridge", "tunnel" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "road-construction", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "construction" ], [ "==", "structure", "none" ] ] + ], + "layout": { "line-join": "miter" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "line-dasharray": { + "base": 1, + "stops": [ + [ 14, [ 0.4, 0.8 ] ], + [ 15, [ 0.3, 0.6 ] ], + [ 16, [ 0.2, 0.3 ] ], + [ 17, [ 0.2, 0.25 ] ], + [ 18, [ 0.15, 0.15 ] ] + ] + } + } + }, + { + "id": "road-sidewalks", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-sidewalk-bg", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 16, 0 ], [ 16.25, 1 ] ] } + } + }, + { + "id": "road-path", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-path-bg", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "road-steps", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-steps-bg", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 16, 1.6 ], [ 18, 6 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 0.3, 0.3 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "road-trunk_link", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-trunk_link-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(46, 85%, 67%)", + "line-opacity": 1 + } + }, + { + "id": "road-motorway_link", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-motorway_link-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(26, 100%, 68%)", + "line-opacity": 1 + } + }, + { + "id": "road-pedestrian", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-pedestrian-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": { "base": 1, "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.5, 0.4 ] ], [ 16, [ 1, 0.2 ] ] ] } + } + }, + { + "id": "road-pedestrian-polygon-fill", + "type": "fill", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + [ "==", "$type", "Polygon" ], + [ "all", [ "in", "class", "path", "pedestrian" ], [ "==", "structure", "none" ] ] + ], + "layout": {}, + "paint": { + "fill-color": { "base": 1, "stops": [ [ 16, "hsl(230, 16%, 94%)" ], [ 16.25, "hsl(230, 50%, 98%)" ] ] }, + "fill-outline-color": "hsl(230, 26%, 88%)", + "fill-opacity": 1 + } + }, + { + "id": "road-pedestrian-polygon-pattern", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-pedestrian-polygon-fill", + "paint": { + "fill-color": "hsl(0, 0%, 100%)", + "fill-outline-color": "hsl(35, 10%, 83%)", + "fill-pattern": "pedestrian-polygon", + "fill-opacity": { "base": 1, "stops": [ [ 16, 0 ], [ 16.25, 1 ] ] } + } + }, + { + "id": "road-polygon", + "type": "fill", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + [ "==", "$type", "Polygon" ], + [ + "all", + [ "!in", "class", "motorway", "path", "pedestrian", "trunk" ], + [ "!in", "structure", "bridge", "tunnel" ] + ] + ], + "layout": {}, + "paint": { "fill-color": "hsl(0, 0%, 100%)", "fill-outline-color": "#d6d9e6" } + }, + { + "id": "road-service-link-track", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-service-link-track-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "road-street_limited", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(35, 14%, 93%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-street", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "road-secondary-tertiary", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-secondary-tertiary-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-color": { "base": 1, "stops": [ [ 5, "hsl(35, 32%, 91%)" ], [ 8, "hsl(0, 0%, 100%)" ] ] }, + "line-opacity": { "base": 1.2, "stops": [ [ 5, 0 ], [ 5.5, 1 ] ] } + } + }, + { + "id": "road-primary", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-primary-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": { "base": 1, "stops": [ [ 5, "hsl(35, 32%, 91%)" ], [ 7, "hsl(0, 0%, 100%)" ] ] }, + "line-opacity": 1 + } + }, + { + "id": "road-oneway-arrows-blue-minor", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "path", "pedestrian", "service", "track" ], + [ "==", "oneway", "true" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 17, "oneway-small" ], [ 18, "oneway-large" ] ] }, + "icon-rotation-alignment": "map", + "icon-padding": 2, + "symbol-spacing": 200 + }, + "paint": {} + }, + { + "id": "road-oneway-arrows-blue-major", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "primary", "secondary", "street", "street_limited", "tertiary" ], + [ "==", "oneway", "true" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-small" ], [ 17, "oneway-large" ] ] }, + "icon-rotation-alignment": "map", + "icon-padding": 2, + "symbol-spacing": 200 + }, + "paint": {} + }, + { + "id": "road-trunk", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-trunk-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": { + "base": 1, + "stops": [ [ 6, "hsl(0, 0%, 100%)" ], [ 6.1, "hsl(46, 80%, 60%)" ], [ 9, "hsl(46, 85%, 67%)" ] ] + } + } + }, + { + "id": "road-motorway", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-motorway-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": { "base": 1, "stops": [ [ 8, "hsl(26, 87%, 62%)" ], [ 9, "hsl(26, 100%, 68%)" ] ] } + } + }, + { + "id": "road-rail", + "type": "line", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "major_rail", "minor_rail" ], + [ "!in", "structure", "bridge", "tunnel" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] } + } + }, + { + "id": "road-rail-tracks", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "ref": "road-rail", + "paint": { + "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 14, 4 ], [ 20, 8 ] ] }, + "line-dasharray": [ 0.1, 15 ], + "line-opacity": { "base": 1, "stops": [ [ 13.75, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "level-crossings", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ "all", [ "==", "$type", "Point" ], [ "==", "class", "level_crossing" ] ], + "layout": { "icon-size": 1, "icon-image": "level-crossing", "icon-allow-overlap": true }, + "paint": {} + }, + { + "id": "road-oneway-arrows-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "motorway", "motorway_link", "trunk" ], + [ "==", "oneway", "true" ], + [ "!in", "structure", "bridge", "tunnel" ], + [ "!in", "type", "primary_link", "secondary_link", "tertiary_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-white-small" ], [ 17, "oneway-white-large" ] ] }, + "icon-padding": 2, + "symbol-spacing": 200 + }, + "paint": {} + }, + { + "id": "turning-features", + "type": "symbol", + "metadata": { "mapbox:group": "1444855786460.0557" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ "all", [ "==", "$type", "Point" ], [ "in", "class", "turning_circle", "turning_loop" ] ], + "layout": { + "icon-image": "turning-circle", + "icon-size": { "base": 1.5, "stops": [ [ 14, 0.095 ], [ 18, 1 ] ] }, + "icon-allow-overlap": true, + "icon-ignore-placement": true, + "icon-padding": 0, + "icon-rotation-alignment": "map" + }, + "paint": {} + }, + { + "id": "bridge-path-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "path" ], + [ "==", "structure", "bridge" ], + [ "!=", "type", "steps" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 18, 7 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-color": "hsl(230, 17%, 82%)", + "line-blur": 0, + "line-opacity": { "base": 1, "stops": [ [ 15, 0 ], [ 15.25, 1 ] ] } + } + }, + { + "id": "bridge-steps-bg", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "bridge" ], [ "==", "type", "steps" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 17, 4.6 ], [ 18, 7 ] ] }, + "line-color": "hsl(230, 17%, 82%)", + "line-dasharray": [ 1, 0 ], + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 0.75 ] ] } + } + }, + { + "id": "bridge-pedestrian-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "pedestrian" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 2 ], [ 18, 14.5 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": 0, + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "bridge-street-low", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "bridge-street_limited-low", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] } + } + }, + { + "id": "bridge-service-link-track-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "service", "track" ], + [ "==", "structure", "bridge" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] } + } + }, + { + "id": "bridge-street_limited-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] } + } + }, + { + "id": "bridge-street-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "street" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] } + } + }, + { + "id": "bridge-secondary-tertiary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "in", "class", "secondary", "tertiary" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.2, "stops": [ [ 10, 0.75 ], [ 18, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-translate": [ 0, 0 ] + } + }, + { + "id": "bridge-primary-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "primary" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-translate": [ 0, 0 ] + } + }, + { + "id": "bridge-trunk_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ], + [ "==", "type", "trunk_link" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] } + } + }, + { + "id": "bridge-motorway_link-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway_link" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": 1 + } + }, + { + "id": "bridge-trunk-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "trunk" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "bridge-motorway-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "bridge-construction", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "class", "construction" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "miter" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(230, 24%, 87%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "line-dasharray": { + "base": 1, + "stops": [ + [ 14, [ 0.4, 0.8 ] ], + [ 15, [ 0.3, 0.6 ] ], + [ 16, [ 0.2, 0.3 ] ], + [ 17, [ 0.2, 0.25 ] ], + [ 18, [ 0.15, 0.15 ] ] + ] + } + } + }, + { + "id": "bridge-path", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "path" ], + [ "==", "structure", "bridge" ], + [ "!=", "type", "steps" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "bridge-steps", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-steps-bg", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 16, 1.6 ], [ 18, 6 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-dasharray": { + "base": 1, + "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 0.3, 0.3 ] ] ] + }, + "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] } + } + }, + { + "id": "bridge-trunk_link", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ], + [ "==", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(46, 85%, 67%)" + } + }, + { + "id": "bridge-motorway_link", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway_link" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(26, 100%, 68%)" + } + }, + { + "id": "bridge-pedestrian", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-pedestrian-case", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1, + "line-dasharray": { "base": 1, "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.5, 0.4 ] ], [ 16, [ 1, 0.2 ] ] ] } + } + }, + { + "id": "bridge-service-link-track", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "service", "track" ], + [ "==", "structure", "bridge" ], + [ "!=", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }, + "line-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "bridge-street_limited", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-street_limited-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(35, 14%, 93%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "bridge-street", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-street-low", + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] } + } + }, + { + "id": "bridge-secondary-tertiary", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "bridge" ], [ "in", "type", "secondary", "tertiary" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": { "base": 1.2, "stops": [ [ 5, 0 ], [ 5.5, 1 ] ] } + } + }, + { + "id": "bridge-primary", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "==", "structure", "bridge" ], [ "==", "type", "primary" ] ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-opacity": 1 + } + }, + { + "id": "bridge-oneway-arrows-blue-minor", + "type": "symbol", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "path", "pedestrian", "service", "track" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 17, "oneway-small" ], [ 18, "oneway-large" ] ] }, + "symbol-spacing": 200, + "icon-rotation-alignment": "map", + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "bridge-oneway-arrows-blue-major", + "type": "symbol", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "primary", "secondary", "street", "street_limited", "tertiary" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-small" ], [ 17, "oneway-large" ] ] }, + "symbol-spacing": 200, + "icon-rotation-alignment": "map", + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "bridge-trunk", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "trunk" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(46, 85%, 67%)" + } + }, + { + "id": "bridge-motorway", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway" ], + [ "!in", "layer", 2, 3, 4, 5 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(26, 100%, 68%)" + } + }, + { + "id": "bridge-rail", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "all", [ "in", "class", "major_rail", "minor_rail" ], [ "==", "structure", "bridge" ] ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] } + } + }, + { + "id": "bridge-rail-tracks", + "metadata": { "mapbox:group": "1444855799204.86" }, + "ref": "bridge-rail", + "paint": { + "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] }, + "line-width": { "base": 1.5, "stops": [ [ 14, 4 ], [ 20, 8 ] ] }, + "line-dasharray": [ 0.1, 15 ], + "line-opacity": { "base": 1, "stops": [ [ 13.75, 0 ], [ 20, 1 ] ] } + } + }, + { + "id": "bridge-trunk_link-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ], + [ "==", "type", "trunk_link" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] } + } + }, + { + "id": "bridge-motorway_link-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway_link" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-opacity": 1 + } + }, + { + "id": "bridge-trunk-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "trunk" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "bridge-motorway-2-case", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] }, + "line-color": "hsl(0, 0%, 100%)", + "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] } + } + }, + { + "id": "bridge-trunk_link-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ], + [ "==", "type", "trunk_link" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(46, 85%, 67%)" + } + }, + { + "id": "bridge-motorway_link-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway_link" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] }, + "line-color": "hsl(26, 100%, 68%)" + } + }, + { + "id": "bridge-trunk-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "trunk" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(46, 85%, 67%)" + } + }, + { + "id": "bridge-motorway-2", + "type": "line", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "==", "class", "motorway" ], + [ ">=", "layer", 2 ], + [ "==", "structure", "bridge" ] + ] + ], + "layout": { "line-cap": "round", "line-join": "round" }, + "paint": { + "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }, + "line-color": "hsl(26, 100%, 68%)" + } + }, + { + "id": "bridge-oneway-arrows-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444855799204.86" }, + "source": "composite", + "source-layer": "road", + "minzoom": 16, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "all", + [ "in", "class", "link", "motorway", "motorway_link", "trunk" ], + [ "==", "oneway", "true" ], + [ "==", "structure", "bridge" ], + [ "!in", "type", "primary_link", "secondary_link", "tertiary_link" ] + ] + ], + "layout": { + "symbol-placement": "line", + "icon-image": { "base": 1, "stops": [ [ 16, "oneway-white-small" ], [ 17, "oneway-white-large" ] ] }, + "symbol-spacing": 200, + "icon-padding": 2 + }, + "paint": {} + }, + { + "id": "aerialway", + "type": "line", + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "class", "aerialway" ] ], + "layout": { "line-join": "round" }, + "paint": { + "line-color": "hsl(230, 10%, 74%)", + "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] } + } + }, + { + "id": "admin-3-4-boundaries-bg", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "filter": [ "all", [ ">=", "admin_level", 3 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "bevel" }, + "paint": { + "line-color": { "base": 1, "stops": [ [ 8, "hsl(35, 12%, 89%)" ], [ 16, "hsl(230, 49%, 90%)" ] ] }, + "line-width": { "base": 1, "stops": [ [ 7, 3.75 ], [ 12, 5.5 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 7, 0 ], [ 8, 0.75 ] ] }, + "line-dasharray": [ 1, 0 ], + "line-translate": [ 0, 0 ], + "line-blur": { "base": 1, "stops": [ [ 3, 0 ], [ 8, 3 ] ] } + } + }, + { + "id": "admin-2-boundaries-bg", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ "all", [ "==", "admin_level", 2 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "miter" }, + "paint": { + "line-width": { "base": 1, "stops": [ [ 3, 3.5 ], [ 10, 8 ] ] }, + "line-color": { "base": 1, "stops": [ [ 6, "hsl(35, 12%, 89%)" ], [ 8, "hsl(230, 49%, 90%)" ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 3, 0 ], [ 4, 0.5 ] ] }, + "line-translate": [ 0, 0 ], + "line-blur": { "base": 1, "stops": [ [ 3, 0 ], [ 10, 2 ] ] } + } + }, + { + "id": "admin-3-4-boundaries", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "filter": [ "all", [ ">=", "admin_level", 3 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "round", "line-cap": "round" }, + "paint": { + "line-dasharray": { "base": 1, "stops": [ [ 6, [ 2, 0 ] ], [ 7, [ 2, 2, 6, 2 ] ] ] }, + "line-width": { "base": 1, "stops": [ [ 7, 0.75 ], [ 12, 1.5 ] ] }, + "line-opacity": { "base": 1, "stops": [ [ 2, 0 ], [ 3, 1 ] ] }, + "line-color": { "base": 1, "stops": [ [ 3, "hsl(230, 14%, 77%)" ], [ 7, "hsl(230, 8%, 62%)" ] ] } + } + }, + { + "id": "admin-2-boundaries", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ "all", [ "==", "admin_level", 2 ], [ "==", "disputed", 0 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "round", "line-cap": "round" }, + "paint": { + "line-color": "hsl(230, 8%, 51%)", + "line-width": { "base": 1, "stops": [ [ 3, 0.5 ], [ 10, 2 ] ] } + } + }, + { + "id": "admin-2-boundaries-dispute", + "type": "line", + "metadata": { "mapbox:group": "1444934295202.7542" }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ "all", [ "==", "admin_level", 2 ], [ "==", "disputed", 1 ], [ "==", "maritime", 0 ] ], + "layout": { "line-join": "round" }, + "paint": { + "line-dasharray": [ 1.5, 1.5 ], + "line-color": "hsl(230, 8%, 51%)", + "line-width": { "base": 1, "stops": [ [ 3, 0.5 ], [ 10, 2 ] ] } + } + }, + { + "id": "housenum-label", + "type": "symbol", + "source": "composite", + "source-layer": "housenum_label", + "minzoom": 17, + "layout": { + "text-field": "{house_num}", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-padding": 4, + "text-max-width": 7, + "text-size": 9.5 + }, + "paint": { + "text-color": "hsl(35, 2%, 69%)", + "text-halo-color": "hsl(35, 8%, 85%)", + "text-halo-width": 0.5, + "text-halo-blur": 0 + } + }, + { + "id": "waterway-label", + "type": "symbol", + "source": "composite", + "source-layer": "waterway_label", + "minzoom": 12, + "filter": [ "in", "class", "canal", "river" ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-max-angle": 30, + "text-size": { "base": 1, "stops": [ [ 13, 12 ], [ 18, 16 ] ] } + }, + "paint": { + "text-halo-width": 0.5, + "text-halo-color": "hsl(196, 80%, 70%)", + "text-color": "hsl(230, 48%, 44%)", + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-scalerank4-l15", + "type": "symbol", + "metadata": { "mapbox:group": "1444933456003.5437" }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 17, + "filter": [ + "all", + [ ">=", "localrank", 15 ], + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 4 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-scalerank4-l1", + "type": "symbol", + "metadata": { "mapbox:group": "1444933456003.5437" }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 15, + "filter": [ + "all", + [ "<=", "localrank", 14 ], + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 4 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 1, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-parks_scalerank4", + "type": "symbol", + "metadata": { "mapbox:group": "1444933456003.5437" }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 15, + "filter": [ + "all", + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 4 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 1, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(100, 100%, 20%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-scalerank3", + "type": "symbol", + "metadata": { "mapbox:group": "1444933372896.5967" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 3 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 1, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-parks-scalerank3", + "type": "symbol", + "metadata": { "mapbox:group": "1444933372896.5967" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 3 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{maki}-11", + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(100, 100%, 20%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "road-label-small", + "type": "symbol", + "metadata": { "mapbox:group": "1444933721429.3076" }, + "source": "composite", + "source-layer": "road_label", + "minzoom": 15, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ + "!in", + "class", + "link", + "motorway", + "pedestrian", + "primary", + "secondary", + "street", + "street_limited", + "tertiary", + "trunk" + ] + ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 15, 10 ], [ 20, 13 ] ] }, + "text-max-angle": 30, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-padding": 1, + "text-rotation-alignment": "map", + "text-field": "{name_en}", + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "text-halo-blur": 1 + } + }, + { + "id": "road-label-medium", + "type": "symbol", + "metadata": { "mapbox:group": "1444933721429.3076" }, + "source": "composite", + "source-layer": "road_label", + "minzoom": 11, + "filter": [ + "all", + [ "==", "$type", "LineString" ], + [ "in", "class", "link", "pedestrian", "street", "street_limited" ] + ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 11, 10 ], [ 20, 14 ] ] }, + "text-max-angle": 30, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-padding": 1, + "text-rotation-alignment": "map", + "text-field": ["format", ["get", "name_en"], {"text-color": "green"}], + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "road-label-large", + "type": "symbol", + "metadata": { "mapbox:group": "1444933721429.3076" }, + "source": "composite", + "source-layer": "road_label", + "filter": [ "in", "class", "motorway", "primary", "secondary", "tertiary", "trunk" ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 9, 10 ], [ 20, 16 ] ] }, + "text-max-angle": 30, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-padding": 1, + "text-rotation-alignment": "map", + "text-field": "{name_en}", + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsla(0, 0%, 100%, 0.75)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "road-shields-black", + "type": "symbol", + "metadata": { "mapbox:group": "1444933575858.6992" }, + "source": "composite", + "source-layer": "road_label", + "filter": [ + "all", + [ "<=", "reflen", 6 ], + [ + "!in", + "shield", + "at-expressway", + "at-motorway", + "at-state-b", + "bg-motorway", + "bg-national", + "ch-main", + "ch-motorway", + "cz-motorway", + "cz-road", + "de-motorway", + "e-road", + "fi-main", + "gr-motorway", + "gr-national", + "hr-motorway", + "hr-state", + "hu-main", + "hu-motorway", + "nz-state", + "pl-expressway", + "pl-motorway", + "pl-national", + "ro-county", + "ro-motorway", + "ro-national", + "rs-motorway", + "rs-state-1b", + "se-main", + "si-expressway", + "si-motorway", + "sk-highway", + "sk-road", + "us-interstate", + "us-interstate-business", + "us-interstate-duplex", + "us-interstate-truck", + "za-metropolitan", + "za-national", + "za-provincial", + "za-regional" + ] + ], + "layout": { + "text-size": 9, + "icon-image": "{shield}-{reflen}", + "icon-rotation-alignment": "viewport", + "text-max-angle": 38, + "symbol-spacing": { "base": 1, "stops": [ [ 11, 150 ], [ 14, 200 ] ] }, + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "symbol-placement": { "base": 1, "stops": [ [ 10, "point" ], [ 11, "line" ] ] }, + "text-padding": 2, + "text-rotation-alignment": "viewport", + "text-field": "{ref}", + "text-letter-spacing": 0.05, + "icon-padding": 2 + }, + "paint": { + "text-color": "hsl(0, 0%, 7%)", + "icon-halo-color": "rgba(0, 0, 0, 1)", + "icon-halo-width": 1, + "text-opacity": 1, + "icon-color": "white", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0 + } + }, + { + "id": "road-shields-white", + "type": "symbol", + "metadata": { "mapbox:group": "1444933575858.6992" }, + "source": "composite", + "source-layer": "road_label", + "filter": [ + "all", + [ "<=", "reflen", 6 ], + [ + "in", + "shield", + "at-expressway", + "at-motorway", + "at-state-b", + "bg-motorway", + "bg-national", + "ch-main", + "ch-motorway", + "cz-motorway", + "cz-road", + "de-motorway", + "e-road", + "fi-main", + "gr-motorway", + "gr-national", + "hr-motorway", + "hr-state", + "hu-main", + "hu-motorway", + "nz-state", + "pl-expressway", + "pl-motorway", + "pl-national", + "ro-county", + "ro-motorway", + "ro-national", + "rs-motorway", + "rs-state-1b", + "se-main", + "si-expressway", + "si-motorway", + "sk-highway", + "sk-road", + "us-interstate", + "us-interstate-business", + "us-interstate-duplex", + "us-interstate-truck", + "za-metropolitan", + "za-national", + "za-provincial", + "za-regional" + ] + ], + "layout": { + "text-size": 9, + "icon-image": "{shield}-{reflen}", + "icon-rotation-alignment": "viewport", + "text-max-angle": 38, + "symbol-spacing": { "base": 1, "stops": [ [ 11, 150 ], [ 14, 200 ] ] }, + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "symbol-placement": { "base": 1, "stops": [ [ 10, "point" ], [ 11, "line" ] ] }, + "text-padding": 2, + "text-rotation-alignment": "viewport", + "text-field": "{ref}", + "text-letter-spacing": 0.05, + "icon-padding": 2 + }, + "paint": { + "text-color": "hsl(0, 0%, 100%)", + "icon-halo-color": "rgba(0, 0, 0, 1)", + "icon-halo-width": 1, + "text-opacity": 1, + "icon-color": "white", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0 + } + }, + { + "id": "motorway-junction", + "type": "symbol", + "metadata": { "mapbox:group": "1444933575858.6992" }, + "source": "composite", + "source-layer": "motorway_junction", + "minzoom": 14, + "filter": [ "all", [ ">", "reflen", 0 ], [ "<=", "reflen", 9 ] ], + "layout": { + "text-field": "{ref}", + "text-size": 9, + "icon-image": "motorway-exit-{reflen}", + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ] + }, + "paint": { "text-color": "hsl(0, 0%, 100%)", "text-translate": [ 0, 0 ] } + }, + { + "id": "poi-scalerank2", + "type": "symbol", + "metadata": { "mapbox:group": "1444933358918.2366" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 2 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 14, 11 ], [ 20, 14 ] ] }, + "icon-image": { "stops": [ [ 14, "{maki}-11" ], [ 15, "{maki}-15" ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-parks-scalerank2", + "type": "symbol", + "metadata": { "mapbox:group": "1444933358918.2366" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "==", "scalerank", 2 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 14, 11 ], [ 20, 14 ] ] }, + "icon-image": { "stops": [ [ 14, "{maki}-11" ], [ 15, "{maki}-15" ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(100, 100%, 20%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "rail-label", + "type": "symbol", + "source": "composite", + "source-layer": "rail_station_label", + "minzoom": 12, + "filter": [ "!=", "maki", "entrance" ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] }, + "icon-image": "{network}", + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-offset": [ 0, 0.85 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": { "base": 1, "stops": [ [ 0, "" ], [ 13, "{name_en}" ] ] }, + "text-letter-spacing": 0.01, + "icon-padding": 0, + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(230, 48%, 44%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "icon-halo-width": 4, + "icon-halo-color": "#fff", + "text-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }, + "text-halo-blur": 0.5 + } + }, + { + "id": "water-label-sm", + "type": "symbol", + "metadata": { "mapbox:group": "1444933808272.805" }, + "source": "composite", + "source-layer": "water_label", + "minzoom": 15, + "filter": [ "<=", "area", 10000 ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-max-width": 7, + "text-size": { "base": 1, "stops": [ [ 16, 13 ], [ 20, 16 ] ] } + }, + "paint": { "text-color": "hsl(230, 48%, 44%)" } + }, + { + "id": "water-label", + "type": "symbol", + "metadata": { "mapbox:group": "1444933808272.805" }, + "source": "composite", + "source-layer": "water_label", + "minzoom": 5, + "filter": [ ">", "area", 10000 ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-max-width": 7, + "text-size": { "base": 1, "stops": [ [ 13, 13 ], [ 18, 18 ] ] } + }, + "paint": { "text-color": "hsl(230, 48%, 44%)" } + }, + { + "id": "place-residential", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "maxzoom": 18, + "filter": [ + "all", + [ "in", "$type", "LineString", "Point", "Polygon" ], + [ "all", [ "<=", "localrank", 10 ], [ "==", "type", "residential" ] ] + ], + "layout": { + "text-line-height": 1.2, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 14 ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0 ], + "text-rotation-alignment": "viewport", + "text-field": "{name_en}", + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-parks-scalerank1", + "type": "symbol", + "metadata": { "mapbox:group": "1444933322393.2852" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "<=", "scalerank", 1 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 14 ] ] }, + "icon-image": { "stops": [ [ 13, "{maki}-11" ], [ 14, "{maki}-15" ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(100, 100%, 20%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "poi-scalerank1", + "type": "symbol", + "metadata": { "mapbox:group": "1444933322393.2852" }, + "source": "composite", + "source-layer": "poi_label", + "filter": [ + "all", + [ + "!in", + "maki", + "campsite", + "cemetery", + "dog-park", + "garden", + "golf", + "park", + "picnic-site", + "playground", + "zoo" + ], + [ "<=", "scalerank", 1 ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 14 ] ] }, + "icon-image": { "stops": [ [ 13, "{maki}-11" ], [ 14, "{maki}-15" ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.65 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(26, 25%, 32%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "airport-label", + "type": "symbol", + "source": "composite", + "source-layer": "airport_label", + "minzoom": 9, + "filter": [ "<=", "scalerank", 2 ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 10, 12 ], [ 18, 18 ] ] }, + "icon-image": { "stops": [ [ 12, "{maki}-11" ], [ 13, "{maki}-15" ] ] }, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0.75 ], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": { "stops": [ [ 11, "{ref}" ], [ 12, "{name_en}" ] ] }, + "text-letter-spacing": 0.01, + "text-max-width": 9 + }, + "paint": { + "text-color": "hsl(230, 48%, 44%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5 + } + }, + { + "id": "place-islet-archipelago-aboriginal", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "maxzoom": 16, + "filter": [ "in", "type", "aboriginal_lands", "archipelago", "islet" ], + "layout": { + "text-line-height": 1.2, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 16 ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0 ], + "text-rotation-alignment": "viewport", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 8 + }, + "paint": { + "text-color": "hsl(230, 29%, 35%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "place-neighbourhood", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 10, + "maxzoom": 16, + "filter": [ "==", "type", "neighbourhood" ], + "layout": { + "text-field": "{name_en}", + "text-transform": "uppercase", + "text-letter-spacing": 0.1, + "text-max-width": 7, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-padding": 3, + "text-size": { "base": 1, "stops": [ [ 12, 11 ], [ 16, 16 ] ] } + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "text-color": "hsl(230, 29%, 35%)", + "text-halo-blur": 0.5 + } + }, + { + "id": "place-suburb", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 10, + "maxzoom": 16, + "filter": [ "==", "type", "suburb" ], + "layout": { + "text-field": "{name_en}", + "text-transform": "uppercase", + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-letter-spacing": 0.15, + "text-max-width": 7, + "text-padding": 3, + "text-size": { "base": 1, "stops": [ [ 11, 11 ], [ 15, 18 ] ] } + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "text-color": "hsl(230, 29%, 35%)", + "text-halo-blur": 0.5 + } + }, + { + "id": "place-hamlet", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 10, + "maxzoom": 16, + "filter": [ "==", "type", "hamlet" ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 12, 11.5 ], [ 15, 16 ] ] } + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "text-color": "hsl(0, 0%, 0%)" + } + }, + { + "id": "place-village", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 8, + "maxzoom": 15, + "filter": [ "==", "type", "village" ], + "layout": { + "text-field": "{name_en}", + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-max-width": 7, + "text-size": { "base": 1, "stops": [ [ 10, 11.5 ], [ 16, 18 ] ] } + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "text-color": "hsl(0, 0%, 0%)" + } + }, + { + "id": "place-town", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "minzoom": 6, + "maxzoom": 15, + "filter": [ "==", "type", "town" ], + "layout": { + "icon-image": "dot-9", + "text-font": { + "base": 1, + "stops": [ + [ 11, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 12, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7, [ 0, -0.15 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7, + "text-size": { "base": 1, "stops": [ [ 7, 11.5 ], [ 15, 20 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] } + } + }, + { + "id": "place-island", + "type": "symbol", + "source": "composite", + "source-layer": "place_label", + "maxzoom": 16, + "filter": [ "==", "type", "island" ], + "layout": { + "text-line-height": 1.2, + "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 16 ] ] }, + "text-max-angle": 38, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ], + "text-padding": 2, + "text-offset": [ 0, 0 ], + "text-rotation-alignment": "viewport", + "text-field": "{name_en}", + "text-letter-spacing": 0.01, + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(230, 29%, 35%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "place-city-sm", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "maxzoom": 14, + "filter": [ "all", [ "!in", "scalerank", 0, 1, 2, 3, 4, 5 ], [ "==", "type", "city" ] ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 6, 12 ], [ 14, 22 ] ] }, + "icon-image": "dot-9", + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, -0.2 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1.25, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] } + } + }, + { + "id": "place-city-md-s", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "maxzoom": 14, + "filter": [ + "all", + [ "in", "ldir", "E", "S", "SE", "SW" ], + [ "in", "scalerank", 3, 4, 5 ], + [ "==", "type", "city" ] + ], + "layout": { + "text-field": "{name_en}", + "icon-image": "dot-10", + "text-anchor": { "base": 1, "stops": [ [ 7, "top" ], [ 8, "center" ] ] }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, 0.1 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-size": { "base": 0.9, "stops": [ [ 5, 12 ], [ 12, 22 ] ] } + }, + "paint": { + "text-halo-width": 1, + "text-halo-color": "hsl(0, 0%, 100%)", + "text-color": "hsl(0, 0%, 0%)", + "text-halo-blur": 1, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] } + } + }, + { + "id": "place-city-md-n", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "maxzoom": 14, + "filter": [ + "all", + [ "in", "ldir", "N", "NE", "NW", "W" ], + [ "in", "scalerank", 3, 4, 5 ], + [ "==", "type", "city" ] + ], + "layout": { + "icon-image": "dot-10", + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, -0.25 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7, + "text-size": { "base": 0.9, "stops": [ [ 5, 12 ], [ 12, 22 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] }, + "text-halo-blur": 1 + } + }, + { + "id": "place-city-lg-s", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 1, + "maxzoom": 14, + "filter": [ + "all", + [ "in", "ldir", "E", "S", "SE", "SW" ], + [ "<=", "scalerank", 2 ], + [ "==", "type", "city" ] + ], + "layout": { + "icon-image": "dot-11", + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, 0.15 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "top" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7, + "text-size": { "base": 0.9, "stops": [ [ 4, 12 ], [ 10, 22 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] }, + "text-halo-blur": 1 + } + }, + { + "id": "place-city-lg-n", + "type": "symbol", + "metadata": { "mapbox:group": "1444862510685.128" }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 1, + "maxzoom": 14, + "filter": [ + "all", + [ "in", "ldir", "N", "NE", "NW", "W" ], + [ "<=", "scalerank", 2 ], + [ "==", "type", "city" ] + ], + "layout": { + "icon-image": "dot-11", + "text-font": { + "base": 1, + "stops": [ + [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ], + [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ] + ] + }, + "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, -0.25 ] ], [ 8, [ 0, 0 ] ] ] }, + "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] }, + "text-field": "{name_en}", + "text-max-width": 7, + "text-size": { "base": 0.9, "stops": [ [ 4, 12 ], [ 10, 22 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-opacity": 1, + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1, + "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] }, + "text-halo-blur": 1 + } + }, + { + "id": "marine-label-sm-ln", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 3, + "maxzoom": 10, + "filter": [ "all", [ "==", "$type", "LineString" ], [ ">=", "labelrank", 4 ] ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1, "stops": [ [ 3, 12 ], [ 6, 16 ] ] }, + "symbol-spacing": { "base": 1, "stops": [ [ 4, 100 ], [ 6, 400 ] ] }, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-field": "{name_en}", + "text-letter-spacing": 0.1, + "text-max-width": 5 + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-sm-pt", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 3, + "maxzoom": 10, + "filter": [ "all", [ "==", "$type", "Point" ], [ ">=", "labelrank", 4 ] ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 5, + "text-letter-spacing": 0.1, + "text-line-height": 1.5, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 3, 12 ], [ 6, 16 ] ] } + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-md-ln", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 2, + "maxzoom": 8, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "in", "labelrank", 2, 3 ] ], + "layout": { + "text-line-height": 1.1, + "text-size": { "base": 1.1, "stops": [ [ 2, 12 ], [ 5, 20 ] ] }, + "symbol-spacing": 250, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "symbol-placement": "line", + "text-field": "{name_en}", + "text-letter-spacing": 0.15, + "text-max-width": 5 + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-md-pt", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 2, + "maxzoom": 8, + "filter": [ "all", [ "==", "$type", "Point" ], [ "in", "labelrank", 2, 3 ] ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 5, + "text-letter-spacing": 0.15, + "text-line-height": 1.5, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-size": { "base": 1.1, "stops": [ [ 2, 14 ], [ 5, 20 ] ] } + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-lg-ln", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 1, + "maxzoom": 4, + "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "labelrank", 1 ] ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 4, + "text-letter-spacing": 0.25, + "text-line-height": 1.1, + "symbol-placement": "line", + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 1, 14 ], [ 4, 30 ] ] } + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "marine-label-lg-pt", + "type": "symbol", + "metadata": { "mapbox:group": "1444856087950.3635" }, + "source": "composite", + "source-layer": "marine_label", + "minzoom": 1, + "maxzoom": 4, + "filter": [ "all", [ "==", "$type", "Point" ], [ "==", "labelrank", 1 ] ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 4, + "text-letter-spacing": 0.25, + "text-line-height": 1.5, + "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 1, 14 ], [ 4, 30 ] ] } + }, + "paint": { "text-color": "hsl(205, 83%, 88%)" } + }, + { + "id": "state-label-sm", + "type": "symbol", + "metadata": { "mapbox:group": "1444856151690.9143" }, + "source": "composite", + "source-layer": "state_label", + "minzoom": 3, + "maxzoom": 9, + "filter": [ "<", "area", 20000 ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 6, 10 ], [ 9, 14 ] ] }, + "text-transform": "uppercase", + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "text-field": { "base": 1, "stops": [ [ 0, "{abbr}" ], [ 6, "{name_en}" ] ] }, + "text-letter-spacing": 0.15, + "text-max-width": 5 + }, + "paint": { + "text-opacity": 1, + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "state-label-md", + "type": "symbol", + "metadata": { "mapbox:group": "1444856151690.9143" }, + "source": "composite", + "source-layer": "state_label", + "minzoom": 3, + "maxzoom": 8, + "filter": [ "all", [ "<", "area", 80000 ], [ ">=", "area", 20000 ] ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 5, 10 ], [ 8, 16 ] ] }, + "text-transform": "uppercase", + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "text-field": { "base": 1, "stops": [ [ 0, "{abbr}" ], [ 5, "{name_en}" ] ] }, + "text-letter-spacing": 0.15, + "text-max-width": 6 + }, + "paint": { + "text-opacity": 1, + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "state-label-lg", + "type": "symbol", + "metadata": { "mapbox:group": "1444856151690.9143" }, + "source": "composite", + "source-layer": "state_label", + "minzoom": 3, + "maxzoom": 7, + "filter": [ ">=", "area", 80000 ], + "layout": { + "text-size": { "base": 1, "stops": [ [ 4, 10 ], [ 7, 18 ] ] }, + "text-transform": "uppercase", + "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ], + "text-padding": 1, + "text-field": { "base": 1, "stops": [ [ 0, "{abbr}" ], [ 4, "{name_en}" ] ] }, + "text-letter-spacing": 0.15, + "text-max-width": 6 + }, + "paint": { + "text-opacity": 1, + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 1 + } + }, + { + "id": "country-label-sm", + "type": "symbol", + "metadata": { "mapbox:group": "1444856144497.7825" }, + "source": "composite", + "source-layer": "country_label", + "minzoom": 1, + "maxzoom": 10, + "filter": [ ">=", "scalerank", 5 ], + "layout": { + "text-field": "{name_en}", + "text-max-width": 6, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-size": { "base": 0.9, "stops": [ [ 5, 14 ], [ 9, 22 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": { "base": 1, "stops": [ [ 2, "rgba(255,255,255,0.75)" ], [ 3, "hsl(0, 0%, 100%)" ] ] }, + "text-halo-width": 1.25 + } + }, + { + "id": "country-label-md", + "type": "symbol", + "metadata": { "mapbox:group": "1444856144497.7825" }, + "source": "composite", + "source-layer": "country_label", + "minzoom": 1, + "maxzoom": 8, + "filter": [ "in", "scalerank", 3, 4 ], + "layout": { + "text-field": { "base": 1, "stops": [ [ 0, "{code}" ], [ 2, "{name_en}" ] ] }, + "text-max-width": 6, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 3, 10 ], [ 8, 24 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": { "base": 1, "stops": [ [ 2, "rgba(255,255,255,0.75)" ], [ 3, "hsl(0, 0%, 100%)" ] ] }, + "text-halo-width": 1.25 + } + }, + { + "id": "country-label-lg", + "type": "symbol", + "metadata": { "mapbox:group": "1444856144497.7825" }, + "source": "composite", + "source-layer": "country_label", + "minzoom": 1, + "maxzoom": 7, + "filter": [ "in", "scalerank", 1, 2 ], + "layout": { + "text-field": "{name_en}", + "text-max-width": { "base": 1, "stops": [ [ 0, 5 ], [ 3, 6 ] ] }, + "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ], + "text-size": { "base": 1, "stops": [ [ 1, 10 ], [ 6, 24 ] ] } + }, + "paint": { + "text-color": "hsl(0, 0%, 0%)", + "text-halo-color": { "base": 1, "stops": [ [ 2, "rgba(255,255,255,0.75)" ], [ 3, "hsl(0, 0%, 100%)" ] ] }, + "text-halo-width": 1.25 + } + } + ] +} \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/function/camera_function.benchmark.cpp b/mapbox-gl-native/benchmark/function/camera_function.benchmark.cpp new file mode 100644 index 000000000..0ce7d7c76 --- /dev/null +++ b/mapbox-gl-native/benchmark/function/camera_function.benchmark.cpp @@ -0,0 +1,61 @@ +#include + +#include +#include +#include +#include + +using namespace mbgl; +using namespace mbgl::style; + +static std::string createFunctionJSON(size_t stopCount) { + std::string stops = "["; + for (size_t i = 0; i < stopCount; i++) { + std::string value = std::to_string(24.0f / stopCount * i); + if (stops.size() > 1) stops += ","; + stops += "[" + value + ", " + value + "]"; + } + stops += "]"; + return R"({"type": "exponential", "base": 2, "stops": )" + stops + "}"; +} + +static void Parse_CameraFunction(benchmark::State& state) { + size_t stopCount = state.range(0); + + while (state.KeepRunning()) { + conversion::Error error; + state.PauseTiming(); + auto doc = createFunctionJSON(stopCount); + state.ResumeTiming(); + optional> result = conversion::convertJSON>(doc, error, false, false); + if (!result) { + state.SkipWithError(error.message.c_str()); + } + } + state.SetLabel(std::to_string(stopCount).c_str()); +} + +static void Evaluate_CameraFunction(benchmark::State& state) { + size_t stopCount = state.range(0); + auto doc = createFunctionJSON(stopCount); + conversion::Error error; + optional> function = conversion::convertJSON>(doc, error, false, false); + if (!function) { + state.SkipWithError(error.message.c_str()); + } + + while(state.KeepRunning()) { + float z = 24.0f * static_cast(rand() % 100) / 100; + function->asExpression().evaluate(z); + } + + state.SetLabel(std::to_string(stopCount).c_str()); +} + +BENCHMARK(Parse_CameraFunction) + ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12); + +BENCHMARK(Evaluate_CameraFunction) + ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12); + + diff --git a/mapbox-gl-native/benchmark/function/composite_function.benchmark.cpp b/mapbox-gl-native/benchmark/function/composite_function.benchmark.cpp new file mode 100644 index 000000000..8064d548f --- /dev/null +++ b/mapbox-gl-native/benchmark/function/composite_function.benchmark.cpp @@ -0,0 +1,67 @@ +#include + +#include + +#include +#include +#include +#include + +using namespace mbgl; +using namespace mbgl::style; + +static std::string createFunctionJSON(size_t stopCount) { + std::string stops = "["; + for (size_t outerStop = 0; outerStop < stopCount; outerStop++) { + for (size_t innerStop = 0; innerStop < stopCount; innerStop++) { + std::string zoom = std::to_string(24.0f / stopCount * outerStop); + std::string value = std::to_string(100.0f / stopCount * innerStop); + + if (stops.size() > 1) stops += ","; + stops += R"([{"zoom":)" + zoom + R"(,"value":)" + value + "}, " + value + "]"; + } + } + stops += "]"; + return R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})"; +} + +static void Parse_CompositeFunction(benchmark::State& state) { + size_t stopCount = state.range(0); + + while (state.KeepRunning()) { + conversion::Error error; + state.PauseTiming(); + auto doc = createFunctionJSON(stopCount); + state.ResumeTiming(); + optional> result = conversion::convertJSON>(doc, error, true, false); + if (!result) { + state.SkipWithError(error.message.c_str()); + } + } + state.SetLabel(std::to_string(stopCount).c_str()); +} + +static void Evaluate_CompositeFunction(benchmark::State& state) { + size_t stopCount = state.range(0); + auto doc = createFunctionJSON(stopCount); + conversion::Error error; + optional> function = conversion::convertJSON>(doc, error, true, false); + if (!function) { + state.SkipWithError(error.message.c_str()); + } + + while(state.KeepRunning()) { + float z = 24.0f * static_cast(rand() % 100) / 100; + function->asExpression().evaluate(z, StubGeometryTileFeature(PropertyMap { { "x", static_cast(rand() % 100) } }), -1.0f); + } + + state.SetLabel(std::to_string(stopCount).c_str()); +} + +BENCHMARK(Parse_CompositeFunction) + ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12); + +BENCHMARK(Evaluate_CompositeFunction) + ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12); + + diff --git a/mapbox-gl-native/benchmark/function/source_function.benchmark.cpp b/mapbox-gl-native/benchmark/function/source_function.benchmark.cpp new file mode 100644 index 000000000..b75cc143d --- /dev/null +++ b/mapbox-gl-native/benchmark/function/source_function.benchmark.cpp @@ -0,0 +1,61 @@ +#include + +#include + +#include +#include +#include + +using namespace mbgl; +using namespace mbgl::style; + +static std::string createFunctionJSON(size_t stopCount) { + std::string stops = "["; + for (size_t i = 0; i < stopCount; i++) { + std::string value = std::to_string(100.0f / stopCount * i); + if (stops.size() > 1) stops += ","; + stops += "[" + value + ", " + value + "]"; + } + stops += "]"; + return R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})"; +} + +static void Parse_SourceFunction(benchmark::State& state) { + size_t stopCount = state.range(0); + + while (state.KeepRunning()) { + conversion::Error error; + state.PauseTiming(); + auto doc = createFunctionJSON(stopCount); + state.ResumeTiming(); + optional> result = conversion::convertJSON>(doc, error, true, false); + if (!result) { + state.SkipWithError(error.message.c_str()); + } + } + state.SetLabel(std::to_string(stopCount).c_str()); +} + +static void Evaluate_SourceFunction(benchmark::State& state) { + size_t stopCount = state.range(0); + auto doc = createFunctionJSON(stopCount); + conversion::Error error; + optional> function = conversion::convertJSON>(doc, error, true, false); + if (!function) { + state.SkipWithError(error.message.c_str()); + } + + while(state.KeepRunning()) { + function->asExpression().evaluate(StubGeometryTileFeature(PropertyMap { { "x", static_cast(rand() % 100) } }), -1.0f); + } + + state.SetLabel(std::to_string(stopCount).c_str()); +} + +BENCHMARK(Parse_SourceFunction) + ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12); + +BENCHMARK(Evaluate_SourceFunction) + ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12); + + diff --git a/mapbox-gl-native/benchmark/include/mbgl/benchmark.hpp b/mapbox-gl-native/benchmark/include/mbgl/benchmark.hpp new file mode 100644 index 000000000..1a9904de5 --- /dev/null +++ b/mapbox-gl-native/benchmark/include/mbgl/benchmark.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace mbgl { + +int runBenchmark(int argc, char* argv[]); + +} // namespace mbgl diff --git a/mapbox-gl-native/benchmark/ios/BenchmarkApp_iphoneos13.2-arm64e-debug.xctestrun b/mapbox-gl-native/benchmark/ios/BenchmarkApp_iphoneos13.2-arm64e-debug.xctestrun new file mode 100644 index 000000000..4270610f0 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/BenchmarkApp_iphoneos13.2-arm64e-debug.xctestrun @@ -0,0 +1,68 @@ + + + + + BenchmarkAppTests + + BlueprintName + BenchmarkAppTests + BundleIdentifiersForCrashReportEmphasis + + com.mapbox.BenchmarkApp + com.mapbox.BenchmarkAppTests + + CommandLineArguments + + DependentProductPaths + + __TESTROOT__/Debug-iphoneos/BenchmarkApp.app + __TESTROOT__/Debug-iphoneos/BenchmarkApp.app/PlugIns/BenchmarkAppTests.xctest + + EnvironmentVariables + + OS_ACTIVITY_DT_MODE + YES + SQLITE_ENABLE_THREAD_ASSERTIONS + 1 + + IsAppHostedTestBundle + + ProductModuleName + BenchmarkAppTests + RunOrder + 0 + SystemAttachmentLifetime + deleteOnSuccess + TestBundlePath + __TESTHOST__/PlugIns/BenchmarkAppTests.xctest + TestHostBundleIdentifier + com.mapbox.BenchmarkApp + TestHostPath + __TESTROOT__/Debug-iphoneos/BenchmarkApp.app + TestLanguage + + TestRegion + + TestingEnvironmentVariables + + DYLD_FALLBACK_FRAMEWORK_PATH + + DYLD_INSERT_LIBRARIES + __PLATFORMS__/iPhoneOS.platform/Developer/usr/lib/libXCTestBundleInject.dylib + XCInjectBundleInto + unused + + ToolchainsSettingValue + + UITargetAppCommandLineArguments + + UserAttachmentLifetime + deleteOnSuccess + + __xctestrun_metadata__ + + FormatVersion + 1 + + + diff --git a/mapbox-gl-native/benchmark/ios/BenchmarkApp_iphoneos13.2-arm64e-release.xctestrun b/mapbox-gl-native/benchmark/ios/BenchmarkApp_iphoneos13.2-arm64e-release.xctestrun new file mode 100644 index 000000000..6c6d73f3b --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/BenchmarkApp_iphoneos13.2-arm64e-release.xctestrun @@ -0,0 +1,68 @@ + + + + + BenchmarkAppTests + + BlueprintName + BenchmarkAppTests + BundleIdentifiersForCrashReportEmphasis + + com.mapbox.BenchmarkApp + com.mapbox.BenchmarkAppTests + + CommandLineArguments + + DependentProductPaths + + __TESTROOT__/Release-iphoneos/BenchmarkApp.app + __TESTROOT__/Release-iphoneos/BenchmarkApp.app/PlugIns/BenchmarkAppTests.xctest + + EnvironmentVariables + + OS_ACTIVITY_DT_MODE + YES + SQLITE_ENABLE_THREAD_ASSERTIONS + 1 + + IsAppHostedTestBundle + + ProductModuleName + BenchmarkAppTests + RunOrder + 0 + SystemAttachmentLifetime + deleteOnSuccess + TestBundlePath + __TESTHOST__/PlugIns/BenchmarkAppTests.xctest + TestHostBundleIdentifier + com.mapbox.BenchmarkApp + TestHostPath + __TESTROOT__/Release-iphoneos/BenchmarkApp.app + TestLanguage + + TestRegion + + TestingEnvironmentVariables + + DYLD_FALLBACK_FRAMEWORK_PATH + + DYLD_INSERT_LIBRARIES + __PLATFORMS__/iPhoneOS.platform/Developer/usr/lib/libXCTestBundleInject.dylib + XCInjectBundleInto + unused + + ToolchainsSettingValue + + UITargetAppCommandLineArguments + + UserAttachmentLifetime + deleteOnSuccess + + __xctestrun_metadata__ + + FormatVersion + 1 + + + diff --git a/mapbox-gl-native/benchmark/ios/Info.plist b/mapbox-gl-native/benchmark/ios/Info.plist new file mode 100644 index 000000000..0c6fd6144 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/Info.plist @@ -0,0 +1,62 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + BenchmarkApp + CFBundleIdentifier + com.mapbox.BenchmarkApp + CFBundleGetInfoString + com.mapbox.BenchmarkApp + CFBundleInfoDictionaryVersion + 1.0 + CFBundleLongVersionString + 1.0 + CFBundleName + com.mapbox.BenchmarkApp + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + MBGL + CFBundleVersion + 7877 + CSResourcesFileMapped + + NSHumanReadableCopyright + © 2014–2020 Mapbox + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIBackgroundModes + + fetch + remote-notification + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + + \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/ios/README.md b/mapbox-gl-native/benchmark/ios/README.md new file mode 100644 index 000000000..5ee2a9aa3 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/README.md @@ -0,0 +1,7 @@ +# iOS BenchmarkRunner App + +This is a blank single-view-controller iOS app, linked against to a C++ static libraries, plus a a simple unit xctest. We use CMake to create an Xcode-friendly out-of-source build system. In another word, the build system is maintained by `CMakeLists` file,instead of a configured `.xcodeproj` file. + +This CMake project can build the executable BenchmarkApp which is linked to the static C++ library 'mbgl-benchmark'. CMakeLists.txt files are the only build configuration kept in source control. This is in contrast to committing the `.xcodeproj` directory which includes the backing XML, which is nonsensically hard to edit by hand. + +The test instantiates ObjC object of class `IosTestRunner` from the app, the class will instantiates a C++ object from the linked library `mbgl-benchmark` and calls running benchmark function on it. It subsequently deletes the C++ object pointer. In the end, the test will check the existence of test report, which is a json file that attach to xctest result. diff --git a/mapbox-gl-native/benchmark/ios/codesigning/BenchmarkApp.app.xcent.template b/mapbox-gl-native/benchmark/ios/codesigning/BenchmarkApp.app.xcent.template new file mode 100644 index 000000000..669e8cdc5 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/codesigning/BenchmarkApp.app.xcent.template @@ -0,0 +1,16 @@ + + + + + application-identifier + $TEAM_ID.com.mapbox.BenchmarkApp + com.apple.developer.team-identifier + $TEAM_ID + get-task-allow + + keychain-access-groups + + $TEAM_ID.com.mapbox.BenchmarkApp + + + \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/ios/codesigning/BenchmarkAppTests.xctest.xcent.template b/mapbox-gl-native/benchmark/ios/codesigning/BenchmarkAppTests.xctest.xcent.template new file mode 100644 index 000000000..4070d8939 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/codesigning/BenchmarkAppTests.xctest.xcent.template @@ -0,0 +1,16 @@ + + + + + application-identifier + $TEAM_ID.com.mapbox.BenchmarkAppTests + com.apple.developer.team-identifier + $TEAM_ID + get-task-allow + + keychain-access-groups + + $TEAM_ID.com.mapbox.BenchmarkAppTests + + + \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/ios/codesigning/XCTAutomationSupport.framework.xcent.template b/mapbox-gl-native/benchmark/ios/codesigning/XCTAutomationSupport.framework.xcent.template new file mode 100644 index 000000000..4070d8939 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/codesigning/XCTAutomationSupport.framework.xcent.template @@ -0,0 +1,16 @@ + + + + + application-identifier + $TEAM_ID.com.mapbox.BenchmarkAppTests + com.apple.developer.team-identifier + $TEAM_ID + get-task-allow + + keychain-access-groups + + $TEAM_ID.com.mapbox.BenchmarkAppTests + + + \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/ios/codesigning/XCTest.framework.xcent.template b/mapbox-gl-native/benchmark/ios/codesigning/XCTest.framework.xcent.template new file mode 100644 index 000000000..4070d8939 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/codesigning/XCTest.framework.xcent.template @@ -0,0 +1,16 @@ + + + + + application-identifier + $TEAM_ID.com.mapbox.BenchmarkAppTests + com.apple.developer.team-identifier + $TEAM_ID + get-task-allow + + keychain-access-groups + + $TEAM_ID.com.mapbox.BenchmarkAppTests + + + \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/ios/codesigning/codesign_all.sh b/mapbox-gl-native/benchmark/ios/codesigning/codesign_all.sh new file mode 100755 index 000000000..4c1558795 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/codesigning/codesign_all.sh @@ -0,0 +1,11 @@ +#!/bin/bash +identity=$(/usr/bin/env xcrun security find-identity -v -p codesigning) +var=${identity#*1)\ } +CODE_SIGN_IDENTITY=${var%\ \"Apple\ Development*} + +codesign --force --sign $CODE_SIGN_IDENTITY --entitlements codesigning/BenchmarkApp.app.xcent --timestamp=none ./Release-iphoneos/BenchmarkApp.app +codesign --force --sign $CODE_SIGN_IDENTITY --deep --preserve-metadata=identifier,entitlements,flags --timestamp=none ./Release-iphoneos/BenchmarkApp.app/Frameworks/libXCTestSwiftSupport.dylib +codesign --force --sign $CODE_SIGN_IDENTITY --deep --preserve-metadata=identifier,entitlements,flags --timestamp=none ./Release-iphoneos/BenchmarkApp.app/Frameworks/libXCTestBundleInject.dylib +codesign --force --sign $CODE_SIGN_IDENTITY --deep --preserve-metadata=identifier,entitlements,flags --timestamp=none ./Release-iphoneos/BenchmarkApp.app/Frameworks/XCTAutomationSupport.framework +codesign --force --sign $CODE_SIGN_IDENTITY --deep --preserve-metadata=identifier,entitlements,flags --timestamp=none ./Release-iphoneos/BenchmarkApp.app/Frameworks/XCTest.framework +codesign --force --sign $CODE_SIGN_IDENTITY --deep --entitlements codesigning/BenchmarkAppTests.xctest.xcent --timestamp=none ./Release-iphoneos/BenchmarkApp.app/PlugIns/BenchmarkAppTests.xctest diff --git a/mapbox-gl-native/benchmark/ios/codesigning/generate-entitlements.swift b/mapbox-gl-native/benchmark/ios/codesigning/generate-entitlements.swift new file mode 100755 index 000000000..83834483b --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/codesigning/generate-entitlements.swift @@ -0,0 +1,40 @@ +#!/usr/bin/swift + +/// Generates Entitlement files .xcent for codesigning, containing the correct team id +import Swift +import Foundation + +let fileManager = FileManager.default + +let contentsOfCurrentWorkingDirectory = try FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: "./codesigning/"), includingPropertiesForKeys: nil, options: []) + +guard let rawTeamIdEnv = getenv("IOS_DEVELOPMENT_TEAM") else { + print("Please add a IOS_DEVELOPMENT_TEAM environment variable.") + exit(1) +} + +let teamId2 = String(utf8String: rawTeamIdEnv) + +for file in contentsOfCurrentWorkingDirectory { + if (file.pathExtension == "template") { + var content = "" + do { + content = try String(contentsOf: file, encoding: .utf8) + } + catch { + print("Error reading xcent file: \(error).") + } + + content = content.replacingOccurrences(of: "$TEAM_ID", with: teamId2!) + + var targetFile = file + targetFile.deletePathExtension() + do { + try content.write(to: targetFile, atomically: false, encoding: .utf8) + print("Entitlement " + targetFile.absoluteString + " generated.") + } + catch { + print("Error writing xcent file: \(error).") + } + } +} \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/ios/codesigning/libXCTestSwiftSupport.dylib.xcent.template b/mapbox-gl-native/benchmark/ios/codesigning/libXCTestSwiftSupport.dylib.xcent.template new file mode 100644 index 000000000..4070d8939 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/codesigning/libXCTestSwiftSupport.dylib.xcent.template @@ -0,0 +1,16 @@ + + + + + application-identifier + $TEAM_ID.com.mapbox.BenchmarkAppTests + com.apple.developer.team-identifier + $TEAM_ID + get-task-allow + + keychain-access-groups + + $TEAM_ID.com.mapbox.BenchmarkAppTests + + + \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/ios/fastlane/Appfile b/mapbox-gl-native/benchmark/ios/fastlane/Appfile new file mode 100644 index 000000000..af5ebdffa --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/fastlane/Appfile @@ -0,0 +1,3 @@ +app_identifier("com.mapbox.BenchmarkApp") # The bundle identifier of your app +apple_id ENV['MAPBOX_APPLE_ID'] +team_id ENV['MAPBOX_APPLE_TEAM_ID'] # Developer Portal Team ID \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/ios/fastlane/Matchfile b/mapbox-gl-native/benchmark/ios/fastlane/Matchfile new file mode 100644 index 000000000..f8fe80463 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/fastlane/Matchfile @@ -0,0 +1,5 @@ +git_url("git@github.com:mapbox/apple-certificates.git") +type("development") # The default type, can be: appstore, adhoc, enterprise or development +app_identifier(["com.mapbox.BenchmarkApp", "com.mapbox.BenchmarkAppTests"]) +username ENV['MAPBOX_APPLE_ID'] +keychain_name("fastlane_keychain") \ No newline at end of file diff --git a/mapbox-gl-native/benchmark/ios/iosTestRunner.h b/mapbox-gl-native/benchmark/ios/iosTestRunner.h new file mode 100644 index 000000000..d8cd7991b --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/iosTestRunner.h @@ -0,0 +1,12 @@ +#import + +NSString *dataDir = @"benchmark-data"; +NSString *resultFilePath = @"benchmark/results.json"; + +__attribute__((visibility ("default"))) +@interface IosTestRunner : NSObject + +- (NSString*) getResultPath; +- (BOOL) getTestStatus; + +@end diff --git a/mapbox-gl-native/benchmark/ios/ios_test_runner.cpp b/mapbox-gl-native/benchmark/ios/ios_test_runner.cpp new file mode 100644 index 000000000..fb88d3ed7 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/ios_test_runner.cpp @@ -0,0 +1,34 @@ +#include + +#include + +#include + +#include +#include + +#define EXPORT __attribute__((visibility("default"))) + +EXPORT +bool TestRunner::startTest(const std::string& basePath) { + std::vector arguments = {"mbgl-benchmark-runner", + "--benchmark_repetitions=10", + "--benchmark_format=json", + "--benchmark_out=" + basePath + "/benchmark/results.json"}; + std::vector argv; + for (const auto& arg : arguments) { + argv.push_back(const_cast(arg.data())); + } + argv.push_back(nullptr); + + if (chdir(basePath.c_str())) { + mbgl::Log::Error(mbgl::Event::General, "Failed to change the directory to " + basePath); + return false; + } + + mbgl::Log::Info(mbgl::Event::General, "Start BenchmarkRunner"); + int status = mbgl::runBenchmark(static_cast(argv.size()), argv.data()); + mbgl::Log::Info(mbgl::Event::General, "BenchmarkRunner finished with status: '%d'", status); + + return status == 0; +} diff --git a/mapbox-gl-native/benchmark/ios/tests/Info.plist b/mapbox-gl-native/benchmark/ios/tests/Info.plist new file mode 100644 index 000000000..83e7a9a08 --- /dev/null +++ b/mapbox-gl-native/benchmark/ios/tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + BenchmarkAppTests + CFBundleIdentifier + com.mapbox.BenchmarkAppTests + CFBundleInfoDictionaryVersion + 1.0 + CFBundleName + BenchmarkAppTests + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/mapbox-gl-native/benchmark/parse/filter.benchmark.cpp b/mapbox-gl-native/benchmark/parse/filter.benchmark.cpp new file mode 100644 index 000000000..7f94d9d74 --- /dev/null +++ b/mapbox-gl-native/benchmark/parse/filter.benchmark.cpp @@ -0,0 +1,34 @@ +#include + +#include +#include +#include +#include +#include +#include + +using namespace mbgl; + +style::Filter parse(const char* expression) { + style::conversion::Error error; + return *style::conversion::convertJSON(expression, error); +} + +static void Parse_Filter(benchmark::State& state) { + while (state.KeepRunning()) { + parse(R"FILTER(["==", "foo", "bar"])FILTER"); + } +} + +static void Parse_EvaluateFilter(benchmark::State& state) { + const style::Filter filter = parse(R"FILTER(["==", "foo", "bar"])FILTER"); + const StubGeometryTileFeature feature = { {}, FeatureType::Unknown , {}, {{ "foo", std::string("bar") }} }; + const style::expression::EvaluationContext context(&feature); + + while (state.KeepRunning()) { + filter(context); + } +} + +BENCHMARK(Parse_Filter); +BENCHMARK(Parse_EvaluateFilter); diff --git a/mapbox-gl-native/benchmark/parse/tile_mask.benchmark.cpp b/mapbox-gl-native/benchmark/parse/tile_mask.benchmark.cpp new file mode 100644 index 000000000..a31e0b5ea --- /dev/null +++ b/mapbox-gl-native/benchmark/parse/tile_mask.benchmark.cpp @@ -0,0 +1,36 @@ +#include + +#include + +using namespace mbgl; + +class FakeTile { +public: + FakeTile(TileMask mask_) + : mask(std::move(mask_)) { + } + void setMask(TileMask mask_) { + mask = std::move(mask_); + } + + const bool usedByRenderedLayers = true; + TileMask mask; +}; + +static void TileMaskGeneration(benchmark::State& state) { + std::map renderables = { + { UnwrappedTileID{ 12, 1028, 1456 }, TileMask{} }, + { UnwrappedTileID{ 13, 2056, 2912 }, TileMask{} }, + { UnwrappedTileID{ 13, 2056, 2913 }, TileMask{} }, + { UnwrappedTileID{ 14, 4112, 5824 }, TileMask{} }, + { UnwrappedTileID{ 14, 4112, 5827 }, TileMask{} }, + { UnwrappedTileID{ 14, 4114, 5824 }, TileMask{} }, + { UnwrappedTileID{ 14, 4114, 5825 }, TileMask{} }, + }; + + while (state.KeepRunning()) { + algorithm::updateTileMasks(renderables); + } +} + +BENCHMARK(TileMaskGeneration); diff --git a/mapbox-gl-native/benchmark/parse/vector_tile.benchmark.cpp b/mapbox-gl-native/benchmark/parse/vector_tile.benchmark.cpp new file mode 100644 index 000000000..24623dbda --- /dev/null +++ b/mapbox-gl-native/benchmark/parse/vector_tile.benchmark.cpp @@ -0,0 +1,28 @@ +#include + +#include +#include + +using namespace mbgl; + +static void Parse_VectorTile(benchmark::State& state) { + auto data = std::make_shared(util::read_file("test/fixtures/api/assets/streets/10-163-395.vector.pbf")); + + while (state.KeepRunning()) { + std::size_t length = 0; + VectorTileData tile(data); + for (const auto& name : tile.layerNames()) { + if (auto layer = tile.getLayer(name)) { + const std::size_t count = layer->featureCount(); + for (std::size_t i = 0; i < count; i++) { + if (auto feature = layer->getFeature(i)) { + length += feature->getGeometries().size(); + length += feature->getProperties().size(); + } + } + } + } + } +} + +BENCHMARK(Parse_VectorTile); diff --git a/mapbox-gl-native/benchmark/src/mbgl/benchmark/benchmark.cpp b/mapbox-gl-native/benchmark/src/mbgl/benchmark/benchmark.cpp new file mode 100644 index 000000000..d408e7c5e --- /dev/null +++ b/mapbox-gl-native/benchmark/src/mbgl/benchmark/benchmark.cpp @@ -0,0 +1,13 @@ +#include + +#include + +namespace mbgl { + +int runBenchmark(int argc, char* argv[]) { + ::benchmark::Initialize(&argc, argv); + ::benchmark::RunSpecifiedBenchmarks(); + return 0; +} + +} // namespace mbgl diff --git a/mapbox-gl-native/benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp b/mapbox-gl-native/benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp new file mode 100644 index 000000000..b8f2ffb5b --- /dev/null +++ b/mapbox-gl-native/benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +using namespace mbgl; + +class StubGeometryTileFeature : public GeometryTileFeature { +public: + StubGeometryTileFeature(PropertyMap properties_) + : properties(std::move(properties_)) { + } + + StubGeometryTileFeature(FeatureIdentifier id_, FeatureType type_, GeometryCollection geometry_, PropertyMap properties_) + : properties(std::move(properties_)), + id(std::move(id_)), + type(type_), + geometry(std::move(geometry_)) { + } + + PropertyMap properties; + FeatureIdentifier id; + FeatureType type = FeatureType::Point; + GeometryCollection geometry; + + FeatureType getType() const override { + return type; + } + + FeatureIdentifier getID() const override { + return id; + } + + optional getValue(const std::string& key) const override { + return properties.count(key) ? properties.at(key) : optional(); + } + + const GeometryCollection& getGeometries() const override { + return geometry; + } +}; diff --git a/mapbox-gl-native/benchmark/storage/offline_database.benchmark.cpp b/mapbox-gl-native/benchmark/storage/offline_database.benchmark.cpp new file mode 100644 index 000000000..afdf6db4f --- /dev/null +++ b/mapbox-gl-native/benchmark/storage/offline_database.benchmark.cpp @@ -0,0 +1,196 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include + +class OfflineDatabase : public benchmark::Fixture { +public: + void SetUp(const ::benchmark::State&) override { + using namespace std::chrono_literals; + + db.setOfflineMapboxTileCountLimit(100000); + + response.data = std::make_shared(50 * 1024, 0); + response.mustRevalidate = false; + response.expires = mbgl::util::now() + 1h; + + resetAmbientTiles(); + resetRegion(); + } + + void resetAmbientTiles() { + using namespace mbgl; + + db.clearAmbientCache(); + + for (unsigned i = 0; i < tileCount; ++i) { + const Resource ambient = Resource::tile("mapbox://tile_ambient" + util::toString(i), 1, 0, 0, 0, Tileset::Scheme::XYZ); + db.put(ambient, response); + } + } + + void resetRegion() { + using namespace mbgl; + + auto regions = db.listRegions().value(); + if (!regions.empty()) { + db.deleteRegion(std::move(regions[0])); + } + + OfflineTilePyramidRegionDefinition definition{ "mapbox://style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0, true }; + OfflineRegionMetadata metadata{{ 1, 2, 3 }}; + + auto region = db.createRegion(definition, metadata); + regionID = region->getID(); + + for (unsigned i = 0; i < tileCount; ++i) { + const Resource offline = Resource::tile("mapbox://tile_offline_region" + util::toString(i), 1.0, 0, 0, 0, Tileset::Scheme::XYZ); + db.putRegionResource(regionID, offline, response); + } + } + + mbgl::Response response; + mbgl::OfflineDatabase db{":memory:"}; + + const unsigned tileCount = 100; + int64_t regionID = 0; +}; + +BENCHMARK_F(OfflineDatabase, InvalidateRegion)(benchmark::State& state) { + while (state.KeepRunning()) { + db.invalidateRegion(regionID); + } +} + +BENCHMARK_F(OfflineDatabase, DeleteRegion)(benchmark::State& state) { + while (state.KeepRunning()) { + resetRegion(); + } +} + +BENCHMARK_F(OfflineDatabase, InsertTileRegion)(benchmark::State& state) { + using namespace mbgl; + + while (state.KeepRunning()) { + const Resource offline = Resource::tile("mapbox://InsertTileRegion" + + util::toString(state.iterations()), 1, 0, 0, 0, Tileset::Scheme::XYZ); + db.putRegionResource(regionID, offline, response); + } +} +BENCHMARK_F(OfflineDatabase, InvalidateAmbientCache)(benchmark::State& state) { + while (state.KeepRunning()) { + db.invalidateAmbientCache(); + } +} + +BENCHMARK_F(OfflineDatabase, ClearAmbientCache)(benchmark::State& state) { + while (state.KeepRunning()) { + resetAmbientTiles(); + } +} + +BENCHMARK_F(OfflineDatabase, InsertTileCache)(benchmark::State& state) { + using namespace mbgl; + + while (state.KeepRunning()) { + const Resource ambient = Resource::tile("mapbox://InsertTileCache" + + util::toString(state.iterations()), 1, 0, 0, 0, Tileset::Scheme::XYZ); + db.put(ambient, response); + } +} + +BENCHMARK_F(OfflineDatabase, InsertBigTileCache)(benchmark::State& state) { + using namespace mbgl; + + Response big; + big.data = std::make_shared(util::DEFAULT_MAX_CACHE_SIZE / 100, 0); + + while (state.KeepRunning()) { + const Resource ambient = Resource::tile("mapbox://InsertTileCache" + + util::toString(state.iterations()), 1, 0, 0, 0, Tileset::Scheme::XYZ); + db.put(ambient, big); + } +} + +BENCHMARK_F(OfflineDatabase, GetTile)(benchmark::State& state) { + using namespace mbgl; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, tileCount - 1); + + while (state.KeepRunning()) { + auto res = db.get(Resource::tile("mapbox://tile_ambient" + util::toString(dis(gen)), 1, 0, 0, 0, Tileset::Scheme::XYZ)); + assert(res != nullopt); + } +} + +BENCHMARK_F(OfflineDatabase, AddTilesToFullDatabase)(benchmark::State& state) { + using namespace mbgl; + + Log::setObserver(std::make_unique()); + db.setMaximumAmbientCacheSize(50 * 1024 * 5); + + while (state.KeepRunning()) { + const Resource ambient = Resource::tile("mapbox://AddTilesToFullDatabase" + + util::toString(state.iterations()), 1, 0, 0, 0, Tileset::Scheme::XYZ); + db.put(ambient, response); + } + + Log::removeObserver(); +} + +BENCHMARK_F(OfflineDatabase, AddTilesToDisabledDatabase)(benchmark::State& state) { + using namespace mbgl; + + auto regions = db.listRegions().value(); + if (!regions.empty()) { + db.deleteRegion(std::move(regions[0])); + } + + // Disables the ambient cache. + db.setMaximumAmbientCacheSize(0); + + while (state.KeepRunning()) { + const Resource ambient = Resource::tile("mapbox://AddTilesToFullDatabase" + + util::toString(state.iterations()), 1, 0, 0, 0, Tileset::Scheme::XYZ); + db.put(ambient, response); + } +} + +BENCHMARK_F(OfflineDatabase, GetTileFromDisabledDatabase)(benchmark::State& state) { + using namespace mbgl; + + auto regions = db.listRegions().value(); + if (!regions.empty()) { + db.deleteRegion(std::move(regions[0])); + } + + // Disables the ambient cache. + db.setMaximumAmbientCacheSize(0); + + while (state.KeepRunning()) { + auto res = db.get(Resource::tile("mapbox://tile_ambient", 1, 0, 0, 0, Tileset::Scheme::XYZ)); + assert(res == nullopt); + } +} + +BENCHMARK_F(OfflineDatabase, ResizeDatabase)(benchmark::State& state) { + uint64_t size = 25 * 1024 * 1024; + + while (state.KeepRunning()) { + db.setMaximumAmbientCacheSize(size); + + size -= response.data->size(); + + if (size < response.data->size()) { + size = 25 * 1024 * 1024; + } + } +} diff --git a/mapbox-gl-native/benchmark/util/dtoa.benchmark.cpp b/mapbox-gl-native/benchmark/util/dtoa.benchmark.cpp new file mode 100644 index 000000000..0009c6dd1 --- /dev/null +++ b/mapbox-gl-native/benchmark/util/dtoa.benchmark.cpp @@ -0,0 +1,66 @@ +#include + +#include + +#include +#include + +using namespace mbgl; + +static void Util_dtoa(::benchmark::State& state) { + while (state.KeepRunning()) { + util::dtoa(0.); + util::dtoa(M_E); + util::dtoa(M_LOG2E); + util::dtoa(M_LOG10E); + util::dtoa(M_LN2); + util::dtoa(M_LN10); + util::dtoa(M_PI); + util::dtoa(M_PI_2); + util::dtoa(M_PI_4); + util::dtoa(M_1_PI); + util::dtoa(M_2_PI); + util::dtoa(M_2_SQRTPI); + util::dtoa(M_SQRT2); + util::dtoa(M_SQRT1_2); + } +} + +static void Util_standardDtoa(::benchmark::State& state) { + while (state.KeepRunning()) { + std::to_string(0.); + std::to_string(M_E); + std::to_string(M_LOG2E); + std::to_string(M_LOG10E); + std::to_string(M_LN2); + std::to_string(M_LN10); + std::to_string(M_PI); + std::to_string(M_PI_2); + std::to_string(M_PI_4); + std::to_string(M_1_PI); + std::to_string(M_2_PI); + std::to_string(M_2_SQRTPI); + std::to_string(M_SQRT2); + std::to_string(M_SQRT1_2); + } +} + +static void Util_dtoaLimits(::benchmark::State& state) { + while (state.KeepRunning()) { + util::dtoa(DBL_MIN); + util::dtoa(DBL_MAX); + } +} + +static void Util_standardDtoaLimits(::benchmark::State& state) { + while (state.KeepRunning()) { + std::to_string(DBL_MIN); + std::to_string(DBL_MAX); + } +} + +BENCHMARK(Util_dtoa); +BENCHMARK(Util_standardDtoa); + +BENCHMARK(Util_dtoaLimits); +BENCHMARK(Util_standardDtoaLimits); diff --git a/mapbox-gl-native/benchmark/util/tilecover.benchmark.cpp b/mapbox-gl-native/benchmark/util/tilecover.benchmark.cpp new file mode 100644 index 000000000..e966875a6 --- /dev/null +++ b/mapbox-gl-native/benchmark/util/tilecover.benchmark.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include + +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.jumpTo(CameraOptions().withCenter(LatLng { 0.1, -0.1 }).withZoom(8.0).withBearing(5.0).withPitch(40.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{ + { + {-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/mapbox-gl-native/bin/CMakeLists.txt b/mapbox-gl-native/bin/CMakeLists.txt new file mode 100644 index 000000000..d6ac17ae1 --- /dev/null +++ b/mapbox-gl-native/bin/CMakeLists.txt @@ -0,0 +1,42 @@ +add_executable( + mbgl-cache + ${PROJECT_SOURCE_DIR}/bin/cache.cpp +) + +target_link_libraries( + mbgl-cache + PRIVATE + Mapbox::Base + Mapbox::Base::Extras::args + mbgl-compiler-options + mbgl-core +) + +add_executable( + mbgl-offline + ${PROJECT_SOURCE_DIR}/bin/offline.cpp +) + +target_link_libraries( + mbgl-offline + PRIVATE Mapbox::Base::Extras::args mbgl-compiler-options mbgl-core +) + +add_executable( + mbgl-render + ${PROJECT_SOURCE_DIR}/bin/render.cpp +) + +target_link_libraries( + mbgl-render + PRIVATE Mapbox::Base::Extras::args mbgl-compiler-options mbgl-core +) + +install(TARGETS mbgl-offline mbgl-render RUNTIME DESTINATION bin) + +# FIXME: CI must have a valid token +# +# add_test(NAME mbgl-offline-tool-test COMMAND mbgl-offline -s mapbox://styles/mapbox/satellite-v9 --maxZoom=0 WORKING_DIRECTORY +# ${PROJECT_SOURCE_DIR} ) +# +# add_test(NAME mbgl-render-tool-test COMMAND mbgl-render WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} ) diff --git a/mapbox-gl-native/bin/cache.cpp b/mapbox-gl-native/bin/cache.cpp new file mode 100644 index 000000000..179cd9dc8 --- /dev/null +++ b/mapbox-gl-native/bin/cache.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) { + args::ArgumentParser p("Mapbox GL cache tool", ""); + args::HelpFlag helpFlag(p, "help", "Display this help menu", {'h'}); + + args::ValueFlag urlValue(p, "URL", "Resource URL (required)", {'u'}, args::Options::Required); + args::ValueFlag cacheValue( + p, "cache", "Path to the cache database (required)", {'c'}, args::Options::Required); + args::ValueFlag dataValue( + p, "data", "Path to the resource data (required)", {'d'}, args::Options::Required); + args::ValueFlag etagValue(p, "etag", "Cache eTag, none otherwise", {'t'}); + args::ValueFlag expiresValue(p, "expires", "Expires date, will use 'now' otherwise", {'e'}); + args::ValueFlag modifiedValue(p, "modified", "Modified date, will use 'now' otherwise", {'m'}); + + std::unordered_map typeMap{{"glyphs", mbgl::Resource::Glyphs}, + {"image", mbgl::Resource::Image}, + {"source", mbgl::Resource::Source}, + {"sprite-image", mbgl::Resource::SpriteImage}, + {"sprite-json", mbgl::Resource::SpriteJSON}, + {"style", mbgl::Resource::Style}, + {"tile", mbgl::Resource::Tile}}; + + std::string typeHelp("One of the following (required):"); + for (auto key : typeMap) { + typeHelp += " " + key.first; + } + + args::MapFlag typeFlag( + p, "type", typeHelp, {"type"}, typeMap, args::Options::Required); + + args::Group tileIdGroup(p, "Coordinates (required for 'tile')", args::Group::Validators::AllOrNone); + args::ValueFlag xValueFlag(tileIdGroup, "x", "Tile x coordinate", {'x'}); + args::ValueFlag yValueFlag(tileIdGroup, "y", "Tile y coordinate", {'y'}); + args::ValueFlag zValueFlag(tileIdGroup, "z", "The zoom level", {'z'}); + + try { + p.ParseCLI(argc, argv); + } catch (const args::Help&) { + std::cout << p; + exit(0); + } catch (const args::ParseError& e) { + std::cerr << e.what() << std::endl; + std::cerr << p; + exit(1); + } catch (const args::ValidationError& e) { + std::cerr << e.what() << std::endl; + std::cerr << p; + exit(2); + } + + auto file = mapbox::base::io::readFile(args::get(dataValue)); + if (!file) { + std::cerr << file.error() << std::endl; + exit(3); + } + + mbgl::Response response; + response.data = std::make_shared(*file); + response.etag = etagValue ? args::get(etagValue) : std::string(); + response.expires = expiresValue ? mbgl::Timestamp(mbgl::Seconds(args::get(expiresValue))) : mbgl::util::now(); + response.modified = modifiedValue ? mbgl::Timestamp(mbgl::Seconds(args::get(modifiedValue))) : mbgl::util::now(); + + mbgl::Resource resource(args::get(typeFlag), args::get(urlValue)); + + if (args::get(typeFlag) == mbgl::Resource::Tile) { + if (!xValueFlag || !yValueFlag || !zValueFlag) { + std::cerr << "Error: -x, -y and -z must be provided for tiles" << std::endl; + exit(1); + } + + resource.tileData = {{args::get(urlValue), + 1, + args::get(xValueFlag), + args::get(yValueFlag), + static_cast(args::get(zValueFlag))}}; + } + + mbgl::util::RunLoop loop; + std::shared_ptr dbfs = mbgl::FileSourceManager::get()->getFileSource( + mbgl::FileSourceType::Database, mbgl::ResourceOptions().withCachePath(args::get(cacheValue))); + dbfs->forward(resource, response, [&loop] { loop.stop(); }); + loop.run(); + return 0; +} diff --git a/mapbox-gl-native/bin/offline.cpp b/mapbox-gl-native/bin/offline.cpp new file mode 100644 index 000000000..97b60c223 --- /dev/null +++ b/mapbox-gl-native/bin/offline.cpp @@ -0,0 +1,274 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace std::literals::chrono_literals; + +std::string readFile(const std::string& fileName) { + std::ifstream stream(fileName.c_str()); + if (!stream.good()) { + throw std::runtime_error("Cannot read file: " + fileName); + } + + std::stringstream buffer; + buffer << stream.rdbuf(); + stream.close(); + + return buffer.str(); +} + +mapbox::geometry::geometry parseGeometry(const std::string& json) { + using namespace mapbox::geojson; + auto geojson = parse(json); + return geojson.match( + [](const geometry& geom) { + return geom; + }, + [](const feature& feature) { + return feature.geometry; + }, + [](const feature_collection& featureCollection) { + if (featureCollection.size() < 1) { + throw std::runtime_error("No features in feature collection"); + } + geometry_collection geometries; + + for (auto feature : featureCollection) { + geometries.push_back(feature.geometry); + } + + return geometries; + }); +} + +std::ostream& operator<<(std::ostream& os, mbgl::Response::Error::Reason r) { + switch (r) { + case mbgl::Response::Error::Reason::Success: + return os << "Response::Error::Reason::Success"; + case mbgl::Response::Error::Reason::NotFound: + return os << "Response::Error::Reason::NotFound"; + case mbgl::Response::Error::Reason::Server: + return os << "Response::Error::Reason::Server"; + case mbgl::Response::Error::Reason::Connection: + return os << "Response::Error::Reason::Connection"; + case mbgl::Response::Error::Reason::RateLimit: + return os << "Response::Error::Reason::RateLimit"; + case mbgl::Response::Error::Reason::Other: + return os << "Response::Error::Reason::Other"; + } + + // The above switch is exhaustive, but placate GCC nonetheless: + assert(false); + return os; +} + +int main(int argc, char *argv[]) { + args::ArgumentParser argumentParser("Mapbox GL offline tool"); + args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"}); + + args::ValueFlag tokenValue(argumentParser, "key", "Mapbox access token", {'t', "token"}); + args::ValueFlag styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"}); + args::ValueFlag outputValue(argumentParser, "file", "Output database file name", {'o', "output"}); + args::ValueFlag apiBaseValue(argumentParser, "URL", "API Base URL", {'a', "apiBaseURL"}); + + args::Group mergeGroup(argumentParser, "Merge databases:", args::Group::Validators::AllOrNone); + args::ValueFlag mergePathValue(mergeGroup, "merge", "Database to merge from", {'m', "merge"}); + args::ValueFlag inputValue(mergeGroup, "input", "Database to merge into. Use with --merge option.", {'i', "input"}); + + // LatLngBounds + args::Group latLngBoundsGroup(argumentParser, "LatLng bounds:", args::Group::Validators::AllOrNone); + args::ValueFlag northValue(latLngBoundsGroup, "degrees", "North latitude", {"north"}); + args::ValueFlag westValue(latLngBoundsGroup, "degrees", "West longitude", {"west"}); + args::ValueFlag southValue(latLngBoundsGroup, "degrees", "South latitude", {"south"}); + args::ValueFlag eastValue(latLngBoundsGroup, "degrees", "East longitude", {"east"}); + + // Geometry + args::Group geoJSONGroup(argumentParser, "GeoJson geometry:", args::Group::Validators::AllOrNone); + args::ValueFlag geometryValue(geoJSONGroup, "file", "GeoJSON Feature file containing the region geometry (can't be a FeatureCollection)", {"geojson"}); + + args::ValueFlag minZoomValue(argumentParser, "number", "Min zoom level", {"minZoom"}); + args::ValueFlag maxZoomValue(argumentParser, "number", "Max zoom level", {"maxZoom"}); + args::ValueFlag pixelRatioValue(argumentParser, "number", "Pixel ratio", {"pixelRatio"}); + args::ValueFlag includeIdeographsValue(argumentParser, "boolean", "Include CJK glyphs", {"includeIdeographs"}); + + try { + argumentParser.ParseCLI(argc, argv); + } catch (const args::Help&) { + std::cout << argumentParser; + exit(0); + } catch (const args::ParseError& e) { + std::cerr << e.what() << std::endl; + std::cerr << argumentParser; + exit(1); + } catch (const args::ValidationError& e) { + std::cerr << e.what() << std::endl; + std::cerr << argumentParser; + exit(2); + } + + std::string style = styleValue ? args::get(styleValue) : mbgl::util::default_styles::streets.url; + + mbgl::optional mergePath = {}; + if (mergePathValue) mergePath = args::get(mergePathValue); + mbgl::optional inputDb = {}; + if (inputValue) inputDb = args::get(inputValue); + + const double minZoom = minZoomValue ? args::get(minZoomValue) : 0.0; + const double maxZoom = maxZoomValue ? args::get(maxZoomValue) : 15.0; + const double pixelRatio = pixelRatioValue ? args::get(pixelRatioValue) : 1.0; + const bool includeIdeographs = includeIdeographsValue ? args::get(includeIdeographsValue) : false; + const std::string output = outputValue ? args::get(outputValue) : "offline.db"; + + using namespace mbgl; + + OfflineRegionDefinition definition = [&]() { + if (geometryValue) { + try { + std::string json = readFile(geometryValue.Get()); + auto geometry = parseGeometry(json); + return OfflineRegionDefinition{ OfflineGeometryRegionDefinition(style, geometry, minZoom, maxZoom, pixelRatio, includeIdeographs) }; + } catch(const std::runtime_error& e) { + std::cerr << "Could not parse geojson file " << geometryValue.Get() << ": " << e.what() << std::endl; + exit(1); + } + } else { + // Bay area + const double north = northValue ? args::get(northValue) : 37.2; + const double west = westValue ? args::get(westValue) : -122.8; + const double south = southValue ? args::get(southValue) : 38.1; + const double east = eastValue ? args::get(eastValue) : -121.7; + LatLngBounds boundingBox = LatLngBounds::hull(LatLng(north, west), LatLng(south, east)); + return OfflineRegionDefinition{ OfflineTilePyramidRegionDefinition(style, boundingBox, minZoom, maxZoom, pixelRatio, includeIdeographs) }; + } + }(); + + const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN"); + const std::string token = tokenValue ? args::get(tokenValue) : (tokenEnv ? tokenEnv : std::string()); + + const std::string apiBaseURL = apiBaseValue ? args::get(apiBaseValue) : mbgl::util::API_BASE_URL; + + + util::RunLoop loop; + std::shared_ptr fileSource = std::static_pointer_cast( + std::shared_ptr(FileSourceManager::get()->getFileSource( + FileSourceType::Database, + ResourceOptions().withAccessToken(token).withBaseURL(apiBaseURL).withCachePath(output)))); + + std::unique_ptr region; + + if (inputDb && mergePath) { + DatabaseFileSource inputSource(ResourceOptions().withCachePath(*inputDb)); + + int retCode = 0; + std::cout << "Start Merge" << std::endl; + inputSource.mergeOfflineRegions(*mergePath, [&] (mbgl::expected, std::exception_ptr> result) { + + if (!result) { + std::cerr << "Error merging database: " << util::toString(result.error()) << std::endl; + retCode = 1; + } else { + std::cout << " Added " << result->size() << " Regions" << std::endl; + std::cout << "Finished Merge" << std::endl; + } + loop.stop(); + }); + loop.run(); + return retCode; + } + + OfflineRegionMetadata metadata; + + class Observer : public OfflineRegionObserver { + public: + Observer(OfflineRegion& region_, + std::shared_ptr fileSource_, + util::RunLoop& loop_, + mbgl::optional mergePath_) + : region(region_), + fileSource(std::move(fileSource_)), + loop(loop_), + mergePath(std::move(mergePath_)), + start(util::now()) {} + + void statusChanged(OfflineRegionStatus status) override { + if (status.downloadState == OfflineRegionDownloadState::Inactive) { + std::cout << "stopped" << std::endl; + loop.stop(); + return; + } + + std::string bytesPerSecond = "-"; + + auto elapsedSeconds = (util::now() - start) / 1s; + if (elapsedSeconds != 0) { + bytesPerSecond = util::toString(status.completedResourceSize / elapsedSeconds); + } + + std::cout << status.completedResourceCount << " / " << status.requiredResourceCount << " resources | " + << status.completedTileCount << " / " << status.requiredTileCount << " tiles" + << (status.requiredResourceCountIsPrecise ? " | " : " (indeterminate); ") + << status.completedResourceSize << " bytes downloaded" + << " (" << bytesPerSecond << " bytes/sec)" << std::endl; + + if (status.complete()) { + std::cout << "Finished Download" << std::endl; + loop.stop(); + } + } + + void responseError(Response::Error error) override { + std::cerr << error.reason << " downloading resource: " << error.message << std::endl; + } + + void mapboxTileCountLimitExceeded(uint64_t limit) override { + std::cerr << "Error: reached limit of " << limit << " offline tiles" << std::endl; + } + + OfflineRegion& region; + std::shared_ptr fileSource; + util::RunLoop& loop; + mbgl::optional mergePath; + Timestamp start; + }; + + static auto stop = [&] { + if (region) { + std::cout << "Stopping download... "; + fileSource->setOfflineRegionDownloadState(*region, OfflineRegionDownloadState::Inactive); + } + }; + + std::signal(SIGINT, [] (int) { stop(); }); + + fileSource->createOfflineRegion( + definition, metadata, [&](mbgl::expected region_) { + if (!region_) { + std::cerr << "Error creating region: " << util::toString(region_.error()) << std::endl; + loop.stop(); + exit(1); + } else { + assert(region_); + region = std::make_unique(std::move(*region_)); + fileSource->setOfflineRegionObserver(*region, + std::make_unique(*region, fileSource, loop, mergePath)); + fileSource->setOfflineRegionDownloadState(*region, OfflineRegionDownloadState::Active); + } + }); + + loop.run(); + return 0; +} diff --git a/mapbox-gl-native/bin/offline.sh b/mapbox-gl-native/bin/offline.sh new file mode 100755 index 000000000..99ce2185e --- /dev/null +++ b/mapbox-gl-native/bin/offline.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +OFFLINE=./build/macos/Release/mbgl-offline + +# Barcelona +$OFFLINE --north 41.4664 --west 2.0407 --south 41.2724 --east 2.2680 --output barcelona.db +$OFFLINE --north 41.4664 --west 2.0407 --south 41.2724 --east 2.2680 --output barcelona_sat.db --style mapbox://styles/mapbox/satellite-v8 + +# Greater London within the M25 at zoom levels 0–15 +$OFFLINE --north 51.6777 --west -0.5026 --south 51.3237 --east 0.2527 --maxZoom 15 --output london.db +$OFFLINE --north 51.6777 --west -0.5026 --south 51.3237 --east 0.2527 --maxZoom 15 --output london_sat.db --style mapbox://styles/mapbox/satellite-v8 + +# The contiguous United States at zoom levels 0–9 +$OFFLINE --north 49.6107 --west -125.5078 --south 24.2870 --east -66.3574 --maxZoom 9 --output us.db +$OFFLINE --north 49.6107 --west -125.5078 --south 24.2870 --east -66.3574 --maxZoom 9 --output us_sat.db --style mapbox://styles/mapbox/satellite-v8 diff --git a/mapbox-gl-native/bin/render.cpp b/mapbox-gl-native/bin/render.cpp new file mode 100644 index 000000000..409dfa792 --- /dev/null +++ b/mapbox-gl-native/bin/render.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +int main(int argc, char *argv[]) { + args::ArgumentParser argumentParser("Mapbox GL render tool"); + args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {"help"}); + + args::ValueFlag backendValue(argumentParser, "Backend", "Rendering backend", {"backend"}); + args::ValueFlag tokenValue(argumentParser, "key", "Mapbox access token", {'t', "token"}); + args::ValueFlag styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"}); + args::ValueFlag outputValue(argumentParser, "file", "Output file name", {'o', "output"}); + args::ValueFlag cacheValue(argumentParser, "file", "Cache database file name", {'c', "cache"}); + args::ValueFlag assetsValue(argumentParser, "file", "Directory to which asset:// URLs will resolve", {'a', "assets"}); + + args::Flag debugFlag(argumentParser, "debug", "Debug mode", {"debug"}); + + args::ValueFlag pixelRatioValue(argumentParser, "number", "Image scale factor", {'r', "ratio"}); + + args::ValueFlag zoomValue(argumentParser, "number", "Zoom level", {'z', "zoom"}); + + args::ValueFlag lonValue(argumentParser, "degrees", "Longitude", {'x', "lon"}); + args::ValueFlag latValue(argumentParser, "degrees", "Latitude", {'y', "lat"}); + args::ValueFlag bearingValue(argumentParser, "degrees", "Bearing", {'b', "bearing"}); + args::ValueFlag pitchValue(argumentParser, "degrees", "Pitch", {'p', "pitch"}); + args::ValueFlag widthValue(argumentParser, "pixels", "Image width", {'w', "width"}); + args::ValueFlag heightValue(argumentParser, "pixels", "Image height", {'h', "height"}); + + try { + argumentParser.ParseCLI(argc, argv); + } catch (const args::Help&) { + std::cout << argumentParser; + exit(0); + } catch (const args::ParseError& e) { + std::cerr << e.what() << std::endl; + std::cerr << argumentParser; + exit(1); + } catch (const args::ValidationError& e) { + std::cerr << e.what() << std::endl; + std::cerr << argumentParser; + exit(2); + } + + std::string style = styleValue ? args::get(styleValue) : mbgl::util::default_styles::streets.url; + const double lat = latValue ? args::get(latValue) : 0; + const double lon = lonValue ? args::get(lonValue) : 0; + const double zoom = zoomValue ? args::get(zoomValue) : 0; + const double bearing = bearingValue ? args::get(bearingValue) : 0; + const double pitch = pitchValue ? args::get(pitchValue) : 0; + const double pixelRatio = pixelRatioValue ? args::get(pixelRatioValue) : 1; + + const uint32_t width = widthValue ? args::get(widthValue) : 512; + const uint32_t height = heightValue ? args::get(heightValue) : 512; + const std::string output = outputValue ? args::get(outputValue) : "out.png"; + const std::string cache_file = cacheValue ? args::get(cacheValue) : "cache.sqlite"; + const std::string asset_root = assetsValue ? args::get(assetsValue) : "."; + + // Try to load the token from the environment. + const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN"); + const std::string token = tokenValue ? args::get(tokenValue) : (tokenEnv ? tokenEnv : std::string()); + + const bool debug = debugFlag ? args::get(debugFlag) : false; + + using namespace mbgl; + + util::RunLoop loop; + + HeadlessFrontend frontend({ width, height }, pixelRatio); + Map map(frontend, MapObserver::nullObserver(), + MapOptions().withMapMode(MapMode::Static).withSize(frontend.getSize()).withPixelRatio(pixelRatio), + ResourceOptions().withCachePath(cache_file).withAssetPath(asset_root).withAccessToken(std::string(token))); + + if (style.find("://") == std::string::npos) { + style = std::string("file://") + style; + } + + map.getStyle().loadURL(style); + map.jumpTo(CameraOptions() + .withCenter(LatLng { lat, lon }) + .withZoom(zoom) + .withBearing(bearing) + .withPitch(pitch)); + + if (debug) { + map.setDebug(debug ? mbgl::MapDebugOptions::TileBorders | mbgl::MapDebugOptions::ParseStatus : mbgl::MapDebugOptions::NoDebug); + } + + try { + std::ofstream out(output, std::ios::binary); + out << encodePNG(frontend.render(map).image); + out.close(); + } catch(std::exception& e) { + std::cout << "Error: " << e.what() << std::endl; + exit(1); + } + + return 0; +} diff --git a/mapbox-gl-native/circle.yml b/mapbox-gl-native/circle.yml new file mode 100644 index 000000000..62f9963b9 --- /dev/null +++ b/mapbox-gl-native/circle.yml @@ -0,0 +1,766 @@ +version: 2.1 + +# +# This CI configuration heavily relies on templates and CMake. Most of the +# jobs will have the following steps (usually configurable via parameters): +# prepare, build and test. +# +# 'sanity-checks' and 'baselines' are special. The former will run a series +# of checks to see if the patch has the minimum requirements for landing in +# our repository and the latter will generate new baselines for the tests +# in case the PR changes the baselines. +# +workflows: + version: 2 + development: + jobs: + - sanity-checks + - build-template: + name: android-armeabi-v7a-release + executor_name: ubuntu-eoan + target_is_android: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_INSTALL_PREFIX=/tmp/workspace/install/$CIRCLE_JOB -DCMAKE_TOOLCHAIN_FILE=/opt/android/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_CCACHE=/usr/bin/ccache -DANDROID_ABI=armeabi-v7a' + install: true + - build-template: + name: android-arm64-v8a-release + executor_name: ubuntu-eoan + target_is_android: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_INSTALL_PREFIX=/tmp/workspace/install/$CIRCLE_JOB -DCMAKE_TOOLCHAIN_FILE=/opt/android/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_CCACHE=/usr/bin/ccache -DANDROID_ABI=arm64-v8a' + build_params: '--target libmbgl-render-test-runner.so' + install: true + - build-template: + name: android-x86-release + executor_name: ubuntu-eoan + target_is_android: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_INSTALL_PREFIX=/tmp/workspace/install/$CIRCLE_JOB -DCMAKE_TOOLCHAIN_FILE=/opt/android/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_CCACHE=/usr/bin/ccache -DANDROID_ABI=x86' + build_params: '--target libmbgl-render-test-runner.so' + install: true + - build-template: + name: android-x86_64-release + executor_name: ubuntu-eoan + target_is_android: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_INSTALL_PREFIX=/tmp/workspace/install/$CIRCLE_JOB -DCMAKE_TOOLCHAIN_FILE=/opt/android/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_CCACHE=/usr/bin/ccache -DANDROID_ABI=x86_64' + build_params: '--target libmbgl-render-test-runner.so' + install: true + - build-template: + name: linux-gcc8-release + executor_name: ubuntu-eoan + target_is_linux: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_INSTALL_PREFIX=/tmp/workspace/install/$CIRCLE_JOB -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8' + install: true + metrics: true + style_tests: true + - build-template: + name: linux-gcc5-release + executor_name: ubuntu-eoan + target_is_linux: true + requires: + - linux-gcc8-release + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-5 -DCMAKE_CXX_COMPILER=g++-5' + build_params: '--target mbgl-glfw' + test_params: '-N -Q' + - build-template: + name: linux-gcc8-debug-coverage + executor_name: ubuntu-eoan + target_is_linux: true + requires: + - linux-gcc8-release + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_BUILD_TYPE=Debug -DMBGL_WITH_COVERAGE=ON' + style_tests: true + upload_coverage: true + - build-template: + name: linux-clang8-release + executor_name: ubuntu-eoan + target_is_linux: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_INSTALL_PREFIX=/tmp/workspace/install/$CIRCLE_JOB -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8' + install: true + metrics: true + style_tests: true + - build-template: + name: FIXME-linux-asan + executor_name: ubuntu-eoan + target_is_linux: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DMBGL_WITH_SANITIZER=address' + test_params: '|| true' + style_tests: true + - build-template: + name: linux-tsan + executor_name: ubuntu-eoan + target_is_linux: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DMBGL_WITH_SANITIZER=thread' + style_tests: true + - build-template: + name: FIXME-linux-valgrind + executor_name: ubuntu-eoan + target_is_linux: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_CXX_FLAGS=-DSANITIZE' + test_params: '-j 4 -E "mbgl-node|gl-benchmark" -D ExperimentalMemCheck || true' + - build-template: + name: linux-ubsan + executor_name: ubuntu-eoan + target_is_linux: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8 -DMBGL_WITH_SANITIZER=undefined' + style_tests: true + - build-template: + name: qt5-linux-gcc5-release + executor_name: ubuntu-eoan + target_is_linux: true + requires: + - sanity-checks + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-5 -DCMAKE_CXX_COMPILER=g++-5 -DMBGL_WITH_QT=ON' + - build-template: + name: qt5-macos-gcc5-release + executor_name: macos-11_1_0 + target_is_macos: true + requires: + - linux-gcc8-release + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DMBGL_WITH_QT=ON -DCMAKE_PREFIX_PATH=$(echo /usr/local/Cellar/qt/5.*/lib/cmake)' + - build-template: + name: macos-xcode11-release + executor_name: macos-11_1_0 + target_is_macos: true + requires: + - linux-gcc8-release + config_params: '-G Ninja -DCMAKE_INSTALL_PREFIX=/tmp/workspace/install/$CIRCLE_JOB -DCMAKE_CXX_COMPILER_LAUNCHER=ccache' + install: true + metrics: true + style_tests: true + - build-template: + name: macos-xcode11-debug + executor_name: macos-11_1_0 + target_is_macos: true + requires: + - macos-xcode11-release + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Debug -DMBGL_WITH_COVERAGE=ON' + style_tests: true + upload_coverage: true + - build-template: + name: ios-xcode11-release + executor_name: macos-11_3_1 + target_is_macos: true + requires: + - linux-gcc8-release + config_params: '-G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_ARCHITECTURES=arm64e -DCMAKE_OSX_SYSROOT=iphoneos -DMBGL_WITH_IOS_CCACHE=ON' + build_params: '--config Release' + - android-render-test-runner: + requires: + - linux-gcc8-release +# - android-unit-test-runner: +# requires: +# - linux-gcc8-release + - ios-render-test-runner: + requires: + - linux-gcc8-release + - ios-unit-test-runner: + requires: + - linux-gcc8-release + - baselines: + requires: + - android-arm64-v8a-release + - android-armeabi-v7a-release + - android-render-test-runner + - android-x86-release + - android-x86_64-release + - linux-clang8-release + - linux-gcc8-release + - macos-xcode11-release + - ios-render-test-runner + - trigger-pipeline: + requires: + - baselines + name: mapboxci + slug: mapbox/mapbox-gl-native-internal + - android-api-breakage: + requires: + - baselines + nightly: + triggers: + - schedule: + cron: "0 5 * * *" + filters: + branches: + only: + - master + jobs: + - build-template: + name: linux-gcc8-debug-coverage-nightly + executor_name: ubuntu-eoan + target_is_linux: true + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_BUILD_TYPE=Debug -DMBGL_WITH_COVERAGE=ON' + style_tests: true + upload_coverage: true + - build-template: + name: macos-xcode11-debug-coverage-nightly + executor_name: macos-11_1_0 + target_is_macos: true + requires: + - linux-gcc8-debug-coverage-nightly + config_params: '-G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Debug -DMBGL_WITH_COVERAGE=ON' + style_tests: true + upload_coverage: true + publish_coverage: true + +# +# Executors: we currently support two executors, one based on Ubuntu 19.04 aka Disco +# and another based on macOS + Xcode 11.1.0. The Ubuntu executor is a lot more stable +# and will produce reproduceable builds completely offline because it uses a docker +# image with all the build dependencies preinstalled, whereas the macOS is managed +# by CircleCI and will install dependencies on build time. Build results from the macOS +# bots might differ if in meantime the macOS image gets updated. :-( +# +executors: + ubuntu-eoan: + docker: + # FIXME: Move the image to mbgl/ + - image: tmpsantos/mbgl_ci:1.9 + resource_class: xlarge + working_directory: /src + environment: + DISPLAY: :99 + UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1:report_error_type=1 + ASAN_OPTIONS: strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:halt_on_error=0 + QT_INSTALL_DOCS: /usr/share/qt5/doc + QT_VERSION: 5 + TSAN_OPTIONS: suppressions=/src/platform/linux/tsan_suppress.txt + macos-11_1_0: + macos: + xcode: '11.1.0' + environment: + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 + macos-11_3_1: + macos: + xcode: '11.3.1' + environment: + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 + +commands: + prepare: + steps: + - restore_cache: + keys: + - 'ccache-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}-{{ .Revision }}' + - 'ccache-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}-' + - 'ccache-v1-{{ .Environment.CIRCLE_JOB }}-master' + - run: + name: Prepare + command: | + git submodule sync + git submodule update --init --recursive + git gc + npm install --ignore-scripts + ulimit -c unlimited + prepare-linux: + steps: + - run: + name: Prepare Linux + background: true + command: | + Xvfb :99 -noreset -screen 0 1280x1024x24 + prepare-macos: + steps: + - run: + name: Prepare macOS + command: | + brew install cmake ccache glfw ninja pkgconfig qt chargepoint/xcparse/xcparse + brew cask install google-cloud-sdk + pip install ansi2html scipy + config: + parameters: + config_params: + type: string + steps: + - run: + name: Configure + command: | + cmake . -B build << parameters.config_params >> + build: + parameters: + build_params: + type: string + steps: + - run: + name: Build + command: | + ccache --zero-stats --max-size=2G + cmake --build build -j $(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null) << parameters.build_params >> + ccache --show-stats + install: + steps: + - run: + name: Install + command: | + cmake --build build --target install/strip + test: + parameters: + test_params: + type: string + steps: + - run: + name: Test + command: | + cd build + ctest -V << parameters.test_params >> + metrics: + parameters: + step_name: + type: string + metrics_params: + type: string + steps: + - run: + name: << parameters.step_name >> + command: | + build/mbgl-render-test-runner << parameters.metrics_params >> + save: + steps: + - save_cache: + key: 'ccache-v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}-{{ .Revision }}' + paths: + - .git/modules + - /Users/distiller/Library/Caches/Homebrew + - node_modules + - ~/.ccache + - ~/.gradle + - run: + name: Collecting results + when: always + command: | + mkdir -p /tmp/workspace/metrics && touch /tmp/workspace/.$CIRCLE_JOB + if [ -d metrics/$CIRCLE_JOB ]; then cp -r metrics/$CIRCLE_JOB /tmp/workspace/metrics; fi + mkdir -p /tmp/tests/baselines + if [ -f baselines.patch ]; then cp baselines.patch /tmp/tests/baselines; fi + mkdir -p /tmp/tests/nitpick + if [ -f nitpick.patch ]; then cp nitpick.patch /tmp/tests/nitpick; fi + mkdir -p /tmp/tests/render + if [ -f clang-tidy.log ]; then cp clang-tidy.log /tmp/tests/clang-tidy; fi + mkdir -p /tmp/tests/render + if ls render-test/*.html 1> /dev/null 2>&1; then cp render-test/*.html /tmp/tests/render; fi + mkdir -p /tmp/tests/metrics + if ls metrics/*.html 1> /dev/null 2>&1; then cp metrics/*.html /tmp/tests/metrics; fi + mkdir -p /tmp/tests/coredumps + if ls core* 1> /dev/null 2>&1; then cp core* /tmp/tests/coredumps; fi + mkdir -p /tmp/tests/valgrind + if ls build/Testing/Temporary/MemoryChecker.*.log 1> /dev/null 2>&1; then cp build/Testing/Temporary/MemoryChecker.*.log /tmp/tests/valgrind; fi + - persist_to_workspace: + root: /tmp/workspace + paths: + - '*' + - store_artifacts: + path: /tmp/tests + destination: tests + login-google-cloud-platform: + steps: + - run: + name: Login Google Cloud Platform + command: | + echo "${GCLOUD_SERVICE_ACCOUNT_JSON}" > secret.json + gcloud auth activate-service-account --key-file secret.json --project android-gl-native + rm secret.json + login-google-cloud-platform-ios: + steps: + - run: + name: Login to IOS Google Cloud Platform + command: | + if [[ -n "${GCLOUD_SERVICE_ACCOUNT_JSON_IOS}" ]]; then + echo "${GCLOUD_SERVICE_ACCOUNT_JSON_IOS}" > iosAccount.json + gcloud auth activate-service-account --key-file iosAccount.json --project ios-mapbox-gl-native + rm iosAccount.json + fi + prepare-ios-codesign-keychain: + parameters: + directory: + type: string + steps: + - run: + name: Prepare codesign keychain + command: | + cd << parameters.directory >> + fastlane run create_keychain name:fastlane_keychain password:$FASTLANE_PASSWORD timeout:false unlock:true + fastlane match + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $FASTLANE_PASSWORD fastlane_keychain + codesign-ios-test-runner: + parameters: + directory: + type: string + steps: + - run: + name: Codesign iOS TestApp for running on a physical device + command: | + cp -r << parameters.directory >>/codesigning build + cd build + ./codesigning/generate-entitlements.swift + ./codesigning/codesign_all.sh + codesign -dv --verbose=4 Release-iphoneos/*.app # for debugging that app is signed or not + run-ios-test-app: + parameters: + directory: + type: string + steps: + - run: + name: Run iOS Test App on Firebase + no_output_timeout: 30m + command: | + if [[ -n "${GCLOUD_SERVICE_ACCOUNT_JSON_IOS}" ]]; then + # arrange files in a way Firebase expects it, and package them in a zip file + cp << parameters.directory >>/*_iphoneos13.2-arm64e-release.xctestrun build + cd build + zip testapp.zip -r Release-iphoneos/*.app --quiet + zip testapp.zip *_iphoneos13.2-arm64e-release.xctestrun + gcloud firebase test ios models list + gcloud firebase test ios run \ + --test testapp.zip \ + --device model=iphone11,version=13.3,locale=en,orientation=portrait --xcode-version=11.3.1 --timeout 30m \ + --verbosity=debug --no-record-video --results-dir ios-test-app-${CIRCLE_BUILD_NUM} + fi + upload-coverage-results: + steps: + - run: + name: Get coverage results + command: | + cd build + find . -type f -iname *.gcda -exec gcov -p -b {} \; + - run: + name: Upload coverage results to codecov.io + command: | + bash <(curl -sSfL https://codecov.io/bash) -X gcov || echo 'Codecov failed to upload' + publish-coverage-metrics: + steps: + - run: + name: Upload coverage metrics to s3 + command: | + if [[ $CIRCLE_BRANCH == master ]]; then + scripts/publish_core_codecoverage.js -p Linux -s Core + fi + +jobs: + ios-render-test-runner: + executor: macos-11_3_1 + steps: + - checkout + - prepare + - prepare-macos + - prepare-ios-codesign-keychain: + directory: render-test/ios + - config: + config_params: '-G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos -DMBGL_IOS_RENDER_TEST=ON -DCMAKE_OSX_ARCHITECTURES=arm64e -DMBGL_WITH_IOS_CCACHE=ON' + - build: + build_params: '--config Release' + - codesign-ios-test-runner: + directory: render-test/ios + - login-google-cloud-platform-ios + - run-ios-test-app: + directory: render-test/ios + - run: + name: Retrieve Test Result from gcloud + when: always + command: | + if [[ -n "${GCLOUD_SERVICE_ACCOUNT_JSON_IOS}" ]]; then + testResult=$(gsutil ls -d gs://test-lab-hn7kwqcz3108z-hjm4qtbqqkymk/ios-test-app-${CIRCLE_BUILD_NUM}/iphone11-13.3-en-portrait/TestLogs/) + mkdir -p render-test-app + gsutil -m cp -r $testResult render-test-app + cd render-test-app/TestLogs + xcparse attachments *.xcresult && ls + cd ../.. && mkdir -p /tmp/tests/ios-render-test + if ls render-test-app/TestLogs/*.html 1> /dev/null 2>&1; then cp render-test-app/TestLogs/*.html /tmp/tests/ios-render-test; fi + if [[ -n $(ls render-test-app/TestLogs/*.zip 2>/dev/null) ]]; then + cp render-test-app/TestLogs/*.zip metrics/ios-render-test-runner/ + cp render-test-app/TestLogs/*.zip /tmp/tests/ios-render-test + cd metrics/ios-render-test-runner/ + unzip -o *.zip + rm -rf *.zip + fi + fi + - save + ios-unit-test-runner: + executor: macos-11_3_1 + steps: + - checkout + - prepare + - prepare-macos + - prepare-ios-codesign-keychain: + directory: test/ios + - config: + config_params: '-G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos -DMBGL_IOS_UNIT_TEST=ON -DCMAKE_OSX_ARCHITECTURES=arm64e -DMBGL_WITH_IOS_CCACHE=ON' + - build: + build_params: '--config Release' + - codesign-ios-test-runner: + directory: test/ios + - login-google-cloud-platform-ios + - run-ios-test-app: + directory: test/ios + - run: + name: Retrieve Test Result from gcloud + when: always + command: | + if [[ -n "${GCLOUD_SERVICE_ACCOUNT_JSON_IOS}" ]]; then + testResult=$(gsutil ls -d gs://test-lab-hn7kwqcz3108z-hjm4qtbqqkymk/ios-test-app-${CIRCLE_BUILD_NUM}/iphone11-13.3-en-portrait/TestLogs/) + mkdir -p unit-test-app + gsutil -m cp -r $testResult unit-test-app + cd unit-test-app/TestLogs + xcparse attachments *.xcresult && ls + cd ../.. && mkdir -p /tmp/tests/unit-test + if ls unit-test-app/TestLogs/*.xml 1> /dev/null 2>&1; then cp unit-test-app/TestLogs/*.xml /tmp/tests/unit-test/results.xml; fi + fi + - save + android-api-breakage: + executor: ubuntu-eoan + steps: + - run: + name: Clone mapbox-gl-native-android in working directory + command: git clone https://github.com/mapbox/mapbox-gl-native-android.git . + - prepare + - run: + name: Update mapbox-gl-native to match PR + command: cd vendor/mapbox-gl-native && git fetch origin $CIRCLE_SHA1 && git checkout $CIRCLE_SHA1 && git submodule update --init --recursive + - run: + name: Build SDK + command: BUILDTYPE=Release make android-arm-v8 + - save + android-unit-test-runner: + executor: ubuntu-eoan + steps: + - checkout + - prepare + - run: + name: Build UnitTestRunner APK + command: | + cd test/android + ./gradlew --parallel --max-workers=8 -Pmapbox.abis=arm64-v8a app:assembleRelease app:assembleAndroidTest + - login-google-cloud-platform + - run: + name: Run UnitTestRunner on Firebase + no_output_timeout: 20m + command: | + gcloud firebase test android models list + gcloud firebase test android run --type instrumentation \ + --app test/android/app/build/outputs/apk/release/app-release.apk \ + --test test/android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk \ + --device-ids flame --os-version-ids 29 --locales en --orientations portrait --timeout 20m \ + --directories-to-pull /sdcard/test/results --results-dir unit-test-${CIRCLE_BUILD_NUM} \ + --no-record-video --no-performance-metrics + - run: + name: Retrieve Test Result from gcloud + when: always + command: | + testResult=$(gsutil ls gs://test-lab-186672a0qp5bq-ycr70axads3nc/unit-test-${CIRCLE_BUILD_NUM}/**/results/results.xml) + mkdir -p /tmp/tests/unit-test + gsutil cp $testResult /tmp/tests/unit-test + - save + android-render-test-runner: + executor: ubuntu-eoan + steps: + - checkout + - prepare + - run: + name: Build RenderTestRunner APK + command: | + cd render-test/android + ./gradlew --parallel --max-workers=8 -Pmapbox.abis=arm64-v8a app:assembleRelease app:assembleAndroidTest + - login-google-cloud-platform + - run: + name: Run RenderTestRunner Instrumentation Test on Firebase + no_output_timeout: 20m + command: | + gcloud firebase test android models list + gcloud firebase test android run --type instrumentation \ + --app render-test/android/app/build/outputs/apk/release/app-release.apk \ + --test render-test/android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk \ + --device-ids flame --os-version-ids 29 --locales en --orientations portrait --timeout 20m \ + --directories-to-pull /sdcard --results-dir render-test-app-${CIRCLE_BUILD_NUM} \ + --no-record-video --no-performance-metrics + - run: + name: Retrieve TestResult from gcloud + when: always + command: | + testResult=$(gsutil ls gs://test-lab-186672a0qp5bq-ycr70axads3nc/render-test-app-${CIRCLE_BUILD_NUM}/**/*.html) + mkdir -p /tmp/tests/render-test-app + gsutil cp $testResult /tmp/tests/render-test-app + - run: + name: Retrieve Rebaselined Metrics from gcloud + command: | + metricsResult=$(gsutil ls -d gs://test-lab-186672a0qp5bq-ycr70axads3nc/render-test-app-${CIRCLE_BUILD_NUM}/*/*/sdcard/baselines/*) || true + gsutil -m cp -r $metricsResult metrics/android-render-test-runner/ || true + mkdir -p /tmp/tests/render-test-app/rebaselined_metrics + gsutil -m cp -r $metricsResult /tmp/tests/render-test-app/rebaselined_metrics || true + - save + trigger-pipeline: + executor: ubuntu-eoan + parameters: + slug: + type: string + steps: + - checkout + - run: + name: Trigger Pipeline + command: | + scripts/ci-circleci-start-pipeline.py --target-slug << parameters.slug >> + sanity-checks: + executor: ubuntu-eoan + steps: + - checkout + - prepare + - config: + config_params: '-G Ninja -DCMAKE_C_COMPILER=clang-8 -DCMAKE_CXX_COMPILER=clang++-8' + - run: + name: Code Generators + command: | + cp build/mbgl-core.license LICENSE.mbgl-core.md + platform/default/include/mbgl/storage/offline_schema.js + scripts/generate-shaders.js + scripts/generate-style-code.js + git add -A && git diff --staged --exit-code | tee nitpick.patch + - run: + name: Validation Scripts + command: | + scripts/nitpick/submodule-pin.js + - run: + name: CMake Format + command: | + cmake-format -i $(find -name '*.cmake' -and -not -path './vendor/*/*' -and -not -path './build/*') + cmake-format -i $(find -name CMakeLists.txt -and -not -path './vendor/*/*' -and -not -path './build/*') + git diff --exit-code | tee nitpick.patch + - run: + name: Clang Format + command: | + git diff -U0 --ignore-submodules=all --no-color origin/master... *.cpp *.hpp | clang-format-diff-8 -p1 -i + git diff --exit-code | tee nitpick.patch + - run: + name: Clang Tidy + command: | + run-clang-tidy-8 -quiet -j $(nproc) -header-filter='.*' -p build $PWD/src/.*cpp $PWD/platform/.*cpp + - save + baselines: + executor: ubuntu-eoan + steps: + - checkout + - prepare + - prepare-linux + - attach_workspace: + at: /tmp/attach + - run: + name: 'Binary Size' + command: | + /tmp/attach/install/linux-gcc8-release/bin/mbgl-render-test-runner -u rebaseline -p metrics/binary-size.json + - run: + name: 'Metrics Baselines' + command: | + cp -r /tmp/attach/metrics . + git add -A && git diff --staged --exit-code | tee baselines.patch + - save + build-template: + parameters: + config_params: + type: string + default: '' + build_params: + type: string + default: '' + test_params: + type: string + default: '' + executor_name: + type: string + target_is_android: + type: boolean + default: false + target_is_linux: + type: boolean + default: false + target_is_macos: + type: boolean + default: false + install: + type: boolean + default: false + metrics: + type: boolean + default: false + style_tests: + type: boolean + default: false + upload_coverage: + type: boolean + default: false + publish_coverage: + type: boolean + default: false + executor: << parameters.executor_name >> + steps: + - checkout + - when: + condition: << parameters.target_is_android >> + steps: + - prepare + - config: + config_params: << parameters.config_params >> + - build: + build_params: << parameters.build_params >> + - when: + condition: << parameters.target_is_linux >> + steps: + - prepare + - prepare-linux + - config: + config_params: << parameters.config_params >> + - build: + build_params: << parameters.build_params >> + - test: + test_params: << parameters.test_params >> + - when: + condition: << parameters.target_is_macos >> + steps: + - prepare + - prepare-macos + - config: + config_params: << parameters.config_params >> + - build: + build_params: << parameters.build_params >> + - test: + test_params: << parameters.test_params >> + - when: + condition: << parameters.install >> + steps: + - install + - when: + condition: << parameters.style_tests >> + steps: + - metrics: + step_name: 'Style Tests' + metrics_params: '-u rebaseline -p metrics/$CIRCLE_JOB-style.json' + - when: + condition: << parameters.metrics >> + steps: + - metrics: + step_name: 'Metrics' + metrics_params: '-u rebaseline -p metrics/$CIRCLE_JOB-metrics.json' + - when: + condition: << parameters.upload_coverage >> + steps: + - upload-coverage-results + - when: + condition: << parameters.publish_coverage >> + steps: + - publish-coverage-metrics + - save diff --git a/mapbox-gl-native/cloudformation/ci.template.js b/mapbox-gl-native/cloudformation/ci.template.js new file mode 100644 index 000000000..e0e1fc060 --- /dev/null +++ b/mapbox-gl-native/cloudformation/ci.template.js @@ -0,0 +1,117 @@ +'use strict'; + +const template = { + AWSTemplateFormatVersion: '2010-09-09', + Description: 'mapbox-gl-native travis resources', + Resources: { + BuildUser: { + Type: 'AWS::IAM::User', + Properties: { + Policies: [ + { + PolicyName: 'listBuckets', + PolicyDocument: { + Statement: [ + { + Action: [ 's3:ListBucket' ], + Effect: 'Allow', + Resource: [ 'arn:aws:s3:::mapbox' ], + Condition: { StringLike: { 's3:prefix': 'mapbox-gl-native/*' } } + }, + { + Action: [ 's3:ListBucket' ], + Resource: [ 'arn:aws:s3:::mapbox-node-binary' ], + Effect: 'Allow' + }, + { + Action: [ 's3:ListBucket' ], + Resource: [ 'arn:aws:s3:::mapbox-loading-dock' ], + Effect: 'Allow' + } + ] + } + }, + { + PolicyName: 'build-testing', + PolicyDocument: { + Statement: [ + { + Action: [ + 's3:GetObject', + 's3:GetObjectAcl', + 's3:PutObject', + 's3:PutObjectAcl' + ], + Effect: 'Allow', + Resource: [ + 'arn:aws:s3:::mapbox/mapbox-gl-native/*', + 'arn:aws:s3:::mapbox-node-binary/@mapbox/mapbox-gl-native/*', + 'arn:aws:s3:::mapbox/mapbox-gl-native/ios/builds/*' + ] + } + ] + } + }, + { + PolicyName: 'cloudwatch-metrics', + PolicyDocument: { + Statement: [ + { + Action: [ + 'cloudwatch:PutMetricData', + 'cloudwatch:GetMetricData', + 'cloudwatch:GetMetricStatistics' + ], + Effect: 'Allow', + Resource: [ '*' ] + } + ] + } + }, + { + PolicyName: 'get-signing-key', + PolicyDocument: { + Statement: [ + { + Action: [ 's3:GetObject' ], + Effect: 'Allow', + Resource: [ + 'arn:aws:s3:::mapbox/android/signing-credentials/secring.gpg' + ] + } + ] + } + }, + { + PolicyName: 'publish-metrics', + PolicyDocument: { + Statement: [ + { + Action: [ 's3:PutObject', 's3:GetObject', 's3:GetObjectAcl' ], + Effect: 'Allow', + Resource: [ + 'arn:aws:s3:::mapbox-loading-dock/raw/mobile.binarysize/*', + 'arn:aws:s3:::mapbox-loading-dock/raw/mobile.codecoverage/*', + 'arn:aws:s3:::mapbox-loading-dock/raw/mobile_staging.docs_coverage/*', + 'arn:aws:s3:::mapbox-loading-dock/raw/mobile_staging.codecoverage/*', + 'arn:aws:s3:::mapbox-loading-dock/raw/mobile_staging.github_stats/*' + ] + } + ] + } + } + ] + } + }, + BuildUserKey: { + Type: 'AWS::IAM::AccessKey', + Properties: { UserName: { Ref: 'BuildUser' } } + } + }, + Outputs: { + AccessKeyId: { Value: { Ref: 'BuildUserKey' } }, + SecretAccessKey: { Value: { 'Fn::GetAtt': [ 'BuildUserKey', 'SecretAccessKey' ] } } + } +}; + +module.exports = template; diff --git a/mapbox-gl-native/codecov.yml b/mapbox-gl-native/codecov.yml new file mode 100644 index 000000000..e697b82bb --- /dev/null +++ b/mapbox-gl-native/codecov.yml @@ -0,0 +1,12 @@ +ignore: + - "benchmark/" + - "expression-test/" + - "render-test/" + - "test/" + - "vendor/" + +coverage: + status: + project: + default: + threshold: 1% diff --git a/mapbox-gl-native/expression-test/CMakeLists.txt b/mapbox-gl-native/expression-test/CMakeLists.txt new file mode 100644 index 000000000..f9120ac7c --- /dev/null +++ b/mapbox-gl-native/expression-test/CMakeLists.txt @@ -0,0 +1,42 @@ +add_executable( + mbgl-expression-test + ${PROJECT_SOURCE_DIR}/expression-test/expression_test_logger.cpp + ${PROJECT_SOURCE_DIR}/expression-test/expression_test_logger.hpp + ${PROJECT_SOURCE_DIR}/expression-test/expression_test_parser.cpp + ${PROJECT_SOURCE_DIR}/expression-test/expression_test_parser.hpp + ${PROJECT_SOURCE_DIR}/expression-test/expression_test_runner.cpp + ${PROJECT_SOURCE_DIR}/expression-test/expression_test_runner.hpp + ${PROJECT_SOURCE_DIR}/expression-test/test_runner_common.cpp + ${PROJECT_SOURCE_DIR}/expression-test/test_runner_common.hpp + ${PROJECT_SOURCE_DIR}/expression-test/main.cpp +) + +target_compile_definitions( + mbgl-expression-test + PRIVATE TEST_RUNNER_ROOT_PATH="${PROJECT_SOURCE_DIR}" +) + +# FIXME: Should not use core private interface +target_include_directories( + mbgl-expression-test + PRIVATE ${PROJECT_SOURCE_DIR}/src +) + +target_link_libraries( + mbgl-expression-test + PRIVATE + Mapbox::Base + Mapbox::Base::Extras::args + Mapbox::Base::Extras::filesystem + mbgl-core +) + +set_property(TARGET mbgl-expression-test PROPERTY FOLDER Executables) + +string(RANDOM LENGTH 5 ALPHABET 0123456789 MBGL_EXPRESSION_TEST_SEED) + +add_test( + NAME mbgl-expression-test + COMMAND mbgl-expression-test -s --seed=${MBGL_EXPRESSION_TEST_SEED} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} +) diff --git a/mapbox-gl-native/expression-test/expression_test_logger.cpp b/mapbox-gl-native/expression-test/expression_test_logger.cpp new file mode 100644 index 000000000..9127a6d7a --- /dev/null +++ b/mapbox-gl-native/expression-test/expression_test_logger.cpp @@ -0,0 +1,177 @@ +#include "expression_test_logger.hpp" +#include "expression_test_runner.hpp" +#include "filesystem.hpp" + +#include +#include + +#include + +using namespace mbgl; +using namespace std::literals; + +namespace { + +const char* resultsStyle = R"HTML( + +)HTML"; + +const char* resultsHeaderButtons = R"HTML( + + + + +)HTML"; + +const char* resultsScript = R"HTML( + +)HTML"; + +std::string createResultItem(const TestRunOutput& result, const std::string& status, bool shouldHide) { + std::ostringstream html; + html << "
\n"; + html << R"(

)" << status << " " << result.id << "

\n"; + + html << "

"s << result.expression << "

\n"s; + if (result.passed) { + html << "Serialized:

"s << result.serialized << "

\n"s; + } else { + html << "

Difference:

" << result.text << "

\n"; + } + html << "
\n"; + + return html.str(); +} + +std::string createResultPage(const TestStats& stats, bool shuffle, uint32_t seed) { + const std::size_t unsuccessfulCount = stats.errored.size() + stats.failed.size(); + const bool unsuccessful = unsuccessfulCount > 0; + std::ostringstream resultsPage; + + // Style + resultsPage << resultsStyle; + + // Header with buttons + if (unsuccessful) { + resultsPage << R"HTML(

)HTML"; + resultsPage << util::toString(unsuccessfulCount) << " tests failed."; + } else { + resultsPage << R"HTML(

)HTML"; + resultsPage << "All tests passed!"; + } + + resultsPage << resultsHeaderButtons; + + // Test sequence + { + resultsPage << "
\n"; + + // Failed tests + if (unsuccessful) { + resultsPage << "

Failed tests:"; + for (const auto& failed : stats.failed) { + resultsPage << failed.id << " "; + } + resultsPage << "

Errored tests:"; + for (const auto& errored : stats.errored) { + resultsPage << errored.id << " "; + } + resultsPage << "

\n"; + } + + // Test sequence + resultsPage << "

Test sequence: "; + for (const auto& id : stats.ids) { + resultsPage << id << " "; + } + resultsPage << "

\n"; + + // Shuffle + if (shuffle) { + resultsPage << "

Shuffle seed: " << util::toString(seed) << "

\n"; + } + + resultsPage << "
\n"; + } + + // Script + resultsPage << resultsScript; + + // Tests + resultsPage << "
\n"; + const auto appendResult = [&] (const auto& results, const std::string& status, bool hide = false) { + for (const auto& result : results) { + resultsPage << createResultItem(result, status, hide); + } + }; + + appendResult(stats.passed, "passed"s, unsuccessful); + appendResult(stats.failed, "failed"s); + appendResult(stats.errored, "errored"s); + appendResult(stats.ignorePassed, "ignored passed"s, unsuccessful); + appendResult(stats.ignoreFailed, "ignored"s, true); + resultsPage << "
\n"; + + return resultsPage.str(); +} + +} // namespace + +void printStats(const TestStats& stats) { + const std::size_t count = stats.testCount(); + if (std::size_t passedTests = stats.passed.size()) { + printf(ANSI_COLOR_GREEN "%zu passed (%.1lf%%)" ANSI_COLOR_RESET "\n", passedTests, 100.0 * passedTests / count); + } + if (std::size_t ignorePassedTests = stats.ignorePassed.size()) { + printf(ANSI_COLOR_YELLOW "%zu passed but were ignored (%.1lf%%)" ANSI_COLOR_RESET "\n", ignorePassedTests, 100.0 * ignorePassedTests / count); + } + if (std::size_t ignoreFailedTests = stats.ignoreFailed.size()) { + printf(ANSI_COLOR_LIGHT_GRAY "%zu ignored (%.1lf%%)" ANSI_COLOR_RESET "\n", ignoreFailedTests, 100.0 * ignoreFailedTests / count); + } + if (std::size_t failedTests = stats.failed.size()) { + printf(ANSI_COLOR_RED "%zu failed (%.1lf%%)" ANSI_COLOR_RESET "\n", failedTests, 100.0 * failedTests / count); + } + if (std::size_t erroredTests = stats.errored.size()) { + printf(ANSI_COLOR_RED "%zu errored (%.1lf%%)" ANSI_COLOR_RESET "\n", erroredTests, 100.0 * erroredTests / count); + } +} + +void writeHTMLResults(const TestStats& stats, const std::string& rootPath, bool shuffle, uint32_t seed) { + filesystem::path path = filesystem::path(rootPath) / "index.html"s; + try { + util::write_file(path.string(), createResultPage(stats, shuffle, seed)); + printf("Results at: %s\n", path.string().c_str()); + } catch (std::exception&) { + printf(ANSI_COLOR_RED "* ERROR can't write result page %s" ANSI_COLOR_RESET "\n", path.string().c_str()); + } +} diff --git a/mapbox-gl-native/expression-test/expression_test_logger.hpp b/mapbox-gl-native/expression-test/expression_test_logger.hpp new file mode 100644 index 000000000..95b0697cb --- /dev/null +++ b/mapbox-gl-native/expression-test/expression_test_logger.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_YELLOW "\x1b[33m" +#define ANSI_COLOR_LIGHT_GRAY "\x1b[90m" +#define ANSI_COLOR_RESET "\x1b[0m" + +class TestStats; + +void printStats(const TestStats&); +void writeHTMLResults(const TestStats&, const std::string&, bool, uint32_t); diff --git a/mapbox-gl-native/expression-test/expression_test_parser.cpp b/mapbox-gl-native/expression-test/expression_test_parser.cpp new file mode 100644 index 000000000..9edc3b986 --- /dev/null +++ b/mapbox-gl-native/expression-test/expression_test_parser.cpp @@ -0,0 +1,544 @@ +#include "expression_test_parser.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +using namespace mbgl; +using namespace mbgl::style; +using namespace mbgl::style::conversion; +using namespace std::literals; + +namespace { + +void writeJSON(rapidjson::PrettyWriter& writer, const Value& value) { + value.match( + [&] (const NullValue&) { writer.Null(); }, + [&] (bool b) { writer.Bool(b); }, + [&] (uint64_t u) { writer.Uint64(u); }, + [&] (int64_t i) { writer.Int64(i); }, + [&] (double d) { d == std::floor(d) ? writer.Int64(d) : writer.Double(d); }, + [&] (const std::string& s) { writer.String(s); }, + [&] (const std::vector& arr) { + writer.StartArray(); + for(const auto& item : arr) { + writeJSON(writer, item); + } + writer.EndArray(); + }, + [&] (const std::unordered_map& obj) { + writer.StartObject(); + for(const auto& entry : obj) { + writer.Key(entry.first.c_str()); + writeJSON(writer, entry.second); + } + writer.EndObject(); + } + ); +} + +using ErrorMessage = std::string; +using JSONReply = variant; +JSONReply readJson(const filesystem::path& jsonPath) { + auto maybeJSON = util::readFile(jsonPath); + if (!maybeJSON) { + return { "Unable to open file "s + jsonPath.string() }; + } + + JSDocument document; + document.Parse(*maybeJSON); + if (document.HasParseError()) { + return { formatJSONParseError(document) }; + } + + return { std::move(document) }; +} + +std::string toString(const JSValue& value) { + assert(value.IsString()); + return { value.GetString(), value.GetStringLength() }; +} + +optional toValue(const JSValue& jsvalue) { + if (jsvalue.IsNull()) { + return Value{}; + } + + if (jsvalue.IsArray()) { + std::vector values; + values.reserve(jsvalue.GetArray().Size()); + for (const auto& v : jsvalue.GetArray()) { + if (auto value = toValue(v)) { + values.emplace_back(std::move(*value)); + } + } + return {std::move(values)}; + } + + if (jsvalue.IsObject()) { + std::unordered_map value_map; + for (const auto& pair : jsvalue.GetObject()) { + if (auto value = toValue(pair.value)) { + value_map.emplace(toString(pair.name), std::move(*value)); + } + } + return {std::move(value_map)}; + } + + if (!jsvalue.IsArray() && !jsvalue.IsObject()) { + return toValue(Convertible(&jsvalue)); + } + + return nullopt; +} + +style::expression::type::Type stringToType(const std::string& type) { + using namespace style::expression; + if (type == "string"s || type == "number-format"s) { + return type::String; + } else if (type == "number"s) { + return type::Number; + } else if (type == "boolean"s) { + return type::Boolean; + } else if (type == "object"s) { + return type::Object; + } else if (type == "color"s) { + return type::Color; + } else if (type == "value"s) { + return type::Value; + } else if (type == "formatted"s) { + return type::Formatted; + } else if (type == "resolvedImage"s) { + return type::Image; + } + + // Should not reach. + assert(false); + return type::Null; +} + +optional toExpressionType(const PropertySpec& spec) { + using namespace style::expression; + if (spec.type == "array") { + type::Type itemType = spec.value.empty() ? type::Value : stringToType(spec.value); + if (spec.length) { + return {type::Array(itemType, spec.length)}; + } + return {type::Array(itemType)}; + } + + if (spec.type == "enum") { + return {type::String}; + } + + return spec.type.empty() ? nullopt : optional{stringToType(spec.type)}; +} + +void parseCompiled(const JSValue& compiledValue, TestData& data) { + const auto& compiled = compiledValue.GetObject(); + assert(compiled.HasMember("result")); + assert(compiled["result"].IsString()); + const std::string& result = toString(compiled["result"]); + data.expected.compiled.success = result == "success"; + + if (compiled.HasMember("isFeatureConstant")) { + assert(compiled["isFeatureConstant"].IsBool()); + data.expected.compiled.isFeatureConstant = compiled["isFeatureConstant"].GetBool(); + } + + if (compiled.HasMember("isZoomConstant")) { + assert(compiled["isZoomConstant"].IsBool()); + data.expected.compiled.isZoomConstant = compiled["isZoomConstant"].GetBool(); + } + + if (compiled.HasMember("type")) { + assert(compiled["type"].IsString()); + data.expected.compiled.serializedType = toString(compiled["type"]); + } + + if (compiled.HasMember("errors")) { + assert(compiled["errors"].IsArray()); + for (const auto& errorVal : compiled["errors"].GetArray()) { + assert(errorVal.IsObject()); + const auto& errorObject = errorVal.GetObject(); + assert(errorObject.HasMember("key")); + assert(errorObject.HasMember("error")); + + std::unordered_map errorMap; + errorMap.emplace("key"s, Value{toString(errorObject["key"])}); + errorMap.emplace("error"s, Value{toString(errorObject["error"])}); + data.expected.compiled.errors.emplace_back(Value{std::move(errorMap)}); + } + } +} + +void parseExpected(const JSValue& expectedValue, TestData& data) { + assert(expectedValue.IsObject()); + const auto& expected = expectedValue.GetObject(); + assert(expected.HasMember("compiled")); + parseCompiled(expected["compiled"], data); + + // set outputs + if (expected.HasMember("outputs")) { + data.expected.outputs = toValue(expected["outputs"]); + } + + // set serialized + if (expected.HasMember("serialized")) { + data.expected.serialized = toValue(expected["serialized"]); + } +} + +void parsePropertySpec(const JSValue& value, TestData& data) { + const auto& propertySpec = value.GetObject(); + PropertySpec spec; + + if (propertySpec.HasMember("type")) { + assert(propertySpec["type"].IsString()); + spec.type = toString(propertySpec["type"]); + } + + if (propertySpec.HasMember("value")) { + assert(propertySpec["value"].IsString()); + spec.value = toString(propertySpec["value"]); + } + + if (propertySpec.HasMember("length")) { + assert(propertySpec["length"].IsNumber()); + spec.length = propertySpec["length"].GetDouble(); + } + + if (propertySpec.HasMember("property-type")) { + assert(propertySpec["property-type"].IsString()); + spec.isDataDriven = true; + } + + if (propertySpec.HasMember("expression")) { + assert(propertySpec["expression"].IsObject()); + spec.expression = toValue(propertySpec["expression"]); + } + + data.spec = std::move(spec); +} + +bool parseInputs(const JSValue& inputsValue, TestData& data) { + assert(inputsValue.IsArray()); + for (const auto& input : inputsValue.GetArray()) { + assert(input.IsArray()); + assert(input.Size() == 2); + assert(input[0].IsObject()); + assert(input[1].IsObject()); + + // Parse evaluation context, zoom. + optional zoom; + const auto& evaluationContext = input[0].GetObject(); + if (evaluationContext.HasMember("zoom")) { + assert(evaluationContext["zoom"].IsNumber()); + zoom = evaluationContext["zoom"].GetDouble(); + } + + // Parse heatmap density + optional heatmapDensity; + if (evaluationContext.HasMember("heatmapDensity")) { + assert(evaluationContext["heatmapDensity"].IsNumber()); + heatmapDensity = evaluationContext["heatmapDensity"].GetDouble(); + } + + // Parse canonicalID + optional canonical; + if (evaluationContext.HasMember("canonicalID")) { + const auto& canonicalIDObject = evaluationContext["canonicalID"]; + assert(canonicalIDObject.IsObject()); + assert(canonicalIDObject.HasMember("z") && canonicalIDObject["z"].IsNumber()); + assert(canonicalIDObject.HasMember("x") && canonicalIDObject["x"].IsNumber()); + assert(canonicalIDObject.HasMember("y") && canonicalIDObject["y"].IsNumber()); + canonical = CanonicalTileID( + canonicalIDObject["z"].GetUint(), canonicalIDObject["x"].GetUint(), canonicalIDObject["y"].GetUint()); + } + + // Parse availableImages + std::set availableImages; + if (evaluationContext.HasMember("availableImages")) { + assert(evaluationContext["availableImages"].IsArray()); + for (const auto& image : evaluationContext["availableImages"].GetArray()) { + assert(image.IsString()); + availableImages.emplace(toString(image)); + } + } + + // Parse feature properties + Feature feature(mapbox::geometry::point(0.0, 0.0)); + const auto& featureObject = input[1].GetObject(); + if (featureObject.HasMember("properties")) { + assert(featureObject["properties"].IsObject()); + feature.properties = mapbox::geojson::convert(featureObject["properties"]); + } + + if (featureObject.HasMember("geometry")) { + assert(featureObject["geometry"].IsObject()); + feature.geometry = mapbox::geojson::convert>(featureObject["geometry"]); + } + + if (featureObject.HasMember("id")) { + assert(featureObject["id"].IsNumber() || featureObject["id"].IsString()); + feature.id = mapbox::geojson::convert(featureObject["id"]); + } + + data.inputs.emplace_back(std::move(zoom), + std::move(heatmapDensity), + std::move(canonical), + std::move(availableImages), + std::move(feature)); + } + return true; +} + +} // namespace + +std::tuple, bool, uint32_t> parseArguments(int argc, char** argv) { + args::ArgumentParser argumentParser("Mapbox GL Expression Test Runner"); + + args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", { 'h', "help" }); + args::Flag shuffleFlag(argumentParser, "shuffle", "Toggle shuffling the tests order", {'s', "shuffle"}); + args::ValueFlag seedValue(argumentParser, "seed", "Shuffle seed (default: random)", + { "seed" }); + args::PositionalList testNameValues(argumentParser, "URL", "Test name(s)"); + args::ValueFlag testFilterValue(argumentParser, "filter", "Test filter regex", {'f', "filter"}); + + try { + argumentParser.ParseCLI(argc, argv); + } catch (const args::Help&) { + std::ostringstream stream; + stream << argumentParser; + Log::Info(Event::General, stream.str()); + exit(0); + } catch (const args::ParseError& e) { + std::ostringstream stream; + stream << argumentParser; + Log::Info(Event::General, stream.str()); + Log::Error(Event::General, e.what()); + exit(1); + } catch (const args::ValidationError& e) { + std::ostringstream stream; + stream << argumentParser; + Log::Info(Event::General, stream.str()); + Log::Error(Event::General, e.what()); + exit(2); + } + + filesystem::path rootPath {std::string(TEST_RUNNER_ROOT_PATH).append("/mapbox-gl-js/test/integration/expression-tests")}; + if (!filesystem::exists(rootPath)) { + Log::Error(Event::General, "Test path '%s' does not exist.", rootPath.string().c_str()); + exit(3); + } + + std::vector paths; + for (const auto& testName : args::get(testNameValues)) { + paths.emplace_back(rootPath.string() + "/" + testName); + } + + if (paths.empty()) { + paths.emplace_back(rootPath); + } + + auto testFilter = testFilterValue ? args::get(testFilterValue) : std::string{}; + // Recursively traverse through the test paths and collect test directories containing "test.json". + std::vector testPaths; + testPaths.reserve(paths.size()); + for (const auto& path : paths) { + if (!filesystem::exists(path)) { + Log::Warning(Event::General, "Provided test folder '%s' does not exist.", path.string().c_str()); + continue; + } + + for (auto& testPath : filesystem::recursive_directory_iterator(path)) { + if (!testFilter.empty() && !std::regex_search(testPath.path().string(), std::regex(testFilter))) { + continue; + } + if (testPath.path().filename() == "test.json") { + testPaths.emplace_back(testPath.path()); + } + } + } + + return Arguments{ std::move(rootPath), + std::move(testPaths), + shuffleFlag ? args::get(shuffleFlag) : false, + seedValue ? args::get(seedValue) : 1u }; +} + +Ignores parseExpressionIgnores() { + Ignores ignores; + const auto mainIgnoresPath = filesystem::path(TEST_RUNNER_ROOT_PATH).append("metrics/ignores/platform-all.json"); + auto maybeIgnores = readJson(mainIgnoresPath); + if (!maybeIgnores.is()) { // NOLINT + return {}; + } + + for (const auto& property : maybeIgnores.get().GetObject()) { + std::string id{ toString(property.name) }; + // Keep only expression-test ignores + if (id.rfind("expression-tests", 0) != 0) { + continue; + } + std::string reason{ toString(property.value) }; + ignores.emplace_back(std::move(id), std::move(reason)); + } + + return ignores; +} + +optional parseTestData(const filesystem::path& path) { + TestData data; + auto maybeJson = readJson(path.string()); + if (!maybeJson.is()) { // NOLINT + Log::Error(Event::General, "Cannot parse test '%s'.", path.string().c_str()); + return nullopt; + } + + data.document = std::move(maybeJson.get()); + + // Check that mandatory test data members are present. + if (!data.document.HasMember("expression") || !data.document.HasMember("expected")) { + Log::Error(Event::General, "Test fixture '%s' does not contain required data.", path.string().c_str()); + return nullopt; + } + + // Parse propertySpec + if (data.document.HasMember("propertySpec")) { + assert(data.document["propertySpec"].IsObject()); + parsePropertySpec(data.document["propertySpec"], data); + } + + // Parse expected + parseExpected(data.document["expected"], data); + + // Parse inputs + if (data.document.HasMember("inputs") && !parseInputs(data.document["inputs"], data)) { + Log::Error(Event::General,"Can't convert inputs value for '%s'", path.string().c_str()); + return nullopt; + } + + return {std::move(data)}; +} + +std::string toJSON(const Value& value, unsigned indent, bool singleLine) { + rapidjson::StringBuffer buffer; + rapidjson::PrettyWriter writer(buffer); + if (singleLine) { + writer.SetFormatOptions(rapidjson::kFormatSingleLineArray); + } + writer.SetIndent(' ', indent); + writeJSON(writer, value); + return buffer.GetString(); +} + +JSDocument toDocument(const Value& value) { + JSDocument document; + document.Parse(toJSON(value)); + return document; +} + +Value toValue(const Compiled& compiled) { + std::unordered_map compiledObject; + compiledObject.emplace("result", compiled.success ? "success"s : "error"s); + if (compiled.success) { + compiledObject.emplace("isFeatureConstant", compiled.isFeatureConstant); + compiledObject.emplace("isZoomConstant", compiled.isZoomConstant); + compiledObject.emplace("type", compiled.serializedType); + } else { + compiledObject.emplace("errors", compiled.errors); + } + return {std::move(compiledObject)}; +} + +// Serializes native expression types to JS format. +// Color and Formatted are exceptions. +// TODO: harmonize serialized format to remove this conversion. +optional toValue(const expression::Value& exprValue) { + return exprValue.match( + [](const Color& c) -> optional { + std::vector color { double(c.r), double(c.g), double(c.b), double(c.a) }; + return {Value{std::move(color)}}; + }, + [](const expression::Formatted& formatted) -> optional { return {formatted.toObject()}; }, + [](const std::vector& values) -> optional { + std::vector mbglValues; + for (const auto& value : values) { + if (auto converted = expression::fromExpressionValue(value)) { + mbglValues.emplace_back(std::move(*converted)); + } + } + return {Value{std::move(mbglValues)}}; + }, + [](const std::unordered_map& valueMap) -> optional { + std::unordered_map mbglValueMap; + for (const auto& pair : valueMap) { + if (auto converted = expression::fromExpressionValue(pair.second)) { + mbglValueMap.emplace(pair.first, std::move(*converted)); + } + } + return {Value{std::move(mbglValueMap)}}; + }, + [](const auto& v) { return expression::fromExpressionValue(v); }); +} + +std::unique_ptr parseExpression(const JSValue& value, + optional& spec, + TestResult& result) { + optional expected = spec ? toExpressionType(*spec) : nullopt; + expression::ParsingContext ctx = expected ? expression::ParsingContext(*expected) : + expression::ParsingContext(); + Convertible convertible(&value); + expression::ParseResult parsed; + if (value.IsObject() && !value.IsArray() && expected){ + Error error; + parsed = convertFunctionToExpression(*expected, convertible, error, false /*convert tokens*/); + if (!parsed) { + // TODO: should the error message be checked for function conversion? + } + } else { + parsed = ctx.parseLayerPropertyExpression(convertible); + if (!parsed) { + for (const auto& parsingError : ctx.getErrors()) { + std::unordered_map errorMap; + errorMap.emplace("key"s, Value{parsingError.key}); + errorMap.emplace("error"s, Value{parsingError.message}); + result.compiled.errors.emplace_back(Value{std::move(errorMap)}); + } + } + } + + result.expression = toValue(value); + result.compiled.success = bool(parsed); + if (parsed) { + result.compiled.isFeatureConstant = expression::isFeatureConstant(**parsed); + result.compiled.isZoomConstant = expression::isZoomConstant(**parsed); + result.compiled.serializedType = style::expression::type::toString((*parsed)->getType()); + result.serialized = (*parsed)->serialize(); + return std::move(*parsed); + } + + return nullptr; +} + +std::unique_ptr parseExpression(const optional& value, + optional& spec, + TestResult& result) { + assert(value); + auto document = toDocument(*value); + assert(!document.HasParseError()); + return parseExpression(document, spec, result); +} diff --git a/mapbox-gl-native/expression-test/expression_test_parser.hpp b/mapbox-gl-native/expression-test/expression_test_parser.hpp new file mode 100644 index 000000000..b73dbf053 --- /dev/null +++ b/mapbox-gl-native/expression-test/expression_test_parser.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include "filesystem.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace mbgl; + +struct Input { + Input(optional zoom_, + optional heatmapDensity_, + optional canonical_, + std::set availableImages_, + Feature feature_) + : zoom(std::move(zoom_)), + heatmapDensity(std::move(heatmapDensity_)), + canonical(std::move(canonical_)), + availableImages(std::move(availableImages_)), + feature(std::move(feature_)) {} + optional zoom; + optional heatmapDensity; + optional canonical; + std::set availableImages; + Feature feature; +}; + +struct Compiled { + bool operator==(const Compiled& other) const { + bool typeEqual = success == other.success && + isFeatureConstant == other.isFeatureConstant && + isZoomConstant == other.isZoomConstant && + serializedType == other.serializedType && + errors == other.errors; + return typeEqual; + } + + bool success = false; + bool isFeatureConstant = false; + bool isZoomConstant = false; + std::string serializedType; + std::vector errors; +}; + +struct TestResult { + Compiled compiled; + optional expression; + optional outputs; + optional serialized; +}; + +struct PropertySpec { + std::string type; + std::string value; + std::size_t length = 0; + bool isDataDriven = false; + optional expression; +}; + +class TestData { +public: + std::vector inputs; + TestResult expected; + TestResult result; + TestResult recompiled; + optional spec; + JSDocument document; +}; + +struct Ignore { + Ignore(std::string id_, std::string reason_) + : id(std::move(id_)), + reason(std::move(reason_)) {} + + std::string id; + std::string reason; +}; + +using Arguments = std::tuple, bool, uint32_t>; +Arguments parseArguments(int argc, char** argv); + +using Ignores = std::vector; +Ignores parseExpressionIgnores(); +optional parseTestData(const filesystem::path&); + +std::string toJSON(const Value& value, unsigned indent = 0, bool singleLine = false); +JSDocument toDocument(const Value&); +Value toValue(const Compiled&); +optional toValue(const style::expression::Value&); + +std::unique_ptr parseExpression(const JSValue&, + optional&, + TestResult&); +std::unique_ptr parseExpression(const optional&, + optional&, + TestResult&); + diff --git a/mapbox-gl-native/expression-test/expression_test_runner.cpp b/mapbox-gl-native/expression-test/expression_test_runner.cpp new file mode 100644 index 000000000..c0d451163 --- /dev/null +++ b/mapbox-gl-native/expression-test/expression_test_runner.cpp @@ -0,0 +1,204 @@ +#include "expression_test_runner.hpp" +#include "expression_test_logger.hpp" +#include "expression_test_parser.hpp" +#include "filesystem.hpp" +#include "test_runner_common.hpp" + +#include + +#include +#include +#include + +#include +#include + +using namespace std::literals; + +namespace { + +std::string simpleDiff(const Value& result, const Value& expected) { + std::vector resultTokens {tokenize(toJSON(result, 2, true))}; + std::vector expectedTokens {tokenize(toJSON(expected, 2, true))}; + std::size_t maxLength = std::max(resultTokens.size(), expectedTokens.size()); + std::ostringstream diff; + const auto flush = [] (const std::vector& vec, std::size_t pos, std::ostringstream& out, std::string separator) { + for (std::size_t j = pos; j < vec.size(); ++j) { + out << separator << vec[j] << std::endl; + } + }; + + for (std::size_t i = 0; i < maxLength; ++i) { + if (resultTokens.size() <= i) { + flush(expectedTokens, i, diff, "-"s); + break; + } + + if (expectedTokens.size() <= i) { + flush(resultTokens, i, diff, "+"s); + break; + } + + if (resultTokens[i] != expectedTokens[i]) { + diff << "-"s << expectedTokens[i] << std::endl; + diff << "+"s << resultTokens[i] << std::endl; + } else { + diff << resultTokens[i] << std::endl; + } + } + return diff.str(); +} + +void rewriteRoundtrippedType(const std::string& expected, std::string& actual) { + if (!expected.rfind("array", 0) && !actual.rfind("array", 0)) { + actual = expected; + } +} + +void updateTest(TestData& data) { + assert(data.document["expected"].IsObject()); + auto& expected = data.document["expected"]; + auto compiled = toDocument(toValue(data.result.compiled)); + assert(!compiled.HasParseError()); + expected["compiled"].Swap(compiled); + + if (data.result.outputs) { + auto outputs = toDocument(stripPrecision(data.result.outputs.value_or(Value{}))); + assert(!outputs.HasParseError()); + expected["outputs"].Swap(outputs); + } else { + expected.RemoveMember("outputs"); + } + + if (data.result.serialized) { + auto serialized = toDocument(*data.result.serialized); + assert(!serialized.HasParseError()); + expected["serialized"].Swap(serialized); + } else { + expected.RemoveMember("serialized"); + } +} + +void writeTestData(const JSDocument& document, const std::string& rootPath, const std::string& id) { + rapidjson::StringBuffer buffer; + rapidjson::PrettyWriter writer(buffer); + writer.SetFormatOptions(rapidjson::kFormatSingleLineArray); + writer.SetIndent(' ', 2); + document.Accept(writer); + buffer.Put('\n'); + filesystem::path path = filesystem::path(rootPath) / id / "test.json"s; + try { + util::write_file(path.string(), {buffer.GetString(), buffer.GetSize()}); + } catch (std::exception&) { + printf(ANSI_COLOR_RED "* ERROR can't update '%s' test" ANSI_COLOR_RESET "\n", id.c_str()); + } +} + +} // namespace + +TestRunOutput runExpressionTest(TestData& data, const std::string& rootPath, const std::string& id) { + TestRunOutput output(id); + const auto evaluateExpression = [&data](std::unique_ptr& expression, + TestResult& result) { + assert(expression); + std::vector outputs; + if (!data.inputs.empty()) { + for (const auto& input : data.inputs) { + mbgl::style::expression::EvaluationResult evaluationResult; + if (input.canonical) { + evaluationResult = expression->evaluate( + input.zoom, input.feature, input.heatmapDensity, input.availableImages, *input.canonical); + } else { + evaluationResult = + expression->evaluate(input.zoom, input.feature, input.heatmapDensity, input.availableImages); + } + if (!evaluationResult) { + std::unordered_map error{{"error", Value{evaluationResult.error().message}}}; + outputs.emplace_back(Value{std::move(error)}); + } else { + auto value = toValue(*evaluationResult); + assert(value); + outputs.emplace_back(Value{*value}); + } + } + } + result.outputs = {Value{std::move(outputs)}}; + }; + + // Parse expression + auto parsedExpression = parseExpression(data.document["expression"], data.spec, data.result); + output.expression = toJSON(data.result.expression.value_or(Value{}), 2, true); + + // Evaluate expression + if (parsedExpression) { + evaluateExpression(parsedExpression, data.result); + output.serialized = toJSON(data.result.serialized.value_or(Value{}), 2, true); + + // round trip + auto recompiledExpression = parseExpression(data.result.serialized, data.spec, data.recompiled); + if (recompiledExpression) { + evaluateExpression(recompiledExpression, data.recompiled); + rewriteRoundtrippedType(data.expected.compiled.serializedType, + data.recompiled.compiled.serializedType); + } + } + + if (getenv("UPDATE")) { + output.passed = true; + updateTest(data); + writeTestData(data.document, rootPath, id); + return output; + } + + bool compileOk = data.result.compiled == data.expected.compiled; + bool evalOk = compileOk && deepEqual(data.result.outputs, data.expected.outputs); + + bool recompileOk = true; + bool roundTripOk = true; + bool serializationOk = true; + + if (data.expected.compiled.success) { + serializationOk = compileOk && deepEqual(data.result.serialized, data.expected.serialized); + recompileOk = compileOk && data.recompiled.compiled == data.expected.compiled; + roundTripOk = recompileOk && deepEqual(data.recompiled.outputs, data.expected.outputs); + } + + output.passed = compileOk && evalOk && recompileOk && roundTripOk && serializationOk; + + if (!compileOk) { + auto resultValue = toValue(data.result.compiled); + auto expectedValue = toValue(data.expected.compiled); + output.text += "Compiled expression difference:\n"s + + simpleDiff(resultValue, expectedValue) + + "\n"s; + } + + if (compileOk && !serializationOk) { + auto diff = simpleDiff(data.expected.serialized.value_or(Value{}), + data.result.serialized.value_or(Value{})); + output.text += "Serialized expression difference:\n"s + diff + "\n"s; + } + + if (compileOk && !recompileOk) { + auto recompiledValue = toValue(data.recompiled.compiled); + auto expectedValue = toValue(data.expected.compiled); + output.text += "Recompiled expression difference:\n"s + + simpleDiff(recompiledValue, expectedValue) + + "\n"s; + } + + if (compileOk && !evalOk) { + auto diff = simpleDiff(data.expected.outputs.value_or(Value{}), + data.result.outputs.value_or(Value{})); + output.text += "Expression outputs difference:\n"s + diff + "\n"s; + } + + if (recompileOk && !roundTripOk) { + auto diff = simpleDiff(data.expected.outputs.value_or(Value{}), + data.recompiled.outputs.value_or(Value{})); + output.text += "Roundtripped through serialize expression outputs difference:\n"s + + diff + "\n"s; + } + + return output; +} diff --git a/mapbox-gl-native/expression-test/expression_test_runner.hpp b/mapbox-gl-native/expression-test/expression_test_runner.hpp new file mode 100644 index 000000000..596d5c11b --- /dev/null +++ b/mapbox-gl-native/expression-test/expression_test_runner.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +class TestData; + +struct TestRunOutput { + TestRunOutput(std::string id_) : id(std::move(id_)) {} + std::string id; + bool passed = false; + std::string text; + std::string expression; + std::string serialized; +}; + +class TestStats { +public: + std::size_t testCount() const { + return passed.size() + failed.size() + errored.size() + + ignorePassed.size() + ignoreFailed.size(); + } + + std::vector passed; + std::vector failed; + std::vector errored; + std::vector ignorePassed; + std::vector ignoreFailed; + std::vector ids; +}; + +TestRunOutput runExpressionTest(TestData&, const std::string& rootPath, const std::string& id); diff --git a/mapbox-gl-native/expression-test/filesystem.hpp b/mapbox-gl-native/expression-test/filesystem.hpp new file mode 100644 index 000000000..cee7e9d91 --- /dev/null +++ b/mapbox-gl-native/expression-test/filesystem.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace mbgl { + +namespace filesystem = ghc::filesystem; + +} // namespace mbgl diff --git a/mapbox-gl-native/expression-test/main.cpp b/mapbox-gl-native/expression-test/main.cpp new file mode 100644 index 000000000..db468dffe --- /dev/null +++ b/mapbox-gl-native/expression-test/main.cpp @@ -0,0 +1,75 @@ +#include "expression_test_parser.hpp" +#include "expression_test_runner.hpp" +#include "expression_test_logger.hpp" + +#include + +int main(int argc, char** argv) { + // Parse args + std::vector testPaths; + mbgl::filesystem::path rootPath; + bool shuffle; + uint32_t seed; + std::tie(rootPath, testPaths, shuffle, seed) = parseArguments(argc, argv); + + // Parse ignores + const auto ignores = parseExpressionIgnores(); + + if (shuffle) { + printf(ANSI_COLOR_YELLOW "Shuffle seed: %d" ANSI_COLOR_RESET "\n", seed); + std::seed_seq sequence { seed }; + std::mt19937 shuffler(sequence); + std::shuffle(testPaths.begin(), testPaths.end(), shuffler); + } + + // Run tests, collect stats and output of each run. + TestStats stats; + for (const auto& path : testPaths) { + const auto& expectation = path.parent_path().string(); + std::string id = expectation.substr(rootPath.string().length() + 1, expectation.length() - rootPath.string().length()); + stats.ids.emplace_back(id); + + bool shouldIgnore = false; + std::string ignoreReason; + const std::string ignoreName = "expression-tests/" + id; + const auto it = std::find_if(ignores.cbegin(), ignores.cend(), [&ignoreName] (const auto& ignore) { return ignore.id == ignoreName; }); + if (it != ignores.end()) { + shouldIgnore = true; + ignoreReason = (*it).reason; + } + + optional testRun; + if (auto testData = parseTestData(path)) { + testRun = runExpressionTest(*testData, rootPath.string(), id); + } + + if (!testRun) { + stats.errored.emplace_back(id); + printf(ANSI_COLOR_RED "* ERROR can't parse '%s' test" ANSI_COLOR_RESET "\n", id.c_str()); + continue; + } + + if (shouldIgnore) { + if (testRun->passed) { + stats.ignorePassed.emplace_back(std::move(*testRun)); + printf(ANSI_COLOR_YELLOW "* PASSED ignored test %s (%s)" ANSI_COLOR_RESET "\n", id.c_str(), ignoreReason.c_str()); + } else { + stats.ignoreFailed.emplace_back(std::move(*testRun)); + printf(ANSI_COLOR_LIGHT_GRAY "* FAILED ignored test %s (%s)" ANSI_COLOR_RESET "\n", id.c_str(), ignoreReason.c_str()); + } + } else { + if (testRun->passed) { + stats.passed.emplace_back(std::move(*testRun)); + printf(ANSI_COLOR_GREEN "* PASSED %s" ANSI_COLOR_RESET "\n", id.c_str()); + } else { + printf(ANSI_COLOR_RED "* FAILED %s\n%s" ANSI_COLOR_RESET, id.c_str(), testRun->text.c_str()); + stats.failed.emplace_back(std::move(*testRun)); + } + } + } + + printStats(stats); + writeHTMLResults(stats, rootPath.string(), shuffle, seed); + + return stats.errored.size() + stats.failed.size() == 0 ? 0 : 1; +} diff --git a/mapbox-gl-native/expression-test/test_runner_common.cpp b/mapbox-gl-native/expression-test/test_runner_common.cpp new file mode 100644 index 000000000..745ef9fce --- /dev/null +++ b/mapbox-gl-native/expression-test/test_runner_common.cpp @@ -0,0 +1,131 @@ +#include "test_runner_common.hpp" + +#include +#include + +using namespace mbgl; + +// Strip precision for numbers, so that we can compare evaluated results with fixtures. +// Copied from JS expression harness. +Value stripPrecision(const Value& value) { + const double decimalSigFigs = 6; + if (auto num = numericValue(value)) { + if (*num == 0) { + return *num; + } + + const double multiplier = std::pow(10, std::max(0.0, decimalSigFigs - std::ceil(std::log10(std::fabs(*num))))); + + // We strip precision twice in a row here to avoid cases where + // stripping an already stripped number will modify its value + // due to bad floating point precision luck + // eg `Math.floor(8.16598 * 100000) / 100000` -> 8.16597 + const double firstStrip = std::floor(*num * multiplier) / multiplier; + return std::floor(firstStrip * multiplier) / multiplier; + } + + if (value.is>()) { + std::vector stripped; + const auto& vec = value.get>(); + stripped.reserve(vec.size()); + for (const auto& val : vec) { + stripped.emplace_back(stripPrecision(val)); + } + return stripped; + } else if (value.is>()) { + std::unordered_map stripped; + const auto& map = value.get>(); + for (const auto& pair : map) { + stripped.emplace(pair.first, stripPrecision(pair.second)); + } + return stripped; + } + + return value; +} + +std::vector tokenize(std::string str) { + std::vector tokens; + std::regex re("\n"); + std::copy(std::regex_token_iterator(str.begin(), str.end(), re, -1), + std::regex_token_iterator(), + std::back_inserter(tokens)); + return tokens; +} + +bool deepEqual(const Value& a, const Value& b) { + const auto& anum = numericValue(a); + const auto& bnum = numericValue(b); + if (anum && bnum) { + return stripPrecision(*anum) == stripPrecision(*bnum); + } + + if (a.which() != b.which()) { + return false; + } + + if (a.getArray() && b.getArray()) { + const auto& avec = *a.getArray(); + const auto& bvec = *b.getArray(); + if (avec.size() != bvec.size()) { + return false; + } + for (std::size_t i = 0; i < avec.size(); ++i) { + if (!deepEqual(avec[i], bvec[i])) { + return false; + } + } + return true; + } + + if (a.getObject() && b.getObject()) { + const auto& amap = *a.getObject(); + const auto& bmap = *b.getObject(); + if (amap.size() != bmap.size()) { + return false; + } + for (const auto& pair : amap) { + auto it = bmap.find(pair.first); + if (it == bmap.end()) { + return false; + } + if (!deepEqual(pair.second, it->second)) { + return false; + } + } + return true; + } + + if (a == b) { + return true; + } + + if (a.getString() && b.getString()) { + const auto& strA = *a.getString(); + const auto& strB = *b.getString(); + if (strA == strB) { + return true; + } + + try { + double numA = std::stod(strA); + double numB = std::stod(strB); + return stripPrecision(numA) == stripPrecision(numB); + } catch (...) { + } + } + + return false; +} + +bool deepEqual(const optional& a, const optional& b) { + if ((a && !b) || (!a && b)) { + return false; + } + + if (a && b) { + return deepEqual(*a, *b); + } + + return true; +} diff --git a/mapbox-gl-native/expression-test/test_runner_common.hpp b/mapbox-gl-native/expression-test/test_runner_common.hpp new file mode 100644 index 000000000..b30d1a145 --- /dev/null +++ b/mapbox-gl-native/expression-test/test_runner_common.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include +#include + +using namespace mbgl; + +Value stripPrecision(const Value& value); +std::vector tokenize(std::string str); +bool deepEqual(const Value& a, const Value& b); +bool deepEqual(const optional& a, const optional& b); diff --git a/mapbox-gl-native/include/mbgl/actor/actor.hpp b/mapbox-gl-native/include/mbgl/actor/actor.hpp new file mode 100644 index 000000000..97964ae53 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/actor/actor.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mbgl { + +/* + An `Actor` is an owning reference to an asynchronous object of type `O`: an "actor". + Communication with an actor happens via message passing: you send a message to the object + (using `invoke`), passing a pointer to the member function to call and arguments which + are then forwarded to the actor. + + The actor receives messages sent to it asynchronously, in a manner defined its `Scheduler`. + To store incoming messages before their receipt, each actor has a `Mailbox`, which acts as + a FIFO queue. Messages sent from actor S to actor R are guaranteed to be processed in the + order sent. However, relative order of messages sent by two *different* actors S1 and S2 + to R is *not* guaranteed (and can't be: S1 and S2 may be acting asynchronously with respect + to each other). + + An `Actor` can be converted to an `ActorRef`, a non-owning value object representing + a (weak) reference to the actor. Messages can be sent via the `Ref` as well. + + It's safe -- and encouraged -- to pass `Ref`s between actors via messages. This is how two-way + communication and other forms of collaboration between multiple actors is accomplished. + + It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend + the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is + a no-op. (In the future, a dead-letters queue or log may be implemented.) + + Construction and destruction of an Actor is synchronous: the corresponding `O` + object is constructed synchronously by the `Actor` constructor, and destructed synchronously + by the `~Actor` destructor, after ensuring that the `O` is not currently receiving an + asynchronous message. The constructor of `O` is passed an `ActorRef` referring to itself + (which it can use to self-send messages), followed by the forwarded arguments passed to + `Actor`. Asynchronous object construction can be accomplished by directly using the + lower-level types, `AspiringActor` and `EstablishedActor`. + + Please don't send messages that contain shared pointers or references. That subverts the + purpose of the actor model: prohibiting direct concurrent access to shared state. +*/ +template +class Actor { +public: + template + Actor(Scheduler& scheduler, Args&&... args) + : target(scheduler, parent, std::forward(args)...) {} + + template + Actor(std::shared_ptr scheduler, Args&&... args) + : retainer(std::move(scheduler)), target(*retainer, parent, std::forward(args)...) {} + + Actor(const Actor&) = delete; + + ActorRef> self() { + return parent.self(); + } + +private: + std::shared_ptr retainer; + AspiringActor parent; + EstablishedActor target; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/actor/actor_ref.hpp b/mapbox-gl-native/include/mbgl/actor/actor_ref.hpp new file mode 100644 index 000000000..d700a86d1 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/actor/actor_ref.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { + +/* + An `ActorRef` is a *non*-owning, weak reference to an actor of type `O`. You can send it + messages just like an `Actor`. It's a value object: safe to copy and pass between actors + via messages. + + An `ActorRef` does not extend the lifetime of the corresponding `Actor`. That's determined + entirely by whichever object owns the `Actor` -- the actor's "supervisor". + + It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend + the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is + a no-op. (In the future, a dead-letters queue or log may be implemented.) +*/ + +template +class ActorRef { +public: + ActorRef(Object& object_, std::weak_ptr weakMailbox_) + : object(&object_), + weakMailbox(std::move(weakMailbox_)) { + } + + template + void invoke(Fn fn, Args&&... args) const { + if (auto mailbox = weakMailbox.lock()) { + mailbox->push(actor::makeMessage(*object, fn, std::forward(args)...)); + } + } + + template + auto ask(Fn fn, Args&&... args) const { + // Result type is deduced from the function's return type + using ResultType = typename std::result_of::type; + + std::promise promise; + auto future = promise.get_future(); + + if (auto mailbox = weakMailbox.lock()) { + mailbox->push( + actor::makeMessage( + std::move(promise), *object, fn, std::forward(args)... + ) + ); + } else { + promise.set_exception(std::make_exception_ptr(std::runtime_error("Actor has gone away"))); + } + + return future; + } + +private: + Object* object; + std::weak_ptr weakMailbox; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/actor/aspiring_actor.hpp b/mapbox-gl-native/include/mbgl/actor/aspiring_actor.hpp new file mode 100644 index 000000000..6c410cdfc --- /dev/null +++ b/mapbox-gl-native/include/mbgl/actor/aspiring_actor.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace mbgl { + +template +class EstablishedActor; + +template +class Actor; + +/* + An `AspiringActor` is one half of the pair of types that comprise an actor (see `Actor`), + the other half being `EstablishedActor`. It is responsible for: + - ownership of the actor's `Mailbox` + - allocating the memory for (but *not* constructing) the target object `O` + + Using these two pieces--the mailbox and a stable address for `O`--an `AspiringActor` can + accept messages for the target object, or provide `ActorRef`s that do so, before the object + has actually been constructed by the corresponding `EstablishedActor`. (Such messages are + queued in the mailbox until after the object is constructed.) + + This allows for an `AspiringActor` to be created and safely used by a thread other than the + one on which the target object will (eventually) live. +*/ +template +class AspiringActor { +public: + AspiringActor() : mailbox(std::make_shared()) { + // mailbox starts closed because the `Object` hasn't yet been constructed + assert(!mailbox->isOpen()); + } + + AspiringActor(const AspiringActor&) = delete; + + ActorRef> self() { + return ActorRef>(object(), mailbox); + } + +private: + std::shared_ptr mailbox; + std::aligned_storage_t objectStorage; + + Object& object() { + return *reinterpret_cast(&objectStorage); + } + + friend class EstablishedActor; + friend class Actor; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/actor/established_actor.hpp b/mapbox-gl-native/include/mbgl/actor/established_actor.hpp new file mode 100644 index 000000000..da0d8ac70 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/actor/established_actor.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mbgl { + +/* + An `EstablishedActor` is one half of the pair of types that comprise an actor (see `Actor`), + the other half being `AspiringActor`. It is responsible for managing the lifetime of the + target object `O` and the open/closed state of the parent's `mailbox`. + + The `O` object's lifetime is contained by that of its owning `EstablishedActor`: the + `EstablishedActor` constructor executes the `O` constructor via "placement new", constructing + it at the address provided by the parent `AspiringActor`, and the `~EstablishedActor` destructor + similarly executes the `~O` destructor (after closing the mailbox). `EstablishedActor` should + therefore live entirely on the thread intended to own `O`. +*/ + +template +class EstablishedActor { +public: + // Construct the Object from a parameter pack `args` (i.e. `Object(args...)`) + template ::value || + std::is_constructible, Args...>::value + >::type * = nullptr> + EstablishedActor(Scheduler& scheduler, AspiringActor& parent_, Args&& ... args) + : parent(parent_) { + emplaceObject(std::forward(args)...); + parent.mailbox->open(scheduler); + } + + // Construct the `Object` from a tuple containing the constructor arguments (i.e. + // `Object(std::get<0>(args), std::get<1>(args), ...)`) + template >::value> + EstablishedActor(Scheduler& scheduler, AspiringActor& parent_, ArgsTuple&& args) + : parent(parent_) { + emplaceObject(std::forward(args), std::make_index_sequence{}); + parent.mailbox->open(scheduler); + } + + EstablishedActor(const EstablishedActor&) = delete; + + ~EstablishedActor() { + parent.mailbox->close(); + parent.object().~Object(); + } + +private: + // Enabled for Objects with a constructor taking ActorRef as the first parameter + template , Args...>::value>::type * = nullptr> + void emplaceObject(Args&&... args_) { + new (&parent.objectStorage) Object(parent.self(), std::forward(args_)...); + } + + // Enabled for plain Objects + template ::value>::type * = nullptr> + void emplaceObject(Args&&... args_) { + new (&parent.objectStorage) Object(std::forward(args_)...); + } + + // Used to expand a tuple holding the constructor arguments + template + void emplaceObject(ArgsTuple&& args, std::index_sequence) { + emplaceObject(std::move(std::get(std::forward(args)))...); + (void) args; // mark args as used: if it's empty tuple, it's not actually used above. + } + + AspiringActor& parent; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/actor/mailbox.hpp b/mapbox-gl-native/include/mbgl/actor/mailbox.hpp new file mode 100644 index 000000000..39b33275a --- /dev/null +++ b/mapbox-gl-native/include/mbgl/actor/mailbox.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include + +namespace mbgl { + +class Scheduler; +class Message; + +class Mailbox : public std::enable_shared_from_this { +public: + + // Create a "holding" mailbox, messages to which will remain queued, + // unconsumed, until the mailbox is associated with a Scheduler using + // start(). This allows a Mailbox object to be created on one thread and + // later transferred to a different target thread that may not yet exist. + Mailbox(); + + Mailbox(Scheduler&); + + // Attach the given scheduler to this mailbox and begin processing messages + // sent to it. The mailbox must be a "holding" mailbox, as created by the + // default constructor Mailbox(). + void open(Scheduler& scheduler_); + void close(); + + bool isOpen() const; + + void push(std::unique_ptr); + void receive(); + + static void maybeReceive(const std::weak_ptr&); + static std::function makeClosure(std::weak_ptr); + +private: + mapbox::base::WeakPtr weakScheduler; + + std::recursive_mutex receivingMutex; + std::mutex pushingMutex; + + bool closed { false }; + + std::mutex queueMutex; + std::queue> queue; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/actor/message.hpp b/mapbox-gl-native/include/mbgl/actor/message.hpp new file mode 100644 index 000000000..0a2099335 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/actor/message.hpp @@ -0,0 +1,108 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { + +// A movable type-erasing function wrapper. This allows to store arbitrary invokable +// things (like std::function<>, or the result of a movable-only std::bind()) in the queue. +// Source: http://stackoverflow.com/a/29642072/331379 +class Message { +public: + virtual ~Message() = default; + virtual void operator()() = 0; +}; + +template +class MessageImpl : public Message { +public: + MessageImpl(Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) + : object(object_), + memberFn(memberFn_), + argsTuple(std::move(argsTuple_)) { + } + + void operator()() override { + invoke(std::make_index_sequence::value>()); + } + + template + void invoke(std::index_sequence) { + (object.*memberFn)(std::move(std::get(argsTuple))...); + } + + Object& object; + MemberFn memberFn; + ArgsTuple argsTuple; +}; + +template +class AskMessageImpl : public Message { +public: + AskMessageImpl(std::promise promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) + : object(object_), + memberFn(memberFn_), + argsTuple(std::move(argsTuple_)), + promise(std::move(promise_)) { + } + + void operator()() override { + promise.set_value(ask(std::make_index_sequence::value>())); + } + + template + ResultType ask(std::index_sequence) { + return (object.*memberFn)(std::move(std::get(argsTuple))...); + } + + Object& object; + MemberFn memberFn; + ArgsTuple argsTuple; + std::promise promise; +}; + +template +class AskMessageImpl : public Message { +public: + AskMessageImpl(std::promise promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) + : object(object_), + memberFn(memberFn_), + argsTuple(std::move(argsTuple_)), + promise(std::move(promise_)) { + } + + void operator()() override { + ask(std::make_index_sequence::value>()); + promise.set_value(); + } + + template + void ask(std::index_sequence) { + (object.*memberFn)(std::move(std::get(argsTuple))...); + } + + Object& object; + MemberFn memberFn; + ArgsTuple argsTuple; + std::promise promise; +}; + +namespace actor { + +template +std::unique_ptr makeMessage(Object& object, MemberFn memberFn, Args&&... args) { + auto tuple = std::make_tuple(std::forward(args)...); + return std::make_unique>(object, memberFn, std::move(tuple)); +} + +template +std::unique_ptr makeMessage(std::promise&& promise, Object& object, MemberFn memberFn, Args&&... args) { + auto tuple = std::make_tuple(std::forward(args)...); + return std::make_unique>(std::move(promise), object, memberFn, std::move(tuple)); +} + +} // namespace actor +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/actor/scheduler.hpp b/mapbox-gl-native/include/mbgl/actor/scheduler.hpp new file mode 100644 index 000000000..dbc1a94f4 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/actor/scheduler.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include + +#include + +#include +#include + +namespace mbgl { + +class Mailbox; + +/* + A `Scheduler` is responsible for coordinating the processing of messages by + one or more actors via their mailboxes. It's an abstract interface. Currently, + the following concrete implementations exist: + + * `ThreadPool` can coordinate an unlimited number of actors over any number of + threads via a pool, preserving the following behaviors: + + - Messages from each individual mailbox are processed in order + - Only a single message from a mailbox is processed at a time; there is no + concurrency within a mailbox + + Subject to these constraints, processing can happen on whatever thread in the + pool is available. + + * `Scheduler::GetCurrent()` is typically used to create a mailbox and `ActorRef` + for an object that lives on the main thread and is not itself wrapped an + `Actor`. The underlying implementation of this Scheduler should usually be + a `RunLoop` + auto mailbox = std::make_shared(*Scheduler::Get()); + Actor worker(threadPool, ActorRef(*this, mailbox)); +*/ +class Scheduler { +public: + virtual ~Scheduler() = default; + + // Enqueues a function for execution. + virtual void schedule(std::function) = 0; + // Makes a weak pointer to this Scheduler. + virtual mapbox::base::WeakPtr makeWeakPtr() = 0; + + // Returns a closure wrapping the given one. + // + // When the returned closure is invoked for the first time, it schedules + // the given closure to this scheduler, the consequent calls of the + // returned closure are ignored. + // + // If this scheduler is already deleted by the time the returnded closure is + // first invoked, the call is ignored. + std::function bindOnce(std::function); + + // Enqueues the given |task| for execution into this scheduler's task queue and + // then enqueues the |reply| with the captured task result to the current + // task queue. + // + // The |TaskFn| return type must be compatible with the |ReplyFn| argument type. + // Note: the task result is copied and passed by value. + template + void scheduleAndReplyValue(const TaskFn& task, const ReplyFn& reply) { + assert(GetCurrent()); + scheduleAndReplyValue(task, reply, GetCurrent()->makeWeakPtr()); + } + + // Set/Get the current Scheduler for this thread + static Scheduler* GetCurrent(); + static void SetCurrent(Scheduler*); + + // Get the scheduler for asynchronous tasks. This method + // will lazily initialize a shared worker pool when ran + // from the first time. + // The scheduled tasks might run in parallel on different + // threads. + // TODO : Rename to GetPool() + static PassRefPtr GetBackground(); + + // Get the *sequenced* scheduler for asynchronous tasks. + // Unlike the method above, the returned scheduler + // (once stored) represents a single thread, thus each + // newly scheduled task is guarantied to run after the + // previously scheduled one. + // + // Sequenced scheduler can be used for running tasks + // on the same thread-unsafe object. + static PassRefPtr GetSequenced(); + +protected: + template + void scheduleAndReplyValue(const TaskFn& task, + const ReplyFn& reply, + mapbox::base::WeakPtr replyScheduler) { + auto scheduled = [replyScheduler = std::move(replyScheduler), task, reply] { + auto lock = replyScheduler.lock(); + if (!replyScheduler) return; + auto scheduledReply = [reply, result = task()] { reply(result); }; + replyScheduler->schedule(std::move(scheduledReply)); + }; + + schedule(std::move(scheduled)); + } +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/annotation/annotation.hpp b/mapbox-gl-native/include/mbgl/annotation/annotation.hpp new file mode 100644 index 000000000..0d67e90d7 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/annotation/annotation.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +using AnnotationID = uint64_t; +using AnnotationIDs = std::vector; + +class SymbolAnnotation { +public: + SymbolAnnotation(Point geometry_, std::string icon_ = {}) : geometry(geometry_), icon(std::move(icon_)) {} + + Point geometry; + std::string icon; +}; + +using ShapeAnnotationGeometry = variant< + LineString, + Polygon, + MultiLineString, + MultiPolygon>; + +class LineAnnotation { +public: + LineAnnotation(ShapeAnnotationGeometry geometry_, + style::PropertyValue opacity_ = 1.0f, + style::PropertyValue width_ = 1.0f, + style::PropertyValue color_ = Color::black()) + : geometry(std::move(geometry_)), + opacity(std::move(opacity_)), + width(std::move(width_)), + color(std::move(color_)) {} + + ShapeAnnotationGeometry geometry; + style::PropertyValue opacity; + style::PropertyValue width; + style::PropertyValue color; +}; + +class FillAnnotation { +public: + FillAnnotation(ShapeAnnotationGeometry geometry_, + style::PropertyValue opacity_ = 1.0f, + style::PropertyValue color_ = Color::black(), + style::PropertyValue outlineColor_ = {}) + : geometry(std::move(geometry_)), + opacity(std::move(opacity_)), + color(std::move(color_)), + outlineColor(std::move(outlineColor_)) {} + + ShapeAnnotationGeometry geometry; + style::PropertyValue opacity; + style::PropertyValue color; + style::PropertyValue outlineColor; +}; + +using Annotation = variant< + SymbolAnnotation, + LineAnnotation, + FillAnnotation>; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/gfx/backend.hpp b/mapbox-gl-native/include/mbgl/gfx/backend.hpp new file mode 100644 index 000000000..eed63dfcb --- /dev/null +++ b/mapbox-gl-native/include/mbgl/gfx/backend.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace gfx { + +class Backend { +public: + enum class Type : uint8_t { + OpenGL, + }; + + static constexpr Type DefaultType = Type::OpenGL; + + static void SetType(const Type value) { + if (Value(value) != value) { + abort(); // SetType must be called prior to any GetType calls. + } + } + + static Type GetType() { + return Value(DefaultType); + } + + template + static std::unique_ptr Create(Args... args) { + return Create(std::forward(args)...); + } + +private: + template + static std::unique_ptr Create(Args...); + + static Type Value(Type value) { + static const Type type = value; + return type; + } +}; + +} // namespace gfx +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/gfx/backend_scope.hpp b/mapbox-gl-native/include/mbgl/gfx/backend_scope.hpp new file mode 100644 index 000000000..ae42123e9 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/gfx/backend_scope.hpp @@ -0,0 +1,37 @@ +#pragma once + +namespace mbgl { +namespace gfx { + +class RendererBackend; + +class BackendScope { +public: + // There are two types of scopes: Creating an "Implicit" scope tells Mapbox GL that the + // supporting windowing system has already activated the GL Backend and that no further actions + // are required. Creating an "Explicit" scope actually enables the GL Backend, and disables it + // when the BackendScope is destroyed. + enum class ScopeType : bool { + Implicit, + Explicit, + }; + + BackendScope(RendererBackend&, ScopeType = ScopeType::Explicit); + ~BackendScope(); + + // Returns true when there is currently a BackendScope active in this thread. + static bool exists(); + +private: + void activate(); + void deactivate(); + + BackendScope* priorScope; + BackendScope* nextScope; + RendererBackend& backend; + const ScopeType scopeType; + bool activated = false; +}; + +} // namespace gfx +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/gfx/renderable.hpp b/mapbox-gl-native/include/mbgl/gfx/renderable.hpp new file mode 100644 index 000000000..1aca5a789 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/gfx/renderable.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { +namespace gfx { + +class RenderableResource { +protected: + explicit RenderableResource() = default; + +public: + virtual ~RenderableResource() = default; + RenderableResource(const RenderableResource&) = delete; + RenderableResource& operator=(const RenderableResource&) = delete; +}; + +class Renderable { +protected: + Renderable(const Size size_, std::unique_ptr resource_) + : size(size_), resource(std::move(resource_)) { + } + virtual ~Renderable() = default; + +public: + Size getSize() const { + return size; + } + + template + T& getResource() const { + assert(resource); + return static_cast(*resource); + } + + virtual void wait() {} + +protected: + Size size; + std::unique_ptr resource; +}; + +} // namespace gfx +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/gfx/renderer_backend.hpp b/mapbox-gl-native/include/mbgl/gfx/renderer_backend.hpp new file mode 100644 index 000000000..aa76d7e50 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/gfx/renderer_backend.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { +namespace gfx { + +class Context; +class Renderable; +class BackendScope; + +// We can make some optimizations if we know that the drawing context is not shared with other code. +enum class ContextMode : bool { + Unique, + Shared, +}; + +class RendererBackend { +protected: + explicit RendererBackend(ContextMode); + +public: + virtual ~RendererBackend(); + RendererBackend(const RendererBackend&) = delete; + RendererBackend& operator=(const RendererBackend&) = delete; + + // Returns the device's context. + Context& getContext(); + + template + T& getContext() { + return static_cast(getContext()); + } + + bool contextIsShared() const { + return contextMode == ContextMode::Shared; + } + + // Returns a reference to the default surface that should be rendered on. + virtual Renderable& getDefaultRenderable() = 0; + +protected: + virtual std::unique_ptr createContext() = 0; + + // Called when the backend's GL context needs to be made active or inactive. These are called, + // as a matched pair, exclusively through BackendScope, in two situations: + // + // 1. When releasing GL resources during Renderer destruction + // (Including calling CustomLayerHost::deinitialize during RenderCustomLayer destruction) + // 2. When renderering through Renderer::render() + // (Including calling CustomLayerHost::initialize for newly added custom layers and + // CustomLayerHost::deinitialize on layer removal) + virtual void activate() = 0; + virtual void deactivate() = 0; + +protected: + std::unique_ptr context; + const ContextMode contextMode; + std::once_flag initialized; + + friend class BackendScope; +}; + +MBGL_CONSTEXPR bool operator==(const RendererBackend& a, const RendererBackend& b) { + return &a == &b; +} + +} // namespace gfx +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/gfx/rendering_stats.hpp b/mapbox-gl-native/include/mbgl/gfx/rendering_stats.hpp new file mode 100644 index 000000000..7cc3edca6 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/gfx/rendering_stats.hpp @@ -0,0 +1,37 @@ +#pragma once + +namespace mbgl { +namespace gfx { + +struct RenderingStats { + RenderingStats() = default; + bool isZero() const; + + int numDrawCalls; + int numActiveTextures; + int numCreatedTextures; + int numBuffers; + int numFrameBuffers; + + int memTextures; + int memIndexBuffers; + int memVertexBuffers; + + RenderingStats& operator+=(const RenderingStats& right); +}; + +inline RenderingStats& RenderingStats::operator+=(const RenderingStats& r) { + numDrawCalls += r.numDrawCalls; + numActiveTextures += r.numActiveTextures; + numCreatedTextures += r.numCreatedTextures; + numBuffers += r.numBuffers; + numFrameBuffers += r.numFrameBuffers; + + memTextures += r.memTextures; + memIndexBuffers += r.memIndexBuffers; + memVertexBuffers += r.memVertexBuffers; + return *this; +} + +} // namespace gfx +} // namespace mbgl \ No newline at end of file diff --git a/mapbox-gl-native/include/mbgl/gl/custom_layer.hpp b/mapbox-gl-native/include/mbgl/gl/custom_layer.hpp new file mode 100644 index 000000000..f879cda80 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/gl/custom_layer.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace style { + +/** + * Parameters that define the current camera position for a `CustomLayerHost::render()` function. + */ +struct CustomLayerRenderParameters { + double width; + double height; + double latitude; + double longitude; + double zoom; + double bearing; + double pitch; + double fieldOfView; + std::array projectionMatrix; +}; + +class CustomLayerHost { +public: + virtual ~CustomLayerHost() = default; + /** + * Initialize any GL state needed by the custom layer. This method is called once, from the + * main thread, at a point when the GL context is active but before rendering for the first + * time. + * + * Resources that are acquired in this method must be released in the `deinitialize` function. + */ + virtual void initialize() = 0; + + /** + * Render the layer. This method is called once per frame. The implementation should not make + * any assumptions about the GL state (other than that the correct context is active). It may + * make changes to the state, and is not required to reset values such as the depth mask, stencil + * mask, and corresponding test flags to their original values. + * Make sure that you are drawing your fragments with a z value of 1 to take advantage of the + * opaque fragment culling in case there are opaque layers above your custom layer. + */ + virtual void render(const CustomLayerRenderParameters&) = 0; + + /** + * Called when the system has destroyed the underlying GL context. The + * `deinitialize` function will not be called in this case, however + * `initialize` will be called instead to prepare for a new render. + * + */ + virtual void contextLost() = 0; + + /** + * Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This + * method is called once, from the main thread, at a point when the GL context is active. + * + * Note that it may be called even when the `initialize` function has not been called. + */ + virtual void deinitialize() = 0; +}; + +class CustomLayer final : public Layer { +public: + CustomLayer(const std::string& id, + std::unique_ptr host); + + CustomLayer(const CustomLayer&) = delete; + ~CustomLayer() final; + class Impl; + const Impl& impl() const; + Mutable mutableImpl() const; + +private: + optional setPropertyInternal(const std::string& name, + const conversion::Convertible& value) final; + StyleProperty getProperty(const std::string&) const final; + std::unique_ptr cloneRef(const std::string& id) const final; + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/gl/custom_layer_factory.hpp b/mapbox-gl-native/include/mbgl/gl/custom_layer_factory.hpp new file mode 100644 index 000000000..963a3dc2f --- /dev/null +++ b/mapbox-gl-native/include/mbgl/gl/custom_layer_factory.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { + +class CustomLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/gl/renderable_resource.hpp b/mapbox-gl-native/include/mbgl/gl/renderable_resource.hpp new file mode 100644 index 000000000..958526927 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/gl/renderable_resource.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace gl { + +class RenderableResource : public gfx::RenderableResource { +protected: + explicit RenderableResource() = default; + +public: + virtual void bind() = 0; + + virtual void swap() { + // Renderable resources that require a swap function to be called explicitly + // can override this method. + } +}; + +} // namespace gl +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/gl/renderer_backend.hpp b/mapbox-gl-native/include/mbgl/gl/renderer_backend.hpp new file mode 100644 index 000000000..e027a7fa4 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/gl/renderer_backend.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace gl { + +using ProcAddress = void (*)(); +using FramebufferID = uint32_t; + +class RendererBackend : public gfx::RendererBackend { +public: + RendererBackend(gfx::ContextMode); + ~RendererBackend() override; + + // Called prior to rendering to update the internally assumed OpenGL state. + virtual void updateAssumedState() = 0; + +protected: + std::unique_ptr createContext() override; + + // Called with the name of an OpenGL extension that should be loaded. RendererBackend implementations + // must call the API-specific version that obtains the function pointer for this function, + // or a null pointer if unsupported/unavailable. + virtual ProcAddress getExtensionFunctionPointer(const char*) = 0; + + // Reads the color pixel data from the currently bound framebuffer. + PremultipliedImage readFramebuffer(const Size&); + + // A constant to signal that a framebuffer is bound, but with an unknown ID. + static constexpr const FramebufferID ImplicitFramebufferBinding = + std::numeric_limits::max(); + + // Tells the renderer that OpenGL state has already been set by the windowing toolkit. + // It sets the internal assumed state to the supplied values. + void assumeFramebufferBinding(FramebufferID fbo); + void assumeViewport(int32_t x, int32_t y, const Size&); + void assumeScissorTest(bool); + + // Returns true when assumed framebuffer binding hasn't changed from the implicit binding. + bool implicitFramebufferBound(); + +public: + // Triggers an OpenGL state update if the internal assumed state doesn't match the + // supplied values. + void setFramebufferBinding(FramebufferID fbo); + void setViewport(int32_t x, int32_t y, const Size&); + void setScissorTest(bool); +}; + +} // namespace gl +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/i18n/collator.hpp b/mapbox-gl-native/include/mbgl/i18n/collator.hpp new file mode 100644 index 000000000..69afbae7d --- /dev/null +++ b/mapbox-gl-native/include/mbgl/i18n/collator.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { +namespace platform { + +class Collator { +public: + explicit Collator(bool caseSensitive, bool diacriticSensitive, const optional& locale = nullopt); + int compare(const std::string& lhs, const std::string& rhs) const; + std::string resolvedLocale() const; + bool operator==(const Collator& other) const; + +private: + class Impl; + std::shared_ptr impl; +}; + +} // namespace platform +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/i18n/number_format.hpp b/mapbox-gl-native/include/mbgl/i18n/number_format.hpp new file mode 100644 index 000000000..cb1e94c7b --- /dev/null +++ b/mapbox-gl-native/include/mbgl/i18n/number_format.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace mbgl { +namespace platform { + +std::string formatNumber(double number, + const std::string& localeId, + const std::string& currency, + uint8_t minFractionDigits, + uint8_t maxFractionDigits); + +} // namespace platform +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/background_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/background_layer_factory.hpp new file mode 100644 index 000000000..f2fa2f7a5 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/background_layer_factory.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace mbgl { + +class BackgroundLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/circle_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/circle_layer_factory.hpp new file mode 100644 index 000000000..aaf5fddfb --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/circle_layer_factory.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace mbgl { + +class CircleLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createLayout(const LayoutParameters& parameters, + std::unique_ptr tileLayer, + const std::vector>& group) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/fill_extrusion_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/fill_extrusion_layer_factory.hpp new file mode 100644 index 000000000..fbaa5c125 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/fill_extrusion_layer_factory.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace mbgl { + +class FillExtrusionLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createLayout(const LayoutParameters&, std::unique_ptr, const std::vector>&) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/fill_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/fill_layer_factory.hpp new file mode 100644 index 000000000..09afb0dc3 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/fill_layer_factory.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace mbgl { + +class FillLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createLayout(const LayoutParameters&, std::unique_ptr, const std::vector>&) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/heatmap_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/heatmap_layer_factory.hpp new file mode 100644 index 000000000..2d0e1a5a0 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/heatmap_layer_factory.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace mbgl { + +class HeatmapLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createBucket(const BucketParameters&, const std::vector>&) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/hillshade_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/hillshade_layer_factory.hpp new file mode 100644 index 000000000..25b4f6c2c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/hillshade_layer_factory.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace mbgl { + +class HillshadeLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/layer_factory.hpp new file mode 100644 index 000000000..afa25bf92 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/layer_factory.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include + +namespace mbgl { + +namespace style { +class LayerProperties; +} // namespace style +class Bucket; +class BucketParameters; +class GeometryTileLayer; +class Layout; +class LayoutParameters; +class RenderLayer; + +/** + * @brief The LayerFactory abstract class + * + * This class is responsible for creation of the layer objects that belong to a concrete layer type. + */ +class LayerFactory { +public: + virtual ~LayerFactory() = default; + /// Returns the layer type data. + virtual const style::LayerTypeInfo* getTypeInfo() const noexcept = 0; + /// Returns a new Layer instance on success call; returns `nullptr` otherwise. + virtual std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept = 0; + /// Returns a new RenderLayer instance. + virtual std::unique_ptr createRenderLayer(Immutable) noexcept = 0; + /// Returns a new Bucket instance on success call; returns `nullptr` otherwise. + virtual std::unique_ptr createBucket(const BucketParameters&, const std::vector>&) noexcept; + /// Returns a new Layout instance on success call; returns `nullptr` otherwise. + virtual std::unique_ptr createLayout(const LayoutParameters&, std::unique_ptr, const std::vector>&) noexcept; + +protected: + optional getSource(const style::conversion::Convertible& value) const noexcept; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/layer_manager.hpp b/mapbox-gl-native/include/mbgl/layermanager/layer_manager.hpp new file mode 100644 index 000000000..c6802dbb4 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/layer_manager.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace style { +class LayerProperties; +} // namespace style +class GeometryTileLayer; +class LayerFactory; +class RenderLayer; +class Bucket; +class BucketParameters; +class Layout; +class LayoutParameters; + +/** + * @brief A singleton class responsible for creating layer instances. + * + * The LayerManager has implementation per platform. The LayerManager implementation + * defines what layer types are available and it can also disable annotations. + * + * Linker excludes the unreachable code for the disabled annotations and layers + * from the binaries, significantly reducing their size. + */ +class LayerManager { +public: + /** + * @brief A singleton getter. + * + * @return LayerManager* + */ + static LayerManager* get() noexcept; + + /// Returns a new Layer instance on success call; returns `nullptr` otherwise. + std::unique_ptr createLayer(const std::string& type, const std::string& id, + const style::conversion::Convertible& value, style::conversion::Error& error) noexcept; + /// Returns a new RenderLayer instance on success call; returns `nullptr` otherwise. + std::unique_ptr createRenderLayer(Immutable) noexcept; + /// Returns a new Bucket instance on success call; returns `nullptr` otherwise. + std::unique_ptr createBucket(const BucketParameters&, const std::vector>&) noexcept; + /// Returns a new Layout instance on success call; returns `nullptr` otherwise. + std::unique_ptr createLayout(const LayoutParameters&, std::unique_ptr, + const std::vector>&) noexcept; + + /** + * @brief a build-time flag to enable/disable annotations in mapbox-gl-native core. + * + * At the moment, the annotations implementation in core is creating concrete + * layer instances apart from LayerManager/LayerFactory code path. + * + * So, annotations must be disabled if the LayerManager implementation does + * not provide line, fill or symbol layers (those, used by the annotations + * implementation). + * + * Note: in future, annotations implemantation will be moved from the core to platform + * SDK (see https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation) + * and this flag won't be needed any more. + */ + static const bool annotationsEnabled; + +protected: + virtual ~LayerManager() = default; + virtual LayerFactory* getFactory(const std::string& type) noexcept = 0; + virtual LayerFactory* getFactory(const style::LayerTypeInfo*) noexcept = 0; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/line_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/line_layer_factory.hpp new file mode 100644 index 000000000..803f550e5 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/line_layer_factory.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace mbgl { + +class LineLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createLayout(const LayoutParameters& parameters, + std::unique_ptr tileLayer, + const std::vector>& group) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/location_indicator_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/location_indicator_layer_factory.hpp new file mode 100644 index 000000000..8d8e3b9a4 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/location_indicator_layer_factory.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace mbgl { + +class LocationIndicatorLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, + const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/raster_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/raster_layer_factory.hpp new file mode 100644 index 000000000..d82137ee8 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/raster_layer_factory.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace mbgl { + +class RasterLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/layermanager/symbol_layer_factory.hpp b/mapbox-gl-native/include/mbgl/layermanager/symbol_layer_factory.hpp new file mode 100644 index 000000000..38f382e96 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/layermanager/symbol_layer_factory.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace mbgl { + +class SymbolLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createLayout(const LayoutParameters& parameters, + std::unique_ptr tileLayer, + const std::vector>& group) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/map/bound_options.hpp b/mapbox-gl-native/include/mbgl/map/bound_options.hpp new file mode 100644 index 000000000..6a410ad0b --- /dev/null +++ b/mapbox-gl-native/include/mbgl/map/bound_options.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +namespace mbgl { + +/** + * @brief Holds options to limit what parts of a map are visible. All fields are + * optional. + */ +struct BoundOptions { + /// Sets the latitude and longitude bounds to which the camera center are constrained + BoundOptions& withLatLngBounds(LatLngBounds b) { bounds = b; return *this; } + /// Sets the minimum zoom level + BoundOptions& withMinZoom(double z) { minZoom = z; return *this; } + /// Sets the maximum zoom level + BoundOptions& withMaxZoom(double z) { maxZoom = z; return *this; } + /// Sets the minimum pitch + BoundOptions& withMinPitch(double p) { + minPitch = p; + return *this; + } + /// Sets the maximum pitch + BoundOptions& withMaxPitch(double p) { + maxPitch = p; + return *this; + } + + /// Constrain the center of the camera to be within these bounds. + optional bounds; + + /// Maximum zoom level allowed. + optional maxZoom; + + /// Minimum zoom level allowed. + optional minZoom; + + /// Maximum pitch allowed in degrees. + optional maxPitch; + + /// Minimum pitch allowed in degrees. + optional minPitch; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/map/camera.hpp b/mapbox-gl-native/include/mbgl/map/camera.hpp new file mode 100644 index 000000000..5f723db1c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/map/camera.hpp @@ -0,0 +1,141 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace mbgl { + +/** Various options for describing the viewpoint of a map. All fields are + optional. + Anchor and center points are mutually exclusive, with preference for the + center point when both are set. + */ +struct CameraOptions { + CameraOptions& withCenter(const optional& o) { center = o; return *this; } + CameraOptions& withPadding(const optional& p) { padding = p; return *this; } + CameraOptions& withAnchor(const optional& o) { anchor = o; return *this; } + CameraOptions& withZoom(const optional& o) { zoom = o; return *this; } + CameraOptions& withBearing(const optional& o) { bearing = o; return *this; } + CameraOptions& withPitch(const optional& o) { pitch = o; return *this; } + + /** Coordinate at the center of the map. */ + optional center; + + /** Padding around the interior of the view that affects the frame of + reference for `center`. */ + optional padding; + + /** Point of reference for `zoom` and `angle`, assuming an origin at the + top-left corner of the view. */ + optional anchor; + + /** Zero-based zoom level. Constrained to the minimum and maximum zoom + levels. */ + optional zoom; + + /** Bearing, measured in degrees from true north. Wrapped to [0, 360). */ + optional bearing; + + /** Pitch toward the horizon measured in degrees , with 0 deg resulting in a + two-dimensional map. */ + optional pitch; +}; + +constexpr bool operator==(const CameraOptions& a, const CameraOptions& b) { + return a.center == b.center + && a.padding == b.padding + && a.anchor == b.anchor + && a.zoom == b.zoom + && a.bearing == b.bearing + && a.pitch == b.pitch; +} + +constexpr bool operator!=(const CameraOptions& a, const CameraOptions& b) { + return !(a == b); +} + +/** Various options for describing a transition between viewpoints with + animation. All fields are optional; the default values depend on how this + struct is used. */ +struct AnimationOptions { + /** Time to animate to the viewpoint defined herein. */ + optional duration; + + /** Average velocity of a flyTo() transition, measured in screenfuls per + second, assuming a linear timing curve. + + A screenful is the visible span in pixels. It does not correspond + to a fixed physical distance but rather varies by zoom level. */ + optional velocity; + + /** Zero-based zoom level at the peak of the flyTo() transition’s flight + path. */ + optional minZoom; + + /** The easing timing curve of the transition. */ + optional easing; + + /** A function that is called on each frame of the transition, just before a + screen update, except on the last frame. The first parameter indicates + the elapsed time as a percentage of the duration. */ + std::function transitionFrameFn; + + /** A function that is called once on the last frame of the transition, just + before the corresponding screen update. */ + std::function transitionFinishFn; + + /** Creates an animation with no options specified. */ + AnimationOptions() = default; + + /** Creates an animation with the specified duration. */ + AnimationOptions(Duration d) + : duration(d) {} +}; + +/** Various options for accessing physical properties of the underlying camera entity. + A direct access to these properties allows more flexible and precise controlling of the camera + while also being fully compatible and interchangeable with CameraOptions. All fields are optional. */ +struct FreeCameraOptions { + /** Position of the camera in slightly modified web mercator coordinates + - The size of 1 unit is the width of the projected world instead of the "mercator meter". + Coordinate [0, 0, 0] is the north-west corner and [1, 1, 0] is the south-east corner. + - Z coordinate is conformal and must respect minimum and maximum zoom values. + - Zoom is automatically computed from the altitude (z) + */ + optional position = nullopt; + + /** Orientation of the camera represented as a unit quaternion [x, y, z, w]. + The default pose of the camera is such that the forward vector is looking up the -Z axis and + the up vector is aligned with north orientation of the map: + forward: [0, 0, -1] + up: [0, -1, 0] + right [1, 0, 0] + + Orientation can be set freely but certain constraints still apply + - Orientation must be representable with only pitch and bearing. + - Pitch has an upper limit */ + optional orientation = nullopt; + + /** Helper function for setting the mercator position as Lat&Lng and altitude in meters */ + void setLocation(const LatLngAltitude& location); + + /** Helper function for converting mercator position into Lat&Lng and altitude in meters. + This function fails to return a value if `position` is invalid or is not set */ + optional getLocation() const; + + /** Helper function for setting orientation of the camera by defining a focus point + on the map. Up vector is required in certain scenarios where bearing can't be deduced + from the viewing direction */ + void lookAtPoint(const LatLng& location, const optional& upVector = nullopt); + + /** Helper function for setting the orientation of the camera as a pitch and a bearing. + Both values are in degrees */ + void setPitchBearing(double pitch, double bearing); +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/map/change.hpp b/mapbox-gl-native/include/mbgl/map/change.hpp new file mode 100644 index 000000000..3925d655a --- /dev/null +++ b/mapbox-gl-native/include/mbgl/map/change.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace mbgl { + +enum MapChange : uint8_t { + MapChangeRegionWillChange = 0, + MapChangeRegionWillChangeAnimated = 1, + MapChangeRegionIsChanging = 2, + MapChangeRegionDidChange = 3, + MapChangeRegionDidChangeAnimated = 4, + MapChangeWillStartLoadingMap = 5, + MapChangeDidFinishLoadingMap = 6, + MapChangeDidFailLoadingMap = 7, + MapChangeWillStartRenderingFrame = 8, + MapChangeDidFinishRenderingFrame = 9, + MapChangeDidFinishRenderingFrameFullyRendered = 10, + MapChangeWillStartRenderingMap = 11, + MapChangeDidFinishRenderingMap = 12, + MapChangeDidFinishRenderingMapFullyRendered = 13, + MapChangeDidFinishLoadingStyle = 14, + MapChangeSourceDidChange = 15 +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/map/map.hpp b/mapbox-gl-native/include/mbgl/map/map.hpp new file mode 100644 index 000000000..3c61140eb --- /dev/null +++ b/mapbox-gl-native/include/mbgl/map/map.hpp @@ -0,0 +1,154 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mbgl { + +class RendererFrontend; + +namespace style { +class Image; +class Style; +} // namespace style + +class Map : private util::noncopyable { +public: + explicit Map(RendererFrontend&, + MapObserver&, + const MapOptions&, + const ResourceOptions&); + ~Map(); + + // Register a callback that will get called (on the render thread) when all resources have + // been loaded and a complete render occurs. + using StillImageCallback = std::function; + void renderStill(StillImageCallback); + void renderStill(const CameraOptions&, MapDebugOptions, StillImageCallback); + + // Triggers a repaint. + void triggerRepaint(); + + style::Style& getStyle(); + const style::Style& getStyle() const; + + void setStyle(std::unique_ptr); + + // Transition + void cancelTransitions(); + void setGestureInProgress(bool); + bool isGestureInProgress() const; + bool isRotating() const; + bool isScaling() const; + bool isPanning() const; + + // Camera + CameraOptions getCameraOptions(const optional& = {}) const; + void jumpTo(const CameraOptions&); + void easeTo(const CameraOptions&, const AnimationOptions&); + void flyTo(const CameraOptions&, const AnimationOptions&); + void moveBy(const ScreenCoordinate&, const AnimationOptions& = {}); + void scaleBy(double scale, const optional& anchor, const AnimationOptions& animation = {}); + void pitchBy(double pitch, const AnimationOptions& animation = {}); + void rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& = {}); + CameraOptions cameraForLatLngBounds(const LatLngBounds&, + const EdgeInsets&, + const optional& bearing = {}, + const optional& pitch = {}) const; + CameraOptions cameraForLatLngs(const std::vector&, + const EdgeInsets&, + const optional& bearing = {}, + const optional& pitch = {}) const; + CameraOptions cameraForGeometry(const Geometry&, + const EdgeInsets&, + const optional& bearing = {}, + const optional& pitch = {}) const; + LatLngBounds latLngBoundsForCamera(const CameraOptions&) const; + LatLngBounds latLngBoundsForCameraUnwrapped(const CameraOptions&) const; + + /// @name Bounds + /// @{ + + void setBounds(const BoundOptions& options); + + /// Returns the current map bound options. All optional fields in BoundOptions are set. + BoundOptions getBounds() const; + + /// @} + + // Map Options + void setNorthOrientation(NorthOrientation); + void setConstrainMode(ConstrainMode); + void setViewportMode(ViewportMode); + void setSize(Size); + MapOptions getMapOptions() const; + + //Projection Mode + void setProjectionMode(const ProjectionMode&); + ProjectionMode getProjectionMode() const; + + // Projection + ScreenCoordinate pixelForLatLng(const LatLng&) const; + LatLng latLngForPixel(const ScreenCoordinate&) const; + std::vector pixelsForLatLngs(const std::vector&) const; + std::vector latLngsForPixels(const std::vector&) const; + + // Annotations + void addAnnotationImage(std::unique_ptr); + void removeAnnotationImage(const std::string&); + double getTopOffsetPixelsForAnnotationImage(const std::string&); + + AnnotationID addAnnotation(const Annotation&); + void updateAnnotation(AnnotationID, const Annotation&); + void removeAnnotation(AnnotationID); + + // Tile prefetching + // + // When loading a map, if `PrefetchZoomDelta` is set to any number greater than 0, the map will + // first request a tile for `zoom - delta` in a attempt to display a full map at lower + // resolution as quick as possible. It will get clamped at the tile source minimum zoom. The + // default `delta` is 4. + void setPrefetchZoomDelta(uint8_t delta); + uint8_t getPrefetchZoomDelta() const; + + // Debug + void setDebug(MapDebugOptions); + MapDebugOptions getDebug() const; + + bool isFullyLoaded() const; + void dumpDebugLogs() const; + + // FreeCameraOptions provides more direct access to the underlying camera entity. + // For backwards compatibility the state set using this API must be representable with + // `CameraOptions` as well. Parameters are clamped to a valid range or discarded as invalid + // if the conversion to the pitch and bearing presentation is ambiguous. For example orientation + // can be invalid if it leads to the camera being upside down or the quaternion has zero length. + void setFreeCameraOptions(const FreeCameraOptions& camera); + FreeCameraOptions getFreeCameraOptions() const; + +protected: + class Impl; + const std::unique_ptr impl; + + // For testing only. + Map(std::unique_ptr); +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/map/map_observer.hpp b/mapbox-gl-native/include/mbgl/map/map_observer.hpp new file mode 100644 index 000000000..258699a2a --- /dev/null +++ b/mapbox-gl-native/include/mbgl/map/map_observer.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +#include +#include + +namespace mbgl { + +enum class MapLoadError { + StyleParseError, + StyleLoadError, + NotFoundError, + UnknownError, +}; + +class MapObserver { +public: + virtual ~MapObserver() = default; + + static MapObserver& nullObserver() { + static MapObserver mapObserver; + return mapObserver; + } + + enum class CameraChangeMode : uint32_t { + Immediate, + Animated + }; + + enum class RenderMode : uint32_t { + Partial, + Full + }; + + struct RenderFrameStatus { + RenderMode mode; + bool needsRepaint; // In continous mode, shows that there are ongoig transitions. + bool placementChanged; + }; + + virtual void onCameraWillChange(CameraChangeMode) {} + virtual void onCameraIsChanging() {} + virtual void onCameraDidChange(CameraChangeMode) {} + virtual void onWillStartLoadingMap() {} + virtual void onDidFinishLoadingMap() {} + virtual void onDidFailLoadingMap(MapLoadError, const std::string&) {} + virtual void onWillStartRenderingFrame() {} + virtual void onDidFinishRenderingFrame(RenderFrameStatus) {} + virtual void onWillStartRenderingMap() {} + virtual void onDidFinishRenderingMap(RenderMode) {} + virtual void onDidFinishLoadingStyle() {} + virtual void onSourceChanged(style::Source&) {} + virtual void onDidBecomeIdle() {} + virtual void onStyleImageMissing(const std::string&) {} + // This method should return true if unused image can be removed, + // false otherwise. By default, unused image will be removed. + virtual bool onCanRemoveUnusedStyleImage(const std::string&) { return true; } +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/map/map_options.hpp b/mapbox-gl-native/include/mbgl/map/map_options.hpp new file mode 100644 index 000000000..fcb8c8f32 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/map/map_options.hpp @@ -0,0 +1,142 @@ +#pragma once + +#include +#include +#include + +#include + +namespace mbgl { + +/** + * @brief Holds values for Map options. + */ +class MapOptions final { +public: + /** + * @brief Constructs a MapOptions object with default values. + */ + MapOptions(); + ~MapOptions(); + + MapOptions(MapOptions&&) noexcept; + + /** + * @brief Sets the map rendering mode. By default, it is set to Continuous + * so the map will render as data arrives from the network and react + * immediately to state changes. + * + * @param mode Map rendering mode. + * @return MapOptions for chaining options together. + */ + MapOptions& withMapMode(MapMode mode); + + /** + * @brief Gets the previously set (or default) map mode. + * + * @return map mode. + */ + MapMode mapMode() const; + + /** + * @brief Sets the map constrain mode. This can be used to limit the map + * to wrap around the globe horizontally. By default, it is set to + * HeightOnly. + * + * @param mode Map constrain mode. + * @return MapOptions for chaining options together. + */ + MapOptions& withConstrainMode(ConstrainMode mode); + + /** + * @brief Gets the previously set (or default) constrain mode. + * + * @return constrain mode. + */ + ConstrainMode constrainMode() const; + + /** + * @brief Sets the viewport mode. This can be used to flip the vertical + * orientation of the map as some devices may use inverted orientation. + * + * @param mode Viewport mode. + * @return MapOptions for chaining options together. + */ + MapOptions& withViewportMode(ViewportMode mode); + + /** + * @brief Gets the previously set (or default) viewport mode. + * + * @return viewport mode. + */ + ViewportMode viewportMode() const; + + /** + * @brief Specify whether to enable cross-source symbol collision detection + * or not. By default, it is set to true. + * + * @param enableCollisions true to enable, false to disable + * @return MapOptions for chaining options together. + */ + MapOptions& withCrossSourceCollisions(bool enableCollisions); + + /** + * @brief Gets the previously set (or default) crossSourceCollisions value. + * + * @return true if ecross-source symbol collision detection enabled, + * false otherwise. + */ + bool crossSourceCollisions() const; + + /** + * @brief Sets the orientation of the Map. By default, it is set to + * Upwards. + * + * @param orientation Orientation of the Map. + * @return reference to MapOptions for chaining options together. + */ + MapOptions& withNorthOrientation(NorthOrientation orientation); + + /** + * @brief Gets the previously set (or default) orientation. + * + * @return Map orientation. + */ + NorthOrientation northOrientation() const; + + /** + * @brief Sets the size to resize the map object and renderer backend. + * + * @param size_ A size given in logical pixels. + * @return reference to MapOptions for chaining options together. + */ + MapOptions& withSize(Size size_); + + /** + * @brief Gets the previously set size. + * + * @return Size. + */ + Size size() const; + + /** + * @brief Sets the custom pixel ratio. By default, it is set to 1. + * + * @param ratio Pixel ratio value. + * @return reference to MapOptions for chaining options together. + */ + MapOptions& withPixelRatio(float ratio); + + /** + * @brief Gets the previously set (or default) pixel ratio value. + * + * @return pixel ratio value. + */ + float pixelRatio() const; + +private: + class Impl; + std::unique_ptr impl_; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/map/mode.hpp b/mapbox-gl-native/include/mbgl/map/mode.hpp new file mode 100644 index 000000000..11dd8583a --- /dev/null +++ b/mapbox-gl-native/include/mbgl/map/mode.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { + +using EnumType = uint32_t; + +enum class MapMode : EnumType { + Continuous, // continually updating map + Static, // a once-off still image of an arbitrary viewport + Tile // a once-off still image of a single tile +}; + +// We can choose to constrain the map both horizontally or vertically, or only +// vertically e.g. while panning. +enum class ConstrainMode : EnumType { + None, + HeightOnly, + WidthAndHeight, +}; + +// Satisfies embedding platforms that requires the viewport coordinate systems +// to be set according to its standards. +enum class ViewportMode : EnumType { + Default, + FlippedY, +}; + +enum class MapDebugOptions : EnumType { + NoDebug = 0, + TileBorders = 1 << 1, + ParseStatus = 1 << 2, + Timestamps = 1 << 3, + Collision = 1 << 4, + Overdraw = 1 << 5, + StencilClip = 1 << 6, + DepthBuffer = 1 << 7, +}; + +MBGL_CONSTEXPR MapDebugOptions operator|(MapDebugOptions lhs, MapDebugOptions rhs) { + return MapDebugOptions(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs)); +} + +MBGL_CONSTEXPR MapDebugOptions& operator|=(MapDebugOptions& lhs, MapDebugOptions rhs) { + return (lhs = MapDebugOptions(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs))); +} + +MBGL_CONSTEXPR bool operator&(MapDebugOptions lhs, MapDebugOptions rhs) { + return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs); +} + +MBGL_CONSTEXPR MapDebugOptions& operator&=(MapDebugOptions& lhs, MapDebugOptions rhs) { + return (lhs = MapDebugOptions(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs))); +} + +MBGL_CONSTEXPR MapDebugOptions operator~(MapDebugOptions value) { + return MapDebugOptions(~mbgl::underlying_type(value)); +} + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/map/projection_mode.hpp b/mapbox-gl-native/include/mbgl/map/projection_mode.hpp new file mode 100644 index 000000000..4617371e1 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/map/projection_mode.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +namespace mbgl { + +/** + * @brief Holds values for Axonometric rendering. All fields are + * optional. + */ +struct ProjectionMode { + ProjectionMode& withAxonometric(bool o) { axonometric = o; return *this; } + ProjectionMode& withXSkew(double o) { xSkew = o; return *this; } + ProjectionMode& withYSkew(double o) { ySkew = o; return *this; } + + /** + * @brief Set to True to enable axonometric rendering, false otherwise. + */ + optional axonometric; + + /** + * @brief The X skew value represents how much to skew on the x-axis. + */ + optional xSkew; + + /** + * @brief The Y skew value represents how much to skew on the y-axis. + */ + optional ySkew; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/math/clamp.hpp b/mapbox-gl-native/include/mbgl/math/clamp.hpp new file mode 100644 index 000000000..38d430547 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/math/clamp.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace mbgl { +namespace util { + +template +T clamp(T value, T min_, T max_) { + return max(min_, min(max_, value)); +} + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/math/log2.hpp b/mapbox-gl-native/include/mbgl/math/log2.hpp new file mode 100644 index 000000000..4f5fde60d --- /dev/null +++ b/mapbox-gl-native/include/mbgl/math/log2.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +#if defined(__ANDROID__) +#include +#endif + +namespace mbgl { +namespace util { + +// Computes the log2(x) rounded up to the next integer. +// (== number of bits required to store x) +uint32_t ceil_log2(uint64_t x); + +template +T log2(T x) { +// log2() is producing wrong results on ARMv5 binaries +// running on ARMv7+ CPUs. +#if defined(__ANDROID__) + return ::log(x) / M_LN2; +#else + return ::log2(x); +#endif +} + +} // namespace util +} // namespace mbgl + +// log2 is not available on Android before API 18. +#if defined(__ANDROID__) && defined(__GNUC__) && \ + defined(__ANDROID_API__) && __ANDROID_API__ < 18 + +template +typename std::enable_if_t::value, T> log2(T x) { + return ::log(x) / M_LN2; +} + +#endif diff --git a/mapbox-gl-native/include/mbgl/math/minmax.hpp b/mapbox-gl-native/include/mbgl/math/minmax.hpp new file mode 100644 index 000000000..e8c50a39c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/math/minmax.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace util { + +template +typename std::enable_if_t::value, T> max(T a, T b) { + return std::max(a, b); +} + +template +typename std::enable_if_t::value, T> max(T a, T b) { + return ::fmax(a, b); +} + +template +typename std::enable_if_t::value, T> max(T a, T b, Ts... args) { + return max(a, max(b, args...)); +} + +template +typename std::enable_if_t::value, T> min(T a, T b) { + return std::min(a, b); +} + +template +typename std::enable_if_t::value, T> min(T a, T b) { + return ::fmin(a, b); +} + +template +typename std::enable_if_t::value, T> min(T a, T b, Ts... args) { + return min(a, min(b, args...)); +} + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/math/wrap.hpp b/mapbox-gl-native/include/mbgl/math/wrap.hpp new file mode 100644 index 000000000..307622538 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/math/wrap.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace mbgl { +namespace util { + +// Constrains n to the given range (including min, excluding max) via modular +// arithmetic. +template +T wrap(T value, T min, T max) { + if (value >= min && value < max) { + return value; + } else if (value == max) { + return min; + } + + const T delta = max - min; + const T wrapped = min + std::fmod(value - min, delta); + return value < min ? wrapped + delta : wrapped; +} + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/platform/gl_functions.hpp b/mapbox-gl-native/include/mbgl/platform/gl_functions.hpp new file mode 100644 index 000000000..b617b3561 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/platform/gl_functions.hpp @@ -0,0 +1,337 @@ +#pragma once + +#include + +// Pointers to OpenGL ES 2.0 functions. They must be +// initialized by the platform at linking time. + +#ifndef NDEBUG +#define MBGL_CHECK_ERROR(cmd) ([&]() { struct __MBGL_CHECK_ERROR { ~__MBGL_CHECK_ERROR() noexcept(false) { mbgl::platform::glCheckError(#cmd, __FILE__, __LINE__); } } __MBGL_CHECK_ERROR; return cmd; }()) +#else +#define MBGL_CHECK_ERROR(cmd) (cmd) +#endif + +namespace mbgl { +namespace platform { + +using GLbitfield = unsigned int; +using GLboolean = unsigned char; +using GLchar = char; +using GLdouble = double; +using GLenum = unsigned int; +using GLfloat = float; +using GLint = int; +using GLsizei = int; +using GLubyte = unsigned char; +using GLuint = unsigned int; +using GLvoid = void; + +#if defined(_WIN32) +using GLintptr = long long; +using GLsizeiptr = long long; +#else +using GLintptr = long; +using GLsizeiptr = long; +#endif + +/// Pointer to glActiveTexture OpenGL function. +extern void (* const glActiveTexture)(GLenum); +/// Pointer to glAttachShader OpenGL function. +extern void (* const glAttachShader)(GLuint, GLuint); +/// Pointer to glBindAttribLocation OpenGL function. +extern void (* const glBindAttribLocation)(GLuint, GLuint, const GLchar *); +/// Pointer to glBindBuffer OpenGL function. +extern void (* const glBindBuffer)(GLenum, GLuint); +/// Pointer to glBindFramebuffer OpenGL function. +extern void (* const glBindFramebuffer)(GLenum, GLuint); +/// Pointer to glBindRenderbuffer OpenGL function. +extern void (* const glBindRenderbuffer)(GLenum, GLuint); +/// Pointer to glBindTexture OpenGL function. +extern void (* const glBindTexture)(GLenum, GLuint); +/// Pointer to glBlendColor OpenGL function. +extern void (* const glBlendColor)(GLfloat, GLfloat, GLfloat, GLfloat); +/// Pointer to glBlendEquation OpenGL function. +extern void (* const glBlendEquation)(GLenum); +/// Pointer to glBlendEquationSeparate OpenGL function. +extern void (* const glBlendEquationSeparate)(GLenum, GLenum); +/// Pointer to glBlendFunc OpenGL function. +extern void (* const glBlendFunc)(GLenum, GLenum); +/// Pointer to glBlendFuncSeparate OpenGL function. +extern void (* const glBlendFuncSeparate)(GLenum, GLenum, GLenum, GLenum); +/// Pointer to glBufferData OpenGL function. +extern void (* const glBufferData)(GLenum, GLsizeiptr, const void *, GLenum); +/// Pointer to glBufferSubData OpenGL function. +extern void (* const glBufferSubData)(GLenum, GLintptr, GLsizeiptr, const void *); +/// Pointer to glCheckFramebufferStatus OpenGL function. +extern GLenum (* const glCheckFramebufferStatus)(GLenum); +/// Pointer to glClear OpenGL function. +extern void (* const glClear)(GLbitfield); +/// Pointer to glClearColor OpenGL function. +extern void (* const glClearColor)(GLfloat, GLfloat, GLfloat, GLfloat); +/// Pointer to glClearDepthf OpenGL function. +extern void (* const glClearDepthf)(GLfloat); +/// Pointer to glClearStencil OpenGL function. +extern void (* const glClearStencil)(GLint); +/// Pointer to glColorMask OpenGL function. +extern void (* const glColorMask)(GLboolean, GLboolean, GLboolean, GLboolean); +/// Pointer to glCompileShader OpenGL function. +extern void (* const glCompileShader)(GLuint); +/// Pointer to glCompressedTexImage2D OpenGL function. +extern void (* const glCompressedTexImage2D)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const void *); +/// Pointer to glCompressedTexSubImage2D OpenGL function. +extern void (* const glCompressedTexSubImage2D)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const void *); +/// Pointer to glCopyTexImage2D OpenGL function. +extern void (* const glCopyTexImage2D)(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint); +/// Pointer to glCopyTexSubImage2D OpenGL function. +extern void (* const glCopyTexSubImage2D)(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei); +/// Pointer to glCreateProgram OpenGL function. +extern GLuint (* const glCreateProgram)(); +/// Pointer to glCreateShader OpenGL function. +extern GLuint (* const glCreateShader)(GLenum); +/// Pointer to glCullFace OpenGL function. +extern void (* const glCullFace)(GLenum); +/// Pointer to glDeleteBuffers OpenGL function. +extern void (* const glDeleteBuffers)(GLsizei, const GLuint *); +/// Pointer to glDeleteFramebuffers OpenGL function. +extern void (* const glDeleteFramebuffers)(GLsizei, const GLuint *); +/// Pointer to glDeleteProgram OpenGL function. +extern void (* const glDeleteProgram)(GLuint); +/// Pointer to glDeleteRenderbuffers OpenGL function. +extern void (* const glDeleteRenderbuffers)(GLsizei, const GLuint *); +/// Pointer to glDeleteShader OpenGL function. +extern void (* const glDeleteShader)(GLuint); +/// Pointer to glDeleteTextures OpenGL function. +extern void (* const glDeleteTextures)(GLsizei, const GLuint *); +/// Pointer to glDepthFunc OpenGL function. +extern void (* const glDepthFunc)(GLenum); +/// Pointer to glDepthMask OpenGL function. +extern void (* const glDepthMask)(GLboolean); +/// Pointer to glDepthRangef OpenGL function. +extern void (* const glDepthRangef)(GLfloat, GLfloat); +/// Pointer to glDetachShader OpenGL function. +extern void (* const glDetachShader)(GLuint, GLuint); +/// Pointer to glDisable OpenGL function. +extern void (* const glDisable)(GLenum); +/// Pointer to glDisableVertexAttribArray OpenGL function. +extern void (* const glDisableVertexAttribArray)(GLuint); +/// Pointer to glDrawArrays OpenGL function. +extern void (* const glDrawArrays)(GLenum, GLint, GLsizei); +/// Pointer to glDrawElements OpenGL function. +extern void (* const glDrawElements)(GLenum, GLsizei, GLenum, const void *); +/// Pointer to glEnable OpenGL function. +extern void (* const glEnable)(GLenum); +/// Pointer to glEnableVertexAttribArray OpenGL function. +extern void (* const glEnableVertexAttribArray)(GLuint); +/// Pointer to glFinish OpenGL function. +extern void (* const glFinish)(); +/// Pointer to glFlush OpenGL function. +extern void (* const glFlush)(); +/// Pointer to glFramebufferRenderbuffer OpenGL function. +extern void (* const glFramebufferRenderbuffer)(GLenum, GLenum, GLenum, GLuint); +/// Pointer to glFramebufferTexture2D OpenGL function. +extern void (* const glFramebufferTexture2D)(GLenum, GLenum, GLenum, GLuint, GLint); +/// Pointer to glFrontFace OpenGL function. +extern void (* const glFrontFace)(GLenum); +/// Pointer to glGenBuffers OpenGL function. +extern void (* const glGenBuffers)(GLsizei, GLuint *); +/// Pointer to glGenerateMipmap OpenGL function. +extern void (* const glGenerateMipmap)(GLenum); +/// Pointer to glGenFramebuffers OpenGL function. +extern void (* const glGenFramebuffers)(GLsizei, GLuint *); +/// Pointer to glGenRenderbuffers OpenGL function. +extern void (* const glGenRenderbuffers)(GLsizei, GLuint *); +/// Pointer to glGenTextures OpenGL function. +extern void (* const glGenTextures)(GLsizei, GLuint *); +/// Pointer to glGetActiveAttrib OpenGL function. +extern void (* const glGetActiveAttrib)(GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *); +/// Pointer to glGetActiveUniform OpenGL function. +extern void (* const glGetActiveUniform)(GLuint, GLuint, GLsizei, GLsizei *, GLint *, GLenum *, GLchar *); +/// Pointer to glGetAttachedShaders OpenGL function. +extern void (* const glGetAttachedShaders)(GLuint, GLsizei, GLsizei *, GLuint *); +/// Pointer to glGetAttribLocation OpenGL function. +extern GLint (* const glGetAttribLocation)(GLuint, const GLchar *); +/// Pointer to glGetBooleanv OpenGL function. +extern void (* const glGetBooleanv)(GLenum, GLboolean *); +/// Pointer to glGetBufferParameteriv OpenGL function. +extern void (* const glGetBufferParameteriv)(GLenum, GLenum, GLint *); +/// Pointer to glGetError OpenGL function. +extern GLenum (* const glGetError)(); +/// Pointer to glGetFloatv OpenGL function. +extern void (* const glGetFloatv)(GLenum, GLfloat *); +/// Pointer to glGetFramebufferAttachmentParameteriv OpenGL function. +extern void (* const glGetFramebufferAttachmentParameteriv)(GLenum, GLenum, GLenum, GLint *); +/// Pointer to glGetIntegerv OpenGL function. +extern void (* const glGetIntegerv)(GLenum, GLint *); +/// Pointer to glGetProgramInfoLog OpenGL function. +extern void (* const glGetProgramInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *); +/// Pointer to glGetProgramiv OpenGL function. +extern void (* const glGetProgramiv)(GLuint, GLenum, GLint *); +/// Pointer to glGetRenderbufferParameteriv OpenGL function. +extern void (* const glGetRenderbufferParameteriv)(GLenum, GLenum, GLint *); +/// Pointer to glGetShaderInfoLog OpenGL function. +extern void (* const glGetShaderInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *); +/// Pointer to glGetShaderiv OpenGL function. +extern void (* const glGetShaderiv)(GLuint, GLenum, GLint *); +/// Pointer to glGetShaderSource OpenGL function. +extern void (* const glGetShaderSource)(GLuint, GLsizei, GLsizei *, GLchar *); +/// Pointer to glGetString OpenGL function. +extern const GLubyte *(*glGetString)(GLenum); +/// Pointer to glGetTexParameterfv OpenGL function. +extern void (* const glGetTexParameterfv)(GLenum, GLenum, GLfloat *); +/// Pointer to glGetTexParameteriv OpenGL function. +extern void (* const glGetTexParameteriv)(GLenum, GLenum, GLint *); +/// Pointer to glGetUniformfv OpenGL function. +extern void (* const glGetUniformfv)(GLuint, GLint, GLfloat *); +/// Pointer to glGetUniformiv OpenGL function. +extern void (* const glGetUniformiv)(GLuint, GLint, GLint *); +/// Pointer to glGetUniformLocation OpenGL function. +extern GLint (* const glGetUniformLocation)(GLuint, const GLchar *); +/// Pointer to glGetVertexAttribfv OpenGL function. +extern void (* const glGetVertexAttribfv)(GLuint, GLenum, GLfloat *); +/// Pointer to glGetVertexAttribiv OpenGL function. +extern void (* const glGetVertexAttribiv)(GLuint, GLenum, GLint *); +/// Pointer to glGetVertexAttribPointerv OpenGL function. +extern void (* const glGetVertexAttribPointerv)(GLuint, GLenum, void **); +/// Pointer to glHint OpenGL function. +extern void (* const glHint)(GLenum, GLenum); +/// Pointer to glIsBuffer OpenGL function. +extern GLboolean (* const glIsBuffer)(GLuint); +/// Pointer to glIsEnabled OpenGL function. +extern GLboolean (* const glIsEnabled)(GLenum); +/// Pointer to glIsFramebuffer OpenGL function. +extern GLboolean (* const glIsFramebuffer)(GLuint); +/// Pointer to glIsProgram OpenGL function. +extern GLboolean (* const glIsProgram)(GLuint); +/// Pointer to glIsRenderbuffer OpenGL function. +extern GLboolean (* const glIsRenderbuffer)(GLuint); +/// Pointer to glIsShader OpenGL function. +extern GLboolean (* const glIsShader)(GLuint); +/// Pointer to glIsTexture OpenGL function. +extern GLboolean (* const glIsTexture)(GLuint); +/// Pointer to glLineWidth OpenGL function. +extern void (* const glLineWidth)(GLfloat); +/// Pointer to glLinkProgram OpenGL function. +extern void (* const glLinkProgram)(GLuint); +/// Pointer to glPixelStorei OpenGL function. +extern void (* const glPixelStorei)(GLenum, GLint); +/// Pointer to glPolygonOffset OpenGL function. +extern void (* const glPolygonOffset)(GLfloat, GLfloat); +/// Pointer to glReadPixels OpenGL function. +extern void (* const glReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void *); +/// Pointer to glRenderbufferStorage OpenGL function. +extern void (* const glRenderbufferStorage)(GLenum, GLenum, GLsizei, GLsizei); +/// Pointer to glSampleCoverage OpenGL function. +extern void (* const glSampleCoverage)(GLfloat, GLboolean); +/// Pointer to glScissor OpenGL function. +extern void (* const glScissor)(GLint, GLint, GLsizei, GLsizei); +/// Pointer to glShaderSource OpenGL function. +extern void (* const glShaderSource)(GLuint, GLsizei, const GLchar * const*, const GLint *); +/// Pointer to glStencilFunc OpenGL function. +extern void (* const glStencilFunc)(GLenum, GLint, GLuint); +/// Pointer to glStencilFuncSeparate OpenGL function. +extern void (* const glStencilFuncSeparate)(GLenum, GLenum, GLint, GLuint); +/// Pointer to glStencilMask OpenGL function. +extern void (* const glStencilMask)(GLuint); +/// Pointer to glStencilMaskSeparate OpenGL function. +extern void (* const glStencilMaskSeparate)(GLenum, GLuint); +/// Pointer to glStencilOp OpenGL function. +extern void (* const glStencilOp)(GLenum, GLenum, GLenum); +/// Pointer to glStencilOpSeparate OpenGL function. +extern void (* const glStencilOpSeparate)(GLenum, GLenum, GLenum, GLenum); +/// Pointer to glTexImage2D OpenGL function. +extern void (* const glTexImage2D)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void *); +/// Pointer to glTexParameterf OpenGL function. +extern void (* const glTexParameterf)(GLenum, GLenum, GLfloat); +/// Pointer to glTexParameterfv OpenGL function. +extern void (* const glTexParameterfv)(GLenum, GLenum, const GLfloat *); +/// Pointer to glTexParameteri OpenGL function. +extern void (* const glTexParameteri)(GLenum, GLenum, GLint); +/// Pointer to glTexParameteriv OpenGL function. +extern void (* const glTexParameteriv)(GLenum, GLenum, const GLint *); +/// Pointer to glTexSubImage2D OpenGL function. +extern void (* const glTexSubImage2D)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void *); +/// Pointer to glUniform1f OpenGL function. +extern void (* const glUniform1f)(GLint, GLfloat); +/// Pointer to glUniform1fv OpenGL function. +extern void (* const glUniform1fv)(GLint, GLsizei, const GLfloat *); +/// Pointer to glUniform1i OpenGL function. +extern void (* const glUniform1i)(GLint, GLint); +/// Pointer to glUniform1iv OpenGL function. +extern void (* const glUniform1iv)(GLint, GLsizei, const GLint *); +/// Pointer to glUniform2f OpenGL function. +extern void (* const glUniform2f)(GLint, GLfloat, GLfloat); +/// Pointer to glUniform2fv OpenGL function. +extern void (* const glUniform2fv)(GLint, GLsizei, const GLfloat *); +/// Pointer to glUniform2i OpenGL function. +extern void (* const glUniform2i)(GLint, GLint, GLint); +/// Pointer to glUniform2iv OpenGL function. +extern void (* const glUniform2iv)(GLint, GLsizei, const GLint *); +/// Pointer to glUniform3f OpenGL function. +extern void (* const glUniform3f)(GLint, GLfloat, GLfloat, GLfloat); +/// Pointer to glUniform3fv OpenGL function. +extern void (* const glUniform3fv)(GLint, GLsizei, const GLfloat *); +/// Pointer to glUniform3i OpenGL function. +extern void (* const glUniform3i)(GLint, GLint, GLint, GLint); +/// Pointer to glUniform3iv OpenGL function. +extern void (* const glUniform3iv)(GLint, GLsizei, const GLint *); +/// Pointer to glUniform4f OpenGL function. +extern void (* const glUniform4f)(GLint, GLfloat, GLfloat, GLfloat, GLfloat); +/// Pointer to glUniform4fv OpenGL function. +extern void (* const glUniform4fv)(GLint, GLsizei, const GLfloat *); +/// Pointer to glUniform4i OpenGL function. +extern void (* const glUniform4i)(GLint, GLint, GLint, GLint, GLint); +/// Pointer to glUniform4iv OpenGL function. +extern void (* const glUniform4iv)(GLint, GLsizei, const GLint *); +/// Pointer to glUniformMatrix2fv OpenGL function. +extern void (* const glUniformMatrix2fv)(GLint, GLsizei, GLboolean, const GLfloat *); +/// Pointer to glUniformMatrix3fv OpenGL function. +extern void (* const glUniformMatrix3fv)(GLint, GLsizei, GLboolean, const GLfloat *); +/// Pointer to glUniformMatrix4fv OpenGL function. +extern void (* const glUniformMatrix4fv)(GLint, GLsizei, GLboolean, const GLfloat *); +/// Pointer to glUseProgram OpenGL function. +extern void (* const glUseProgram)(GLuint); +/// Pointer to glValidateProgram OpenGL function. +extern void (* const glValidateProgram)(GLuint); +/// Pointer to glVertexAttrib1f OpenGL function. +extern void (* const glVertexAttrib1f)(GLuint, GLfloat); +/// Pointer to glVertexAttrib1fv OpenGL function. +extern void (* const glVertexAttrib1fv)(GLuint, const GLfloat *); +/// Pointer to glVertexAttrib2f OpenGL function. +extern void (* const glVertexAttrib2f)(GLuint, GLfloat, GLfloat); +/// Pointer to glVertexAttrib2fv OpenGL function. +extern void (* const glVertexAttrib2fv)(GLuint, const GLfloat *); +/// Pointer to glVertexAttrib3f OpenGL function. +extern void (* const glVertexAttrib3f)(GLuint, GLfloat, GLfloat, GLfloat); +/// Pointer to glVertexAttrib3fv OpenGL function. +extern void (* const glVertexAttrib3fv)(GLuint, const GLfloat *); +/// Pointer to glVertexAttrib4f OpenGL function. +extern void (* const glVertexAttrib4f)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +/// Pointer to glVertexAttrib4fv OpenGL function. +extern void (* const glVertexAttrib4fv)(GLuint, const GLfloat *); +/// Pointer to glVertexAttribPointer OpenGL function. +extern void (* const glVertexAttribPointer)(GLuint, GLint, GLenum, GLboolean, GLsizei, const void *); +/// Pointer to glViewport OpenGL function. +extern void (* const glViewport)(GLint, GLint, GLsizei, GLsizei); + +#ifndef MBGL_USE_GLES2 +/// Pointer to glDrawPixels OpenGL function. +extern void (* const glDrawPixels)(GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); +/// Pointer to glGetDoublev OpenGL function. +extern void (* const glGetDoublev)(GLenum, GLdouble *); +/// Pointer to glPixelTransferf OpenGL function. +extern void (* const glPixelTransferf)(GLenum, GLfloat); +/// Pointer to glPixelZoom OpenGL function. +extern void (* const glPixelZoom)(GLfloat, GLfloat); +/// Pointer to glPointSize OpenGL function. +extern void (* const glPointSize)(GLfloat); +/// Pointer to glRasterPos4d OpenGL function. +extern void (* const glRasterPos4d)(GLdouble, GLdouble, GLdouble, GLdouble); +#endif + +#ifndef NDEBUG +/// Check for GL errors and print on the console. +void glCheckError(const char *cmd, const char *file, int line); +#endif + +} // namespace platform +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/platform/settings.hpp b/mapbox-gl-native/include/mbgl/platform/settings.hpp new file mode 100644 index 000000000..af5dcce21 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/platform/settings.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace platform { + +#define DECLARE_MAPBOX_SETTING(name, value) constexpr const char* name = "mapbox_" #value + +// The value for EXPERIMENTAL_THREAD_PRIORITY_* keys, must be a double. +DECLARE_MAPBOX_SETTING(EXPERIMENTAL_THREAD_PRIORITY_WORKER, thread_priority_worker); +DECLARE_MAPBOX_SETTING(EXPERIMENTAL_THREAD_PRIORITY_FILE, thread_priority_file); +DECLARE_MAPBOX_SETTING(EXPERIMENTAL_THREAD_PRIORITY_NETWORK, thread_priority_network); +DECLARE_MAPBOX_SETTING(EXPERIMENTAL_THREAD_PRIORITY_DATABASE, thread_priority_database); + +// Settings class provides non-persistent, in-process key-value storage. +class Settings final { +public: + // Returns singleton instance + static Settings& getInstance() noexcept; + + // Sets setting value for a specified key. + void set(const std::string& key, mapbox::base::Value value) noexcept; + + // Sets multiple setting values by merging current Settings object + // with provided `values` object. + void set(const mapbox::base::ValueObject& values) noexcept; + + // Returns setting value for a specified key or NullValue if element + // for specified key is missing. + mapbox::base::Value get(const std::string& key) const noexcept; + + // Returns values for settings whose keys are equal to the ones provided in `keys` argument. + // Null values would be provided for keys whose elements are missing in Settings. + mapbox::base::ValueObject get(const std::vector& keys) const noexcept; + +private: + Settings(); + +private: + class Impl; + std::unique_ptr impl; +}; + +} // namespace platform +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/platform/thread.hpp b/mapbox-gl-native/include/mbgl/platform/thread.hpp new file mode 100644 index 000000000..72899a4fd --- /dev/null +++ b/mapbox-gl-native/include/mbgl/platform/thread.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace mbgl { +namespace platform { + +// Called when a thread is created +void attachThread(); + +// Called when a thread is destroyed +void detachThread(); + +} // namespace platform +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/platform/time.hpp b/mapbox-gl-native/include/mbgl/platform/time.hpp new file mode 100644 index 000000000..876952451 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/platform/time.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace mbgl { +namespace platform { + +// Returns the current time. Abstracted because some platforms +// will not allow direct access to the current time via syscall. +std::chrono::time_point now(); + +} // namespace platform +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/renderer/query.hpp b/mapbox-gl-native/include/mbgl/renderer/query.hpp new file mode 100644 index 000000000..4cadf4f01 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/renderer/query.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include +#include + +namespace mbgl { + +/** + * Options for query rendered features. + */ +class RenderedQueryOptions { +public: + RenderedQueryOptions(optional> layerIDs_ = {}, + optional filter_ = {}) + : layerIDs(std::move(layerIDs_)), + filter(std::move(filter_)) {} + + /** layerIDs to include in the query */ + optional> layerIDs; + + optional filter; +}; + +/** + * Options for query source features + */ +class SourceQueryOptions { +public: + SourceQueryOptions(optional> sourceLayers_ = {}, + optional filter_ = {}) + : sourceLayers(std::move(sourceLayers_)), + filter(std::move(filter_)) {} + + // Required for VectorSource, ignored for GeoJSONSource + optional> sourceLayers; + + optional filter; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/renderer/renderer.hpp b/mapbox-gl-native/include/mbgl/renderer/renderer.hpp new file mode 100644 index 000000000..1bd51f0f7 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/renderer/renderer.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mbgl { + +class RendererObserver; +class RenderedQueryOptions; +class SourceQueryOptions; +class UpdateParameters; + +namespace gfx { +class RendererBackend; +} // namespace gfx + +struct PlacedSymbolData { + // Contents of the label + std::u16string key; + // If symbol contains text, text collision box in viewport coordinates + optional> textCollisionBox; + // If symbol contains icon, icon collision box in viewport coordinates + optional> iconCollisionBox; + // Symbol text was placed + bool textPlaced; + // Symbol icon was placed + bool iconPlaced; + // Symbol text or icon collision box intersects tile borders + bool intersectsTileBorder; + // Viewport padding ({viewportPadding, viewportPadding} is a coordinate of the tile's top-left corner) + float viewportPadding; + // Layer id (leader of the symbol layout group) + std::string layer; +}; + +class Renderer { +public: + Renderer(gfx::RendererBackend&, float pixelRatio_, const optional& localFontFamily = {}); + ~Renderer(); + + void markContextLost(); + + void setObserver(RendererObserver*); + + void render(const std::shared_ptr&); + + // Feature queries + std::vector queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions& options = {}) const; + std::vector queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options = {}) const; + std::vector queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options = {}) const; + std::vector querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}) const; + AnnotationIDs queryPointAnnotations(const ScreenBox& box) const; + AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const; + AnnotationIDs getAnnotationIDs(const std::vector&) const; + + // Feature extension query + FeatureExtensionValue queryFeatureExtensions(const std::string& sourceID, + const Feature& feature, + const std::string& extension, + const std::string& extensionField, + const optional>& args = {}) const; + + void setFeatureState(const std::string& sourceID, const optional& sourceLayerID, + const std::string& featureID, const FeatureState& state); + + void getFeatureState(FeatureState& state, + const std::string& sourceID, + const optional& sourceLayerID, + const std::string& featureID) const; + + void removeFeatureState(const std::string& sourceID, + const optional& sourceLayerID, + const optional& featureID, + const optional& stateKey); + + // Debug + void dumpDebugLogs(); + + /** + * @brief In Tile map mode, enables or disables collecting of the placed symbols data, + * which can be obtained with `getPlacedSymbolsData()`. + * + * The placed symbols data collecting is disabled by default. + */ + void collectPlacedSymbolData(bool enable); + + /** + * @brief If collecting of the placed symbols data is enabled, returns the reference + * to the `PlacedSymbolData` vector holding the collected data. + * + * Note: the returned vector gets re-populated at every `render()` call. + * + * @return collected placed symbols data + */ + const std::vector& getPlacedSymbolsData() const; + + // Memory + void reduceMemoryUse(); + void clearData(); + +private: + class Impl; + std::unique_ptr impl; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/renderer/renderer_frontend.hpp b/mapbox-gl-native/include/mbgl/renderer/renderer_frontend.hpp new file mode 100644 index 000000000..f72b0ccdd --- /dev/null +++ b/mapbox-gl-native/include/mbgl/renderer/renderer_frontend.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace mbgl { + +class RendererObserver; +class UpdateParameters; + +// The RenderFrontend is the bridge between the Map and +// platform used to update and observer the Renderer +// +// It hides any threading specifics and always replies on +// the original thread. +class RendererFrontend { +public: + + virtual ~RendererFrontend() = default; + + // Must synchronously clean up the Renderer if set + virtual void reset() = 0; + + // Implementer must bind the renderer observer to the renderer in a + // appropriate manner so that the callbacks occur on the main thread + virtual void setObserver(RendererObserver&) = 0; + + // Coalescing updates is up to the implementer + virtual void update(std::shared_ptr) = 0; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/renderer/renderer_observer.hpp b/mapbox-gl-native/include/mbgl/renderer/renderer_observer.hpp new file mode 100644 index 000000000..5d5a7bb5f --- /dev/null +++ b/mapbox-gl-native/include/mbgl/renderer/renderer_observer.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace mbgl { + +class RendererObserver { +public: + virtual ~RendererObserver() = default; + + enum class RenderMode : uint32_t { + Partial, + Full + }; + + // Signals that a repaint is required + virtual void onInvalidate() {} + + // Resource failed to download / parse + virtual void onResourceError(std::exception_ptr) {} + + // First frame + virtual void onWillStartRenderingMap() {} + + // Start of frame, initial is the first frame for this map + virtual void onWillStartRenderingFrame() {} + + // End of frame, booleans flags that a repaint is required and that placement changed. + virtual void onDidFinishRenderingFrame(RenderMode, bool /*repaint*/, bool /*placementChanged*/) {} + + // Final frame + virtual void onDidFinishRenderingMap() {} + + // Style is missing an image + using StyleImageMissingCallback = std::function; + virtual void onStyleImageMissing(const std::string&, const StyleImageMissingCallback& done) { done(); } + virtual void onRemoveUnusedStyleImages(const std::vector&) {} +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/renderer/renderer_state.hpp b/mapbox-gl-native/include/mbgl/renderer/renderer_state.hpp new file mode 100644 index 000000000..71011389e --- /dev/null +++ b/mapbox-gl-native/include/mbgl/renderer/renderer_state.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +namespace mbgl { + +class UpdateParameters; + +/** + * RendererState acts as a Memento object that is able to extract data out of UpdateParameters (an opaque object + * from the public API perspective). + */ +class RendererState { +public: + + // Obtains a CameraOptions out of a given UpdateParameters object. + static CameraOptions getCameraOptions(const UpdateParameters&, const EdgeInsets& = {}); + + // Returns whether an image with the given ID exists from a given UpdateParamters object. + static bool hasImage(const UpdateParameters&, const std::string& id); + + // Returns whether a layer with the given ID exists from a given UpdateParamters object. + static bool hasLayer(const UpdateParameters&, const std::string& id); + + // Returns whether a source with the given ID exists from a given UpdateParamters object. + static bool hasSource(const UpdateParameters&, const std::string& id); + + // Obtains the geographical coordinate out of a given screen coordinate. + static ScreenCoordinate pixelForLatLng(const UpdateParameters&, const LatLng&); + + // Obtains the screen coordinate out of a given geographical coordinate. + static LatLng latLngForPixel(const UpdateParameters&, const ScreenCoordinate&); +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/database_file_source.hpp b/mapbox-gl-native/include/mbgl/storage/database_file_source.hpp new file mode 100644 index 000000000..8a4791462 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/database_file_source.hpp @@ -0,0 +1,240 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { + +class ResourceOptions; + +// TODO: Split DatabaseFileSource into Ambient cache and Database interfaces. +class DatabaseFileSource : public FileSource { +public: + explicit DatabaseFileSource(const ResourceOptions& options); + ~DatabaseFileSource() override; + + // FileSource overrides + std::unique_ptr request(const Resource&, Callback) override; + void forward(const Resource&, const Response&, std::function callback) override; + bool canRequest(const Resource&) const override; + void setProperty(const std::string&, const mapbox::base::Value&) override; + void pause() override; + void resume() override; + + // Methods common to Ambient cache and Offline functionality + + /* + * Sets path of a database to be used by DatabaseFileSource and invokes provided + * callback when a database path is set. + */ + virtual void setDatabasePath(const std::string&, std::function callback); + + /* + * Delete existing database and re-initialize. + * + * When the operation is complete or encounters an error, the given callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + */ + virtual void resetDatabase(std::function); + + /* + * Packs the existing database file into a minimal amount of disk space. + * + * This operation has a performance impact as it will vacuum the database, + * forcing it to move pages on the filesystem. + * + * When the operation is complete or encounters an error, the given callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + */ + virtual void packDatabase(std::function callback); + + /* + * Sets whether packing the database file occurs automatically after an offline + * region is deleted (deleteOfflineRegion()) or the ambient cache is cleared + * (clearAmbientCache()). + * + * By default, packing is enabled. If disabled, disk space will not be freed + * after resources are removed unless packDatabase() is explicitly called. + */ + virtual void runPackDatabaseAutomatically(bool); + + // Ambient cache + + /* + * Insert the provided resource into the ambient cache + * + * Consumers of the resource will expect the uncompressed version; the + * OfflineDatabase will determine whether to compress the data on disk. + * This call is asynchronous: the data may not be immediately available + * for in-progress requests, although subsequent requests should have + * access to the cached data. + */ + virtual void put(const Resource&, const Response&); + + /* + * Forces revalidation of the ambient cache. + * + * Forces Mapbox GL Native to revalidate resources stored in the ambient + * cache with the tile server before using them, making sure they + * are the latest version. This is more efficient than cleaning the + * cache because if the resource is considered valid after the server + * lookup, it will not get downloaded again. + * + * Resources overlapping with offline regions will not be affected + * by this call. + */ + virtual void invalidateAmbientCache(std::function); + + /* + * Erase resources from the ambient cache, freeing storage space. + * + * Erases the ambient cache, freeing resources. + * + * Note that this operation can be potentially slow if packing the database + * occurs automatically (see runPackDatabaseAutomatically() and packDatabase()). + * + * Resources overlapping with offline regions will not be affected + * by this call. + */ + virtual void clearAmbientCache(std::function); + + /* + * Sets the maximum size in bytes for the ambient cache. + * + * This call is potentially expensive because it will try + * to trim the data in case the database is larger than the + * size defined. The size of offline regions are not affected + * by this settings, but the ambient cache will always try + * to not exceed the maximum size defined, taking into account + * the current size for the offline regions. + * + * If the maximum size is set to 50 MB and 40 MB are already + * used by offline regions, the cache size will be effectively + * 10 MB. + * + * Setting the size to 0 will disable the cache if there is no + * offline region on the database. + * + * This method should always be called before using the database, + * otherwise the default maximum size will be used. + */ + virtual void setMaximumAmbientCacheSize(uint64_t size, std::function callback); + + // Offline + + /* + * Retrieve all regions in the offline database. + * + * The query will be executed asynchronously and the results passed to the given + * callback, which will be executed on the database thread; it is the responsibility + * of the SDK bindings to re-execute a user-provided callback on the main thread. + */ + virtual void listOfflineRegions(std::function)>); + + /* + * Create an offline region in the database. + * + * When the initial database queries have completed, the provided callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + * + * Note that the resulting region will be in an inactive download state; to begin + * downloading resources, call `setOfflineRegionDownloadState(OfflineRegionDownloadState::Active)`, + * optionally registering an `OfflineRegionObserver` beforehand. + */ + virtual void createOfflineRegion(const OfflineRegionDefinition& definition, + const OfflineRegionMetadata& metadata, + std::function)>); + /* + * Update an offline region metadata in the database. + */ + virtual void updateOfflineMetadata(int64_t regionID, + const OfflineRegionMetadata& metadata, + std::function)>); + + /* + * Register an observer to be notified when the state of the region changes. + */ + virtual void setOfflineRegionObserver(const OfflineRegion&, std::unique_ptr); + + /* + * Pause or resume downloading of regional resources. + */ + virtual void setOfflineRegionDownloadState(const OfflineRegion&, OfflineRegionDownloadState); + + /* + * Retrieve the current status of the region. The query will be executed + * asynchronously and the results passed to the given callback, which will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + */ + virtual void getOfflineRegionStatus(const OfflineRegion&, + std::function)>) const; + + /* + * Merge offline regions from a secondary database into the main offline database. + * + * When the database merge is completed, the provided callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + * + * The secondary database may need to be upgraded to the latest schema. This is done + * in-place and requires write-access to `sideDatabasePath`; it is the + * responsibility of the SDK bindings to ensure that this path is writeable. + * + * Only resources and tiles that belong to a region will be copied over. Identical + * regions will be flattened into a single new region in the main database. + * + * Invokes the callback with a `MapboxOfflineTileCountExceededException` error if + * the merge operation would result in the offline tile count limit being exceeded. + * + * Merged regions may not be in a completed status if the secondary database + * does not contain all the tiles or resources required by the region definition. + */ + virtual void mergeOfflineRegions(const std::string& sideDatabasePath, + std::function)>); + + /* + * Remove an offline region from the database and perform any resources evictions + * necessary as a result. + * + * Eviction works by removing the least-recently requested resources not also required + * by other regions, until the database shrinks below a certain size. + * + * Note that this method takes ownership of the input, reflecting the fact that once + * region deletion is initiated, it is not legal to perform further actions with the + * region. + * + * Note that this operation can be potentially slow if packing the database occurs + * automatically (see runPackDatabaseAutomatically() and packDatabase()). + * + * When the operation is complete or encounters an error, the given callback will be + * executed on the database thread; it is the responsibility of the SDK bindings + * to re-execute a user-provided callback on the main thread. + */ + virtual void deleteOfflineRegion(const OfflineRegion&, std::function); + + /* + * Invalidate all the tiles from an offline region forcing Mapbox GL to revalidate + * the tiles with the server before using. This is more efficient than deleting the + * offline region and downloading it again because if the data on the cache matches + * the server, no new data gets transmitted. + */ + virtual void invalidateOfflineRegion(const OfflineRegion&, std::function); + + /* + * Changing or bypassing this limit without permission from Mapbox is prohibited + * by the Mapbox Terms of Service. + */ + virtual void setOfflineMapboxTileCountLimit(uint64_t) const; + +private: + class Impl; + const std::unique_ptr impl; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/file_source.hpp b/mapbox-gl-native/include/mbgl/storage/file_source.hpp new file mode 100644 index 000000000..562fd8da0 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/file_source.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace mbgl { + +class AsyncRequest; +class Resource; + +// TODO: Rename to ResourceProviderType +enum FileSourceType : uint8_t { + Asset, + // TODO: split to separate types + // - Cache for fast KV store (FASTER, LevelDB, RocksDB) + // - Database for read-only offline use-cases + Database, + FileSystem, + Network, + // Resource loader acts as a proxy and has logic + // for request delegation to Asset, Cache, and other + // file sources. + ResourceLoader +}; + +// TODO: Rename to ResourceProvider to avoid confusion with +// GeoJSONSource, RasterSource, VectorSource, CustomGeometrySource and other *Sources. +class FileSource { +public: + FileSource(const FileSource&) = delete; + FileSource& operator=(const FileSource&) = delete; + virtual ~FileSource() = default; + + using Callback = std::function; + + // Request a resource. The callback will be called asynchronously, in the same + // thread as the request was made. This thread must have an active RunLoop. The + // request may be cancelled before completion by releasing the returned AsyncRequest. + // If the request is cancelled before the callback is executed, the callback will + // not be executed. + virtual std::unique_ptr request(const Resource&, Callback) = 0; + + // Allows to forward response from one source to another. + // Optionally, callback can be provided to receive notification for forward + // operation. + // + // NOLINTNEXTLINE(performance-unnecessary-value-param) + virtual void forward(const Resource&, const Response&, std::function) {} + + // When a file source supports consulting a local cache only, it must return true. + // Cache-only requests are requests that aren't as urgent, but could be useful, e.g. + // to cover part of the map while loading. The FileSource should only do cheap actions to + // retrieve the data, e.g. load it from a cache, but not from the internet. + virtual bool supportsCacheOnlyRequests() const { return false; } + + // Checks whether a resource could be requested from this file source. + virtual bool canRequest(const Resource&) const = 0; + + /* + * Pause file request activity. + * + * If pause is called then no revalidation or network request activity + * will occur. + */ + virtual void pause() {} + + /* + * Resume file request activity. + * + * Calling resume will unpause the file source and process any tasks that + * expired while the file source was paused. + */ + virtual void resume() {} + + /* + * Generic property setter / getter methods. + */ + virtual void setProperty(const std::string&, const mapbox::base::Value&){}; + virtual mapbox::base::Value getProperty(const std::string&) const { return {}; }; + + // When supported, sets the modifier of the requested resources. + virtual void setResourceTransform(ResourceTransform) {} // NOLINT(performance-unnecessary-value-param) + +protected: + FileSource() = default; +}; + +// Properties that may be supported by online file sources: + +// Property name to set / get an access token. +// type: std::string +constexpr const char* ACCESS_TOKEN_KEY = "access-token"; + +// Property name to set / get base url. +// type: std::string +constexpr const char* API_BASE_URL_KEY = "api-base-url"; + +// Property name to set / get maximum number of concurrent requests. +// type: unsigned +constexpr const char* MAX_CONCURRENT_REQUESTS_KEY = "max-concurrent-requests"; + +// Properties that may be supported by database file sources: + +// Property to set database mode. When set, database opens in read-only mode; database opens in read-write-create mode +// otherwise. type: bool +constexpr const char* READ_ONLY_MODE_KEY = "read-only-mode"; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/file_source_manager.hpp b/mapbox-gl-native/include/mbgl/storage/file_source_manager.hpp new file mode 100644 index 000000000..e62d321af --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/file_source_manager.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +namespace mbgl { + +class ResourceOptions; + +/** + * @brief A singleton class responsible for managing file sources. + * + * The FileSourceManager provides following functionality: + * + * - provides access to file sources of a specific type and configuration + * - caches previously created file sources of a (type, configuration) tuples + * - allows to register and unregister file source factories + */ +class FileSourceManager { +public: + using FileSourceFactory = std::function(const ResourceOptions&)>; + + /** + * @brief A singleton getter. + * + * @return FileSourceManager* + */ + static FileSourceManager* get() noexcept; + + // Returns shared instance of a file source for (type, options) tuple. + // Creates new instance via registered factory if needed. If new instance cannot be + // created, nullptr would be returned. + PassRefPtr getFileSource(FileSourceType, const ResourceOptions&) noexcept; + + // Registers file source factory for a provided FileSourceType type. If factory for the + // same type was already registered, will unregister previously registered factory. + // Provided factory must not be null. + virtual void registerFileSourceFactory(FileSourceType, FileSourceFactory&&) noexcept; + + // Unregisters file source factory. If there are no registered factories for a FileSourceType + // invocation has no effect. + virtual FileSourceFactory unRegisterFileSourceFactory(FileSourceType) noexcept; + +protected: + FileSourceManager(); + class Impl; + std::unique_ptr impl; + virtual ~FileSourceManager(); +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/network_status.hpp b/mapbox-gl-native/include/mbgl/storage/network_status.hpp new file mode 100644 index 000000000..0d1ae54ec --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/network_status.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + +namespace util { +class AsyncTask; +} // namespace util + +class NetworkStatus { +public: + enum class Status : uint8_t { + Online, + Offline, + }; + + static Status Get(); + static void Set(Status); + + static void Reachable(); + + static void Subscribe(util::AsyncTask* async); + static void Unsubscribe(util::AsyncTask* async); + +private: + static std::atomic online; + static std::mutex mtx; + static std::unordered_set observers; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/offline.hpp b/mapbox-gl-native/include/mbgl/storage/offline.hpp new file mode 100644 index 000000000..97e7fc2e4 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/offline.hpp @@ -0,0 +1,235 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +class TileID; + +/* + * An offline region defined by a style URL, geographic bounding box, zoom range, and + * device pixel ratio. + * + * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom. + * + * maxZoom may be ∞, in which case for each tile source, the region will include + * tiles from minZoom up to the maximum zoom level provided by that source. + * + * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0. + */ +class OfflineTilePyramidRegionDefinition { +public: + OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float, bool); + + /* Private */ + std::string styleURL; + LatLngBounds bounds; + double minZoom; + double maxZoom; + float pixelRatio; + bool includeIdeographs; +}; + +/* + * An offline region defined by a style URL, geometry, zoom range, and + * device pixel ratio. + * + * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom. + * + * maxZoom may be ∞, in which case for each tile source, the region will include + * tiles from minZoom up to the maximum zoom level provided by that source. + * + * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0. + */ +class OfflineGeometryRegionDefinition { +public: + OfflineGeometryRegionDefinition(std::string styleURL, Geometry, double minZoom, double maxZoom, float pixelRatio, bool includeIdeographs); + + /* Private */ + std::string styleURL; + Geometry geometry; + double minZoom; + double maxZoom; + float pixelRatio; + bool includeIdeographs; +}; + +/* + * The offline region definition types supported + */ +using OfflineRegionDefinition = variant; + +/* + * The encoded format is private. + */ +std::string encodeOfflineRegionDefinition(const OfflineRegionDefinition&); +OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string&); + +/* + * Arbitrary binary region metadata. The contents are opaque to the mbgl implementation; + * it just stores and retrieves a BLOB. SDK bindings should leave the interpretation of + * this data up to the application; they _should not_ enforce a higher-level data format. + * In the future we want offline database to be portable across target platforms, and a + * platform-specific metadata format would prevent that. + */ +using OfflineRegionMetadata = std::vector; + +/* + * A region is either inactive (not downloading, but previously-downloaded + * resources are available for use), or active (resources are being downloaded + * or will be downloaded, if necessary, when network access is available). + * + * This state is independent of whether or not the complete set of resources + * is currently available for offline use. To check if that is the case, use + * `OfflineRegionStatus::complete()`. + */ +enum class OfflineRegionDownloadState { + Inactive, + Active +}; + +/* + * A region's status includes its active/inactive state as well as counts + * of the number of resources that have completed downloading, their total + * size in bytes, and the total number of resources that are required. + * + * Note that the total required size in bytes is not currently available. A + * future API release may provide an estimate of this number. + */ +class OfflineRegionStatus { +public: + OfflineRegionDownloadState downloadState = OfflineRegionDownloadState::Inactive; + + /** + * The number of resources that have been fully downloaded and are ready for + * offline access. + */ + uint64_t completedResourceCount = 0; + + /** + * The cumulative size, in bytes, of all resources (inclusive of tiles) that have + * been fully downloaded. + */ + uint64_t completedResourceSize = 0; + + /** + * The number of tiles that are known to be required for this region. This is a + * subset of `completedResourceCount`. + */ + uint64_t completedTileCount = 0; + + /** + * The number of tiles that are known to be required for this region. + */ + uint64_t requiredTileCount = 0; + + /** + * The cumulative size, in bytes, of all tiles that have been fully downloaded. + * This is a subset of `completedResourceSize`. + */ + uint64_t completedTileSize = 0; + + /** + * The number of resources that are known to be required for this region. See the + * documentation for `requiredResourceCountIsPrecise` for an important caveat + * about this number. + */ + uint64_t requiredResourceCount = 0; + + /** + * This property is true when the value of requiredResourceCount is a precise + * count of the number of required resources, and false when it is merely a lower + * bound. + * + * Specifically, it is false during early phases of an offline download. Once + * style and tile sources have been downloaded, it is possible to calculate the + * precise number of required resources, at which point it is set to true. + */ + bool requiredResourceCountIsPrecise = false; + + bool complete() const { + return completedResourceCount >= requiredResourceCount; + } +}; + +/* + * A region can have a single observer, which gets notified whenever a change + * to the region's status occurs. + */ +class OfflineRegionObserver { +public: + virtual ~OfflineRegionObserver() = default; + + /* + * Implement this method to be notified of a change in the status of an + * offline region. Status changes include any change in state of the members + * of OfflineRegionStatus. + * + * Note that this method will be executed on the database thread; it is the + * responsibility of the SDK bindings to wrap this object in an interface that + * re-executes the user-provided implementation on the main thread. + */ + virtual void statusChanged(OfflineRegionStatus) {} + + /* + * Implement this method to be notified of errors encountered while downloading + * regional resources. Such errors may be recoverable; for example the implementation + * will attempt to re-request failed resources based on an exponential backoff + * algorithm, or when it detects that network access has been restored. + * + * Note that this method will be executed on the database thread; it is the + * responsibility of the SDK bindings to wrap this object in an interface that + * re-executes the user-provided implementation on the main thread. + */ + virtual void responseError(Response::Error) {} // NOLINT(performance-unnecessary-value-param) + + /* + * Implement this method to be notified when the limit on the number of Mapbox + * tiles stored for offline regions has been reached. + * + * Once the limit has been reached, the SDK will not download further offline + * tiles from Mapbox APIs until existing tiles have been removed. + * + * This limit does not apply to non-Mapbox tile sources. + * + * Note that this method will be executed on the database thread; it is the + * responsibility of the SDK bindings to wrap this object in an interface that + * re-executes the user-provided implementation on the main thread. + */ + virtual void mapboxTileCountLimitExceeded(uint64_t /* limit */) {} +}; + +class OfflineRegion { +public: + ~OfflineRegion(); + OfflineRegion() = delete; + + int64_t getID() const; + const OfflineRegionDefinition& getDefinition() const; + const OfflineRegionMetadata& getMetadata() const; + +private: + friend class OfflineDatabase; + + OfflineRegion(int64_t id, + OfflineRegionDefinition, + OfflineRegionMetadata); + + int64_t id; + OfflineRegionDefinition definition; + OfflineRegionMetadata metadata; +}; + +using OfflineRegions = std::vector; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/online_file_source.hpp b/mapbox-gl-native/include/mbgl/storage/online_file_source.hpp new file mode 100644 index 000000000..d830450f9 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/online_file_source.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace mbgl { + +class OnlineFileSource : public FileSource { +public: + OnlineFileSource(); + ~OnlineFileSource() override; + +private: + // FileSource overrides + std::unique_ptr request(const Resource&, Callback) override; + bool canRequest(const Resource&) const override; + void pause() override; + void resume() override; + void setProperty(const std::string&, const mapbox::base::Value&) override; + mapbox::base::Value getProperty(const std::string&) const override; + void setResourceTransform(ResourceTransform) override; + + class Impl; + const std::unique_ptr impl; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/resource.hpp b/mapbox-gl-native/include/mbgl/storage/resource.hpp new file mode 100644 index 000000000..bee359e36 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/resource.hpp @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace mbgl { + +class Resource { +public: + enum Kind : uint8_t { + Unknown = 0, + Style, + Source, + Tile, + Glyphs, + SpriteImage, + SpriteJSON, + Image + }; + + enum class Priority : bool { + Regular, + Low + }; + + enum class Usage : bool { + Online, + Offline + }; + + enum class StoragePolicy : bool { Permanent, Volatile }; + + struct TileData { + std::string urlTemplate; + uint8_t pixelRatio; + int32_t x; + int32_t y; + int8_t z; + }; + + enum class LoadingMethod : uint8_t { + None = 0b00, + Cache = 0b01, + Network = 0b10, + + CacheOnly = Cache, + NetworkOnly = Network, + All = Cache | Network, + }; + + Resource(Kind kind_, + std::string url_, + optional tileData_ = {}, + LoadingMethod loadingMethod_ = LoadingMethod::All) + : kind(kind_), + loadingMethod(loadingMethod_), + url(std::move(url_)), + tileData(std::move(tileData_)) { + } + + void setPriority(Priority p) { priority = p; } + void setUsage(Usage u) { usage = u; } + + bool hasLoadingMethod(LoadingMethod method) const; + + static Resource style(const std::string& url); + static Resource source(const std::string& url); + static Resource tile(const std::string& urlTemplate, + float pixelRatio, + int32_t x, + int32_t y, + int8_t z, + Tileset::Scheme scheme, + LoadingMethod = LoadingMethod::All); + static Resource glyphs(const std::string& urlTemplate, + const FontStack& fontStack, + const std::pair& glyphRange); + static Resource spriteImage(const std::string& base, float pixelRatio); + static Resource spriteJSON(const std::string& base, float pixelRatio); + static Resource image(const std::string& url); + + Kind kind; + LoadingMethod loadingMethod; + Usage usage{ Usage::Online }; + Priority priority{ Priority::Regular }; + std::string url; + + // Includes auxiliary data if this is a tile request. + optional tileData; + + optional priorModified = {}; + optional priorExpires = {}; + optional priorEtag = {}; + std::shared_ptr priorData; + Duration minimumUpdateInterval{Duration::zero()}; + StoragePolicy storagePolicy{StoragePolicy::Permanent}; +}; + +inline bool Resource::hasLoadingMethod(Resource::LoadingMethod method) const { + return (loadingMethod & method); +} + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/resource_options.hpp b/mapbox-gl-native/include/mbgl/storage/resource_options.hpp new file mode 100644 index 000000000..6d603b8cc --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/resource_options.hpp @@ -0,0 +1,122 @@ +#pragma once + +#include +#include + +namespace mbgl { + +/** + * @brief Holds values for resource options. + */ +class ResourceOptions final { +public: + /** + * @brief Constructs a ResourceOptions object with default values. + */ + ResourceOptions(); + ~ResourceOptions(); + + ResourceOptions(ResourceOptions&&) noexcept; + + ResourceOptions clone() const; + + /** + * @brief Sets the Mapbox access token - see https://docs.mapbox.com/help/how-mapbox-works/access-tokens/ for details. + * + * @param token Mapbox access token. + * @return ResourceOptions for chaining options together. + */ + ResourceOptions& withAccessToken(std::string token); + + /** + * @brief Gets the previously set (or default) Mapbox access token. + * + * @return const std::string& Mapbox access token. + */ + const std::string& accessToken() const; + + /** + * @brief Sets the API base URL. Default is https://api.mapbox.com for Mapbox. + * + * @param baseURL API base URL. + * @return ResourceOptions for chaining options together. + */ + ResourceOptions& withBaseURL(std::string baseURL); + + /** + * @brief Gets the previously set (or default) API base URL. + * + * @return const std::string& API base URL. + */ + const std::string& baseURL() const; + + /** + * @brief Sets the cache path. + * + * @param path Cache path. + * @return ResourceOptions for chaining options together. + */ + ResourceOptions& withCachePath(std::string path); + + /** + * @brief Gets the previously set (or default) cache path. + * + * @return cache path + */ + const std::string& cachePath() const; + + /** + * @brief Sets the asset path, which is the root directory from where + * the asset:// scheme gets resolved in a style. + * + * @param path Asset path. + * @return ResourceOptions for chaining options together. + */ + ResourceOptions& withAssetPath(std::string path); + + /** + * @brief Gets the previously set (or default) asset path. + * + * @return asset path + */ + const std::string& assetPath() const; + + /** + * @brief Sets the maximum cache size. + * + * @param size Cache maximum size in bytes. + * @return reference to ResourceOptions for chaining options together. + */ + ResourceOptions& withMaximumCacheSize(uint64_t size); + + /** + * @brief Gets the previously set (or default) maximum allowed cache size. + * + * @return maximum allowed cache database size in bytes. + */ + uint64_t maximumCacheSize() const; + + /** + * @brief Sets the platform context. A platform context is usually an object + * that assists the creation of a file source. + * + * @param context Platform context. + * @return reference to ResourceOptions for chaining options together. + */ + ResourceOptions& withPlatformContext(void* context); + + /** + * @brief Gets the previously set (or default) platform context. + * + * @return Platform context. + */ + void* platformContext() const; + +private: + ResourceOptions(const ResourceOptions&); + + class Impl; + std::unique_ptr impl_; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/resource_transform.hpp b/mapbox-gl-native/include/mbgl/storage/resource_transform.hpp new file mode 100644 index 000000000..29d19bb4e --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/resource_transform.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { + +class ResourceTransform { +public: + using FinishedCallback = std::function; + using TransformCallback = std::function; + + ResourceTransform(TransformCallback = {}); + void transform(Resource::Kind, const std::string& url, FinishedCallback); + explicit operator bool() const { return bool(transformCallback); } + +private: + TransformCallback transformCallback; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/storage/response.hpp b/mapbox-gl-native/include/mbgl/storage/response.hpp new file mode 100644 index 000000000..07b059e43 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/storage/response.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +#include +#include + +namespace mbgl { + +class Response { +public: + Response() = default; + Response(const Response&); + Response& operator=(const Response&); + +public: + class Error; + // When this object is empty, the response was successful. + std::unique_ptr error; + + // This is set to true for 204 Not Modified responses, and, for backward compatibility, + // for 404 Not Found responses for tiles. + bool noContent = false; + + // This is set to true for 304 Not Modified responses. + bool notModified = false; + + // This is set to true when the server requested that no expired resources be used by + // specifying "Cache-Control: must-revalidate". + bool mustRevalidate = false; + + // The actual data of the response. Present only for non-error, non-notModified responses. + std::shared_ptr data; + + optional modified; + optional expires; + optional etag; + + bool isFresh() const { + return expires ? *expires > util::now() : !error; + } + + // Indicates whether we are allowed to use this response according to HTTP caching rules. + // It may or may not be stale. + bool isUsable() const { + return !mustRevalidate || (expires && *expires > util::now()); + } +}; + +class Response::Error { +public: + enum class Reason : uint8_t { + Success = 1, + NotFound = 2, + Server = 3, + Connection = 4, + RateLimit = 5, + Other = 6, + } reason = Reason::Other; + + // An error message from the request handler, e.g. a server message or a system message + // informing the user about the reason for the failure. + std::string message; + + optional retryAfter; + +public: + Error(Reason, std::string = "", optional = {}); +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/color_ramp_property_value.hpp b/mapbox-gl-native/include/mbgl/style/color_ramp_property_value.hpp new file mode 100644 index 000000000..1b9385f85 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/color_ramp_property_value.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { + +/* + * Special-case implementation of (a subset of) the PropertyValue interface + * used for building the HeatmapColor paint property traits class. + */ +class ColorRampPropertyValue { +private: + std::shared_ptr value; + + friend bool operator==(const ColorRampPropertyValue& lhs, const ColorRampPropertyValue& rhs) { + return (lhs.isUndefined() && rhs.isUndefined()) || (lhs.value && rhs.value && *(lhs.value) == *(rhs.value)); + } + + friend bool operator!=(const ColorRampPropertyValue& lhs, const ColorRampPropertyValue& rhs) { + return !(lhs == rhs); + } + +public: + ColorRampPropertyValue() : value(nullptr) {} + ColorRampPropertyValue(std::shared_ptr value_) : value(std::move(value_)) {} + + bool isUndefined() const { return value == nullptr; } + + // noop, needed for batch evaluation of paint property values to compile + template + Color evaluate(const Evaluator&, TimePoint = {}) const { return {}; } + + Color evaluate(double rampEvaluationParameter) const { + const auto result = value->evaluate(expression::EvaluationContext({}, nullptr, {rampEvaluationParameter})); + return *expression::fromExpressionValue(*result); + } + + bool isDataDriven() const { return false; } + bool hasDataDrivenPropertyDifference(const ColorRampPropertyValue&) const { return false; } + + const expression::Expression& getExpression() const { return *value; } +}; + + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion.hpp b/mapbox-gl-native/include/mbgl/style/conversion.hpp new file mode 100644 index 000000000..29af9fac9 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace mbgl { +namespace style { +namespace conversion { + +// This is a forward-declaration only header intended to minimize dependencies and to improve +// compilation speed. In order to specialize implementations and get access to the actual +// implementation, include . + +struct Error { std::string message; }; + +template +class ConversionTraits; + +class Convertible; + +template +struct Converter; + +template +struct ValueFactory; + +} // namespace conversion +} // namespace style +} // namespace mbgl + diff --git a/mapbox-gl-native/include/mbgl/style/conversion/color_ramp_property_value.hpp b/mapbox-gl-native/include/mbgl/style/conversion/color_ramp_property_value.hpp new file mode 100644 index 000000000..17aa17742 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/color_ramp_property_value.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error, bool /* allowDataExpressions */ = false, bool /* convertTokens */ = false) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/constant.hpp b/mapbox-gl-native/include/mbgl/style/conversion/constant.hpp new file mode 100644 index 000000000..4acddfb01 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/constant.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error) const; +}; + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error) const; +}; + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error) const; +}; + +template +struct Converter::value>> { + optional operator()(const Convertible& value, Error& error) const; +}; + +template +struct Converter, typename std::enable_if_t::value>> { + optional> operator()(const Convertible& value, Error& error) const; +}; + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error) const; +}; + +template +struct Converter> { + optional> operator()(const Convertible& value, Error& error) const; +}; + +template <> +struct Converter> { + optional> operator()(const Convertible& value, Error& error) const; +}; + +template +struct Converter> { + optional> operator()(const Convertible& value, Error& error) const; +}; + +template <> +struct Converter> { + optional> operator()(const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/coordinate.hpp b/mapbox-gl-native/include/mbgl/style/conversion/coordinate.hpp new file mode 100644 index 000000000..1346ed738 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/coordinate.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template<> +struct Converter { +public: + optional operator() (const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/custom_geometry_source_options.hpp b/mapbox-gl-native/include/mbgl/style/conversion/custom_geometry_source_options.hpp new file mode 100644 index 000000000..090e5b623 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/custom_geometry_source_options.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/filter.hpp b/mapbox-gl-native/include/mbgl/style/conversion/filter.hpp new file mode 100644 index 000000000..2d7ad0afc --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/filter.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { +public: + optional operator()(const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/function.hpp b/mapbox-gl-native/include/mbgl/style/conversion/function.hpp new file mode 100644 index 000000000..9ceeb979c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/function.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +bool hasTokens(const std::string&); +std::unique_ptr convertTokenStringToFormatExpression(const std::string&); +std::unique_ptr convertTokenStringToImageExpression(const std::string&); +std::unique_ptr convertTokenStringToExpression(const std::string&); + +optional> convertFunctionToExpression(expression::type::Type, const Convertible&, Error&, bool convertTokens); + +template +optional> convertFunctionToExpression(const Convertible& value, Error& error, bool convertTokens); + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/geojson.hpp b/mapbox-gl-native/include/mbgl/style/conversion/geojson.hpp new file mode 100644 index 000000000..90c1d6419 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/geojson.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +// Workaround until https://github.com/mapbox/mapbox-gl-native/issues/5623 is done. +optional parseGeoJSON(const std::string&, Error&); + +template <> +struct Converter { +public: + optional operator()(const Convertible&, Error&) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/geojson_options.hpp b/mapbox-gl-native/include/mbgl/style/conversion/geojson_options.hpp new file mode 100644 index 000000000..89511848d --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/geojson_options.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/get_json_type.hpp b/mapbox-gl-native/include/mbgl/style/conversion/get_json_type.hpp new file mode 100644 index 000000000..f7efebccc --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/get_json_type.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +std::string getJSONType(const Convertible& value); + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/layer.hpp b/mapbox-gl-native/include/mbgl/style/conversion/layer.hpp new file mode 100644 index 000000000..9cf019378 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/layer.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter> { +public: + optional> operator()(const Convertible& value, Error& error) const; +}; + +optional setPaintProperties(Layer& layer, const Convertible& value); + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/light.hpp b/mapbox-gl-native/include/mbgl/style/conversion/light.hpp new file mode 100644 index 000000000..2f6f8628b --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/light.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { +public: + optional operator()(const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/position.hpp b/mapbox-gl-native/include/mbgl/style/conversion/position.hpp new file mode 100644 index 000000000..10db5c6ec --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/position.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/property_value.hpp b/mapbox-gl-native/include/mbgl/style/conversion/property_value.hpp new file mode 100644 index 000000000..925f97af1 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/property_value.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template +struct Converter> { + optional> operator()(const Convertible& value, Error& error, bool allowDataExpressions, bool convertTokens) const; + + template + PropertyValue maybeConvertTokens(const S& t) const { + return PropertyValue(t); + }; + + PropertyValue maybeConvertTokens(const std::string& t) const { + return hasTokens(t) + ? PropertyValue(PropertyExpression(convertTokenStringToExpression(t))) + : PropertyValue(t); + } + + PropertyValue maybeConvertTokens(const expression::Formatted& t) const { + // This only works with a single-section `Formatted` created automatically + // by parsing a plain-text `text-field` property. + // Token conversion happens later than the initial string->Formatted conversion + // General purpose `format` expressions with embedded tokens are not supported + const std::string& firstUnformattedSection = t.sections[0].text; + return hasTokens(firstUnformattedSection) + ? PropertyValue(PropertyExpression(convertTokenStringToFormatExpression(firstUnformattedSection))) + : PropertyValue(t); + } + + PropertyValue maybeConvertTokens(const expression::Image& image) const { + return hasTokens(image.id()) + ? PropertyValue(PropertyExpression(convertTokenStringToImageExpression(image.id()))) + : PropertyValue(image); + } +}; + +template <> +struct Converter>, void> { + optional>> operator()(const Convertible& value, Error& error, bool, bool) const; +}; +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/rotation.hpp b/mapbox-gl-native/include/mbgl/style/conversion/rotation.hpp new file mode 100644 index 000000000..86d67240f --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/rotation.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { + optional operator()(const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/source.hpp b/mapbox-gl-native/include/mbgl/style/conversion/source.hpp new file mode 100644 index 000000000..19bc1ce6b --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/source.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter> { +public: + optional> operator()(const Convertible& value, Error& error, const std::string& id) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/tileset.hpp b/mapbox-gl-native/include/mbgl/style/conversion/tileset.hpp new file mode 100644 index 000000000..88661c358 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/tileset.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { +public: + optional operator()(const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion/transition_options.hpp b/mapbox-gl-native/include/mbgl/style/conversion/transition_options.hpp new file mode 100644 index 000000000..a72d757d3 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion/transition_options.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter { +public: + optional operator()(const Convertible& value, Error& error) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/conversion_impl.hpp b/mapbox-gl-native/include/mbgl/style/conversion_impl.hpp new file mode 100644 index 000000000..11d3bae9c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/conversion_impl.hpp @@ -0,0 +1,384 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +/* + The `conversion` namespace defines conversions from JSON structures conforming to the schema defined by + the Mapbox Style Specification, to the various C++ types that form the C++ model of that domain: + + * `std::unique_ptr` + * `std::unique_ptr` + * `Filter` + * `PropertyValue` + + A single template function serves as the public interface: + + template + optional convert(const Convertible& input, Error& error); + + Where `T` is one of the above types. If the conversion fails, the result is empty, and the + error parameter includes diagnostic text suitable for presentation to a library user. Otherwise, + a filled optional is returned. + + `Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that + can serve as input to the conversion algorithm. For instance, on macOS, we need to support + conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc. + On Qt, we need to support conversion from RapidJSON types and QVariant. + + We don't want to use traditional forms of polymorphism to accomplish this: + + * Compile time polymorphism using a template parameter for the actual value type leads to + excessive code bloat and long compile times. + * Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous + use of std::unique_ptr, unsuitable for this performance-sensitive code. + + Therefore, we're using a custom implementation of runtime polymorphism where we manually create and + dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible + underlying types inline on the stack, using `std::aligned_storage`. + + For a given underlying type T, an explicit specialization of `ConversionTraits` must be provided. This + specialization must provide the following static methods: + + * `isUndefined(v)` -- returns a boolean indication whether `v` is undefined or a JSON null + + * `isArray(v)` -- returns a boolean indicating whether `v` represents a JSON array + * `arrayLength(v)` -- called only if `isArray(v)`; returns a size_t length + * `arrayMember(v)` -- called only if `isArray(v)`; returns `V` or `V&` + + * `isObject(v)` -- returns a boolean indicating whether `v` represents a JSON object + * `objectMember(v, name)` -- called only if `isObject(v)`; `name` is `const char *`; return value: + * is true when evaluated in a boolean context iff the named member exists + * is convertable to a `V` or `V&` when dereferenced + * `eachMember(v, [] (const std::string&, const V&) -> optional {...})` -- called + only if `isObject(v)`; calls the provided lambda once for each key and value of the object; + short-circuits if any call returns an `Error` + + * `toBool(v)` -- returns `optional`, absence indicating `v` is not a JSON boolean + * `toNumber(v)` -- returns `optional`, absence indicating `v` is not a JSON number + * `toDouble(v)` -- returns `optional`, absence indicating `v` is not a JSON number + * `toString(v)` -- returns `optional`, absence indicating `v` is not a JSON string + * `toValue(v)` -- returns `optional`, a variant type, for generic conversion, + absence indicating `v` is not a boolean, number, or string. Numbers should be converted to + unsigned integer, signed integer, or floating point, in descending preference. + + In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for + `std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the + possible underlying types. (A static assert will fail if this is not the case.) + + `Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state; + you must not do anything with it except let it go out of scope. +*/ + +template +class ConversionTraits; + +class Convertible { +public: + template + // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) + Convertible(T&& value) : vtable(vtableForType>()) { + static_assert(sizeof(Storage) >= sizeof(std::decay_t), "Storage must be large enough to hold value type"); + new (static_cast(&storage)) std::decay_t(std::forward(value)); + } + + Convertible(Convertible&& v) noexcept : vtable(v.vtable) { + // NOLINTNEXTLINE(performance-move-const-arg) + vtable->move(std::move(v.storage), storage); + } + + ~Convertible() { + vtable->destroy(storage); + } + + Convertible& operator=(Convertible&& v) noexcept { + if (this != &v) { + vtable->destroy(storage); + vtable = v.vtable; + // NOLINTNEXTLINE(performance-move-const-arg) + vtable->move(std::move(v.storage), storage); + } + return *this; + } + + Convertible() = delete; + Convertible(const Convertible&) = delete; + Convertible& operator=(const Convertible&) = delete; + + friend inline bool isUndefined(const Convertible& v) { + assert(v.vtable); + return v.vtable->isUndefined(v.storage); + } + + friend inline bool isArray(const Convertible& v) { + assert(v.vtable); + return v.vtable->isArray(v.storage); + } + + friend inline std::size_t arrayLength(const Convertible& v) { + assert(v.vtable); + return v.vtable->arrayLength(v.storage); + } + + friend inline Convertible arrayMember(const Convertible& v, std::size_t i) { + assert(v.vtable); + return v.vtable->arrayMember(v.storage, i); + } + + friend inline bool isObject(const Convertible& v) { + assert(v.vtable); + return v.vtable->isObject(v.storage); + } + + friend inline optional objectMember(const Convertible& v, const char * name) { + assert(v.vtable); + return v.vtable->objectMember(v.storage, name); + } + + friend inline optional eachMember(const Convertible& v, const std::function (const std::string&, const Convertible&)>& fn) { + assert(v.vtable); + return v.vtable->eachMember(v.storage, fn); + } + + friend inline optional toBool(const Convertible& v) { + assert(v.vtable); + return v.vtable->toBool(v.storage); + } + + friend inline optional toNumber(const Convertible& v) { + assert(v.vtable); + return v.vtable->toNumber(v.storage); + } + + friend inline optional toDouble(const Convertible& v) { + assert(v.vtable); + return v.vtable->toDouble(v.storage); + } + + friend inline optional toString(const Convertible& v) { + assert(v.vtable); + return v.vtable->toString(v.storage); + } + + friend inline optional toValue(const Convertible& v) { + assert(v.vtable); + return v.vtable->toValue(v.storage); + } + + friend inline optional toGeoJSON(const Convertible& v, Error& error) { + assert(v.vtable); + return v.vtable->toGeoJSON(v.storage, error); + } + +private: +#if __ANDROID__ + // Android: JSValue* or mbgl::android::Value + using Storage = std::aligned_storage_t<32, 8>; +#elif __QT__ + // Qt: JSValue* or QVariant + using Storage = std::aligned_storage_t<32, 8>; +#else + // Node: JSValue* or v8::Local + // iOS/macOS: JSValue* or id + using Storage = std::aligned_storage_t<8, 8>; +#endif + + struct VTable { + void (*move) (Storage&& src, Storage& dest); + void (*destroy) (Storage&); + + bool (*isUndefined) (const Storage&); + + bool (*isArray) (const Storage&); + std::size_t (*arrayLength) (const Storage&); + Convertible (*arrayMember) (const Storage&, std::size_t); + + bool (*isObject) (const Storage&); + optional (*objectMember) (const Storage&, const char *); + optional (*eachMember) (const Storage&, const std::function (const std::string&, const Convertible&)>&); + + optional (*toBool) (const Storage&); + optional (*toNumber) (const Storage&); + optional (*toDouble) (const Storage&); + optional (*toString) (const Storage&); + optional (*toValue) (const Storage&); + + // https://github.com/mapbox/mapbox-gl-native/issues/5623 + optional (*toGeoJSON) (const Storage&, Error&); + }; + + // Extracted this function from the table below to work around a GCC bug with differing + // visibility settings for capturing lambdas: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947 + template + static auto vtableEachMember(const Storage& s, const std::function(const std::string&, const Convertible&)>& fn) { + return ConversionTraits::eachMember(reinterpret_cast(s), [&](const std::string& k, T&& v) { + return fn(k, Convertible(std::move(v))); + }); + } + + template + static VTable* vtableForType() { + using Traits = ConversionTraits; + static VTable vtable = { + [] (Storage&& src, Storage& dest) { + new (static_cast(&dest)) T(reinterpret_cast(src)); + }, + [] (Storage& s) { + reinterpret_cast(s).~T(); + }, + [] (const Storage& s) { + return Traits::isUndefined(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::isArray(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::arrayLength(reinterpret_cast(s)); + }, + [] (const Storage& s, std::size_t i) { + return Convertible(Traits::arrayMember(reinterpret_cast(s), i)); + }, + [] (const Storage& s) { + return Traits::isObject(reinterpret_cast(s)); + }, + [] (const Storage& s, const char * key) { + optional member = Traits::objectMember(reinterpret_cast(s), key); + if (member) { + return optional(Convertible(std::move(*member))); + } else { + return optional(); + } + }, + vtableEachMember, + [] (const Storage& s) { + return Traits::toBool(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::toNumber(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::toDouble(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::toString(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::toValue(reinterpret_cast(s)); + }, + [] (const Storage& s, Error& err) { + return Traits::toGeoJSON(reinterpret_cast(s), err); + } + }; + return &vtable; + } + + VTable* vtable; + Storage storage; +}; + +template +optional convert(const Convertible& value, Error& error, Args&&...args) { + return Converter()(value, error, std::forward(args)...); +} + +template <> +struct ValueFactory { + static Value make(const ColorRampPropertyValue& value) { return value.getExpression().serialize(); } +}; + +template <> +struct ValueFactory { + static Value make(const TransitionOptions& value) { return value.serialize(); } +}; + +template <> +struct ValueFactory { + static Value make(const Color& color) { return color.serialize(); } +}; + +template +struct ValueFactory::value && !is_linear_container::value)>::type> { + static Value make(const T& arg) { return {arg}; } +}; + +template +struct ValueFactory::value>::type> { + static Value make(T arg) { return {Enum::toString(arg)}; } +}; + +template +struct ValueFactory::value>::type> { + static Value make(const T& arg) { + mapbox::base::ValueArray result; + result.reserve(arg.size()); + for (const auto& item : arg) { + result.emplace_back(ValueFactory>::make(item)); + } + return result; + } +}; + +template <> +struct ValueFactory { + static Value make(const Position& position) { + return ValueFactory>::make(position.getSpherical()); + } +}; + +template <> +struct ValueFactory { + static Value make(const Rotation& rotation) { return {rotation.getAngle()}; } +}; + +template +Value makeValue(T&& arg) { + return ValueFactory>::make(std::forward(arg)); +} + +template +StyleProperty makeStyleProperty(const PropertyValue& value) { + return value.match([](const Undefined&) -> StyleProperty { return {}; }, + [](const Color& c) -> StyleProperty { + return {makeValue(c), StyleProperty::Kind::Expression}; + }, + [](const PropertyExpression& fn) -> StyleProperty { + return {fn.getExpression().serialize(), StyleProperty::Kind::Expression}; + }, + [](const auto& t) -> StyleProperty { + return {makeValue(t), StyleProperty::Kind::Constant}; + }); +} + +inline StyleProperty makeStyleProperty(const TransitionOptions& value) { + if (!value.isDefined()) return {}; + return {makeValue(value), StyleProperty::Kind::Transition}; +} + +inline StyleProperty makeStyleProperty(const ColorRampPropertyValue& value) { + if (value.isUndefined()) return {}; + return {makeValue(value), StyleProperty::Kind::Expression}; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/assertion.hpp b/mapbox-gl-native/include/mbgl/style/expression/assertion.hpp new file mode 100644 index 000000000..0dec89dac --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/assertion.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Assertion : public Expression { +public: + Assertion(type::Type type_, std::vector> inputs_); + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function& visit) const override; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override; + + mbgl::Value serialize() const override; + std::string getOperator() const override; + +private: + std::vector> inputs; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/at.hpp b/mapbox-gl-native/include/mbgl/style/expression/at.hpp new file mode 100644 index 000000000..f0dbccb79 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/at.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class At : public Expression { +public: + At(std::unique_ptr index_, std::unique_ptr input_) : + Expression(Kind::At, input_->getType().get().itemType), + index(std::move(index_)), + input(std::move(input_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function&) const override; + + bool operator==(const Expression& e) const override { + if (e.getKind() == Kind::At) { + auto rhs = static_cast(&e); + return *index == *(rhs->index) && *input == *(rhs->input); + } + return false; + } + + std::vector> possibleOutputs() const override { + return { nullopt }; + } + + std::string getOperator() const override { return "at"; } + +private: + std::unique_ptr index; + std::unique_ptr input; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/boolean_operator.hpp b/mapbox-gl-native/include/mbgl/style/expression/boolean_operator.hpp new file mode 100644 index 000000000..d25474751 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/boolean_operator.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Any : public Expression { +public: + Any(std::vector> inputs_) : + Expression(Kind::Any, type::Boolean), + inputs(std::move(inputs_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function& visit) const override; + bool operator==(const Expression& e) const override; + std::vector> possibleOutputs() const override; + + std::string getOperator() const override { return "any"; } +private: + std::vector> inputs; +}; + +class All : public Expression { +public: + All(std::vector> inputs_) : + Expression(Kind::All, type::Boolean), + inputs(std::move(inputs_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function& visit) const override; + bool operator==(const Expression& e) const override; + std::vector> possibleOutputs() const override; + + std::string getOperator() const override { return "all"; } +private: + std::vector> inputs; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/case.hpp b/mapbox-gl-native/include/mbgl/style/expression/case.hpp new file mode 100644 index 000000000..4a254dc35 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/case.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Case : public Expression { +public: + using Branch = std::pair, std::unique_ptr>; + + Case(type::Type type_, std::vector branches_, std::unique_ptr otherwise_) + : Expression(Kind::Case, std::move(type_)), branches(std::move(branches_)), otherwise(std::move(otherwise_)) {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function& visit) const override; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override; + + std::string getOperator() const override { return "case"; } +private: + std::vector branches; + std::unique_ptr otherwise; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/check_subtype.hpp b/mapbox-gl-native/include/mbgl/style/expression/check_subtype.hpp new file mode 100644 index 000000000..90e5169de --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/check_subtype.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { +namespace type { + +optional checkSubtype(const Type& expected, const Type& t); + +} // namespace type +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/coalesce.hpp b/mapbox-gl-native/include/mbgl/style/expression/coalesce.hpp new file mode 100644 index 000000000..c4216f234 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/coalesce.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Coalesce : public Expression { +public: + using Args = std::vector>; + Coalesce(const type::Type& type_, Args args_) : + Expression(Kind::Coalesce, type_), + args(std::move(args_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + + EvaluationResult evaluate(const EvaluationContext& params) const override; + + void eachChild(const std::function& visit) const override; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override; + + std::size_t getLength() const { + return args.size(); + } + + Expression* getChild(std::size_t i) const { + return args.at(i).get(); + } + + std::string getOperator() const override { return "coalesce"; } +private: + Args args; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/coercion.hpp b/mapbox-gl-native/include/mbgl/style/expression/coercion.hpp new file mode 100644 index 000000000..28c2c0c67 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/coercion.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Coercion : public Expression { +public: + Coercion(type::Type type_, std::vector> inputs_); + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function& visit) const override; + + mbgl::Value serialize() const override; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override; + + std::string getOperator() const override; +private: + EvaluationResult (*coerceSingleValue) (const Value& v); + std::vector> inputs; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl + diff --git a/mapbox-gl-native/include/mbgl/style/expression/collator.hpp b/mapbox-gl-native/include/mbgl/style/expression/collator.hpp new file mode 100644 index 000000000..95a072618 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/collator.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Collator { +public: + Collator(bool caseSensitive, bool diacriticSensitive, const optional& locale = {}); + + bool operator==(const Collator& other) const; + + int compare(const std::string& lhs, const std::string& rhs) const; + + std::string resolvedLocale() const; +private: + platform::Collator collator; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/collator_expression.hpp b/mapbox-gl-native/include/mbgl/style/expression/collator_expression.hpp new file mode 100644 index 000000000..2551cd19c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/collator_expression.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace expression { + +class CollatorExpression : public Expression { +public: + CollatorExpression(std::unique_ptr caseSensitive, + std::unique_ptr diacriticSensitive, + optional> locale); + + EvaluationResult evaluate(const EvaluationContext&) const override; + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + void eachChild(const std::function&) const override; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override { + // Technically the set of possible outputs is the combinatoric set of Collators produced + // by all possibleOutputs of locale/caseSensitive/diacriticSensitive + // But for the primary use of Collators in comparison operators, we ignore the Collator's + // possibleOutputs anyway, so we can get away with leaving this undefined for now. + return { nullopt }; + } + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "collator"; } +private: + std::unique_ptr caseSensitive; + std::unique_ptr diacriticSensitive; + optional> locale; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/comparison.hpp b/mapbox-gl-native/include/mbgl/style/expression/comparison.hpp new file mode 100644 index 000000000..df1fbcab9 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/comparison.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace expression { + +ParseResult parseComparison(const mbgl::style::conversion::Convertible&, ParsingContext&); + +class BasicComparison : public Expression { +public: + using CompareFunctionType = bool (*)(const Value&, const Value&); + + BasicComparison( + std::string op, + std::unique_ptr lhs, + std::unique_ptr rhs); + + void eachChild(const std::function& visit) const override; + bool operator==(const Expression&) const override; + EvaluationResult evaluate(const EvaluationContext&) const override; + std::vector> possibleOutputs() const override; + std::string getOperator() const override; + +private: + std::string op; + CompareFunctionType compare; + std::unique_ptr lhs; + std::unique_ptr rhs; + bool needsRuntimeTypeCheck; +}; + +class CollatorComparison : public Expression { +public: + using CompareFunctionType = bool (*)(const std::string&, const std::string&, const Collator&); + + CollatorComparison( + std::string op, + std::unique_ptr lhs, + std::unique_ptr rhs, + std::unique_ptr collator); + + void eachChild(const std::function& visit) const override; + bool operator==(const Expression&) const override; + EvaluationResult evaluate(const EvaluationContext&) const override; + std::vector> possibleOutputs() const override; + std::string getOperator() const override; + +private: + std::string op; + CompareFunctionType compare; + std::unique_ptr lhs; + std::unique_ptr rhs; + std::unique_ptr collator; + bool needsRuntimeTypeCheck; +}; + + + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/compound_expression.hpp b/mapbox-gl-native/include/mbgl/style/expression/compound_expression.hpp new file mode 100644 index 000000000..c6c66c3fc --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/compound_expression.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +namespace detail { +struct SignatureBase; +} // namespace detail + +/* + CompoundExpression provides a mechanism for implementing an expression + simply by providing a list of pure functions of the form + (const T0& arg0, const T1& arg1, ...) -> Result where T0, T1, ..., U are + member types of mbgl::style::expression::Value. + + The majority of expressions specified in the style-spec are implemented in + this fashion (see compound_expression.cpp). +*/ +class CompoundExpression : public Expression { +public: + CompoundExpression(const detail::SignatureBase&, std::vector>); + + std::string getOperator() const override; + EvaluationResult evaluate(const EvaluationContext& evaluationParams) const override; + std::vector> possibleOutputs() const override; + void eachChild(const std::function& visit) const override; + bool operator==(const Expression& e) const override; + + optional getParameterCount() const; + + static bool exists(const std::string& name); + +protected: + const detail::SignatureBase& signature; + std::vector> args; +}; + +ParseResult parseCompoundExpression(const std::string& name, + const mbgl::style::conversion::Convertible& value, + ParsingContext& ctx); + +ParseResult createCompoundExpression(const std::string& name, + std::vector> args, + ParsingContext& ctx); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/distance.hpp b/mapbox-gl-native/include/mbgl/style/expression/distance.hpp new file mode 100644 index 000000000..481162be0 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/distance.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Distance final : public Expression { +public: + Distance(GeoJSON geoJSONSource_, Feature::geometry_type geometries_); + + ~Distance() override; + + EvaluationResult evaluate(const EvaluationContext&) const override; + + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + void eachChild(const std::function&) const override {} + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override; + + mbgl::Value serialize() const override; + std::string getOperator() const override; + +private: + GeoJSON geoJSONSource; + Feature::geometry_type geometries; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/dsl.hpp b/mapbox-gl-native/include/mbgl/style/expression/dsl.hpp new file mode 100644 index 000000000..6bb1d22d6 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/dsl.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { +namespace dsl { + +// This convenience API does little to no expression validation or type-checking, and is intended for +// use only by test and other non-production code. + +template +std::vector> vec(Args... args) { + std::vector> result; + util::ignore({ (result.push_back(std::move(args)), 0)... }); + return result; +} + +std::unique_ptr createExpression(const char* expr); +std::unique_ptr createExpression(const mbgl::style::conversion::Convertible& expr); +std::unique_ptr error(std::string); + +std::unique_ptr literal(const char* value); +std::unique_ptr literal(const Value& value); +std::unique_ptr literal(std::initializer_list value); +std::unique_ptr literal(std::initializer_list value); + +std::unique_ptr assertion(const type::Type&, + std::unique_ptr, + std::unique_ptr def = nullptr); +std::unique_ptr number(std::unique_ptr, + std::unique_ptr def = nullptr); +std::unique_ptr string(std::unique_ptr, + std::unique_ptr def = nullptr); +std::unique_ptr boolean(std::unique_ptr, + std::unique_ptr def = nullptr); + +std::unique_ptr toColor(std::unique_ptr, + std::unique_ptr def = nullptr); +std::unique_ptr toString(std::unique_ptr, + std::unique_ptr def = nullptr); +std::unique_ptr toFormatted(std::unique_ptr, + std::unique_ptr def = nullptr); +std::unique_ptr toImage(std::unique_ptr, std::unique_ptr def = nullptr); + +std::unique_ptr get(const char* value); +std::unique_ptr get(std::unique_ptr); + +std::unique_ptr id(); +std::unique_ptr zoom(); + +std::unique_ptr eq(std::unique_ptr, std::unique_ptr); +std::unique_ptr ne(std::unique_ptr, std::unique_ptr); +std::unique_ptr gt(std::unique_ptr, std::unique_ptr); +std::unique_ptr lt(std::unique_ptr, std::unique_ptr); + +std::unique_ptr step(std::unique_ptr input, + std::unique_ptr output0, + double input1, std::unique_ptr output1); + +Interpolator linear(); +Interpolator exponential(double base); +Interpolator cubicBezier(double x1, double y1, double x2, double y2); + +std::unique_ptr interpolate(Interpolator interpolator, + std::unique_ptr input, + double input1, std::unique_ptr output1); + +std::unique_ptr interpolate(Interpolator interpolator, + std::unique_ptr input, + double input1, std::unique_ptr output1, + double input2, std::unique_ptr output2); + +std::unique_ptr interpolate(Interpolator interpolator, + std::unique_ptr input, + double input1, std::unique_ptr output1, + double input2, std::unique_ptr output2, + double input3, std::unique_ptr output3); + +std::unique_ptr concat(std::vector> inputs); + +std::unique_ptr format(const char* value); +std::unique_ptr format(std::unique_ptr); + +std::unique_ptr image(const char* value); +std::unique_ptr image(std::unique_ptr); + +} // namespace dsl +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/error.hpp b/mapbox-gl-native/include/mbgl/style/expression/error.hpp new file mode 100644 index 000000000..9e1da2f0f --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/error.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Error : public Expression { +public: + Error(std::string message_) + : Expression(Kind::Error, type::Error), + message(std::move(message_)) {} + + void eachChild(const std::function&) const override {} + + bool operator==(const Expression& e) const override { + return e.getKind() == Kind::Error; + } + + EvaluationResult evaluate(const EvaluationContext&) const override { + return EvaluationError{message}; + } + + std::vector> possibleOutputs() const override { + return {}; + } + + std::string getOperator() const override { return "error"; } + +private: + std::string message; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/expression.hpp b/mapbox-gl-native/include/mbgl/style/expression/expression.hpp new file mode 100644 index 000000000..51cc3b0f1 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/expression.hpp @@ -0,0 +1,262 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +class GeometryTileFeature; + +namespace style { +namespace expression { + +class EvaluationError { +public: + std::string message; +}; + +class EvaluationContext { +public: + EvaluationContext() = default; + explicit EvaluationContext(float zoom_) : zoom(zoom_) {} + explicit EvaluationContext(GeometryTileFeature const * feature_) : feature(feature_) {} + EvaluationContext(float zoom_, GeometryTileFeature const* feature_) : zoom(zoom_), feature(feature_) {} + EvaluationContext(optional accumulated_, GeometryTileFeature const * feature_) : + accumulated(std::move(accumulated_)), feature(feature_) + {} + EvaluationContext(float zoom_, GeometryTileFeature const* feature_, const FeatureState* state_) + : zoom(zoom_), feature(feature_), featureState(state_) {} + EvaluationContext(optional zoom_, GeometryTileFeature const * feature_, optional colorRampParameter_) : + zoom(std::move(zoom_)), feature(feature_), colorRampParameter(std::move(colorRampParameter_)) + {} + + EvaluationContext& withFormattedSection(const Value* formattedSection_) noexcept { + formattedSection = formattedSection_; + return *this; + }; + + EvaluationContext& withFeatureState(const FeatureState* featureState_) noexcept { + featureState = featureState_; + return *this; + }; + + EvaluationContext& withAvailableImages(const std::set* availableImages_) noexcept { + availableImages = availableImages_; + return *this; + }; + + EvaluationContext& withCanonicalTileID(const mbgl::CanonicalTileID* canonical_) noexcept { + canonical = canonical_; + return *this; + }; + + optional zoom; + optional accumulated; + GeometryTileFeature const* feature = nullptr; + optional colorRampParameter; + // Contains formatted section object, std::unordered_map. + const Value* formattedSection = nullptr; + const FeatureState* featureState = nullptr; + const std::set* availableImages = nullptr; + const mbgl::CanonicalTileID* canonical = nullptr; +}; + +template +class Result : private variant { +public: + using variant::variant; + using Value = T; + + Result() = default; + + explicit operator bool () const { + return this->template is(); + } + + // optional does some type trait magic for this one, so this might + // be problematic as is. + const T* operator->() const { + assert(this->template is()); + return std::addressof(this->template get()); + } + + T* operator->() { + assert(this->template is()); + return std::addressof(this->template get()); + } + + T& operator*() { + assert(this->template is()); + return this->template get(); + } + + const T& operator*() const { + assert(this->template is()); + return this->template get(); + } + + const EvaluationError& error() const { + assert(this->template is()); + return this->template get(); + } +}; + +class EvaluationResult : public Result { +public: + using Result::Result; // NOLINT + + EvaluationResult() = default; + + EvaluationResult(const std::array& arr) : + Result(toExpressionValue(arr)) + {} + + // used only for the special (private) "error" expression + EvaluationResult(const type::ErrorType&) { + assert(false); + } +}; + +/* + Expression is an abstract class that serves as an interface and base class + for particular expression implementations. + + CompoundExpression implements the majority of expressions in the spec by + inferring the argument and output from a simple function (const T0& arg0, + const T1& arg1, ...) -> Result where T0, T1, ..., U are member types of + mbgl::style::expression::Value. + + The other Expression subclasses (Let, Curve, Match, etc.) exist in order to + implement expressions that need specialized parsing, type checking, or + evaluation logic that can't be handled by CompoundExpression's inference + mechanism. + + Each Expression subclass also provides a static + ParseResult ExpressionClass::parse(const V&, ParsingContext), + which handles parsing a style-spec JSON representation of the expression. +*/ + +enum class Kind : int32_t { + Coalesce, + CompoundExpression, + Literal, + At, + Interpolate, + Assertion, + Length, + Step, + Let, + Var, + CollatorExpression, + Coercion, + Match, + Error, + Case, + Any, + All, + Comparison, + FormatExpression, + FormatSectionOverride, + NumberFormat, + ImageExpression, + In, + Within, + Distance +}; + +class Expression { +public: + Expression(Kind kind_, type::Type type_) : kind(kind_), type(std::move(type_)) {} + virtual ~Expression() = default; + + virtual EvaluationResult evaluate(const EvaluationContext& params) const = 0; + virtual void eachChild(const std::function&) const = 0; + virtual bool operator==(const Expression&) const = 0; + bool operator!=(const Expression& rhs) const { + return !operator==(rhs); + } + + Kind getKind() const { return kind; }; + type::Type getType() const { return type; }; + + EvaluationResult evaluate(optional zoom, const Feature& feature, optional colorRampParameter) const; + EvaluationResult evaluate(optional zoom, + const Feature& feature, + optional colorRampParameter, + const std::set& availableImages) const; + EvaluationResult evaluate(optional zoom, + const Feature& feature, + optional colorRampParameter, + const std::set& availableImages, + const CanonicalTileID& canonical) const; + EvaluationResult evaluate(optional accumulated, const Feature& feature) const; + + /** + * Statically analyze the expression, attempting to enumerate possible outputs. Returns + * an array of values plus the sentinel null optional value, used to indicate that the + * complete set of outputs is statically undecidable. + */ + virtual std::vector> possibleOutputs() const = 0; + + virtual mbgl::Value serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + eachChild([&](const Expression &child) { + serialized.emplace_back(child.serialize()); + }); + return serialized; + }; + + virtual std::string getOperator() const = 0; + +protected: + template + static bool childrenEqual(const T& lhs, const T& rhs) { + if (lhs.size() != rhs.size()) return false; + for (auto leftChild = lhs.begin(), rightChild = rhs.begin(); + leftChild != lhs.end(); + leftChild++, rightChild++) + { + if (!Expression::childEqual(*leftChild, *rightChild)) return false; + } + return true; + } + + static bool childEqual(const std::unique_ptr& lhs, const std::unique_ptr& rhs) { + return *lhs == *rhs; + } + + template + static bool childEqual(const std::pair>& lhs, + const std::pair>& rhs) { + return lhs.first == rhs.first && *(lhs.second) == *(rhs.second); + } + + template + static bool childEqual(const std::pair>& lhs, + const std::pair>& rhs) { + return lhs.first == rhs.first && *(lhs.second) == *(rhs.second); + } + + static bool childEqual(const std::pair, std::unique_ptr>& lhs, + const std::pair, std::unique_ptr>& rhs) { + return *(lhs.first) == *(rhs.first) && *(lhs.second) == *(rhs.second); + } + +private: + Kind kind; + type::Type type; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/find_zoom_curve.hpp b/mapbox-gl-native/include/mbgl/style/expression/find_zoom_curve.hpp new file mode 100644 index 000000000..6f1419a69 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/find_zoom_curve.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +optional> findZoomCurve(const expression::Expression* e); + +variant findZoomCurveChecked(const expression::Expression* e); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/format_expression.hpp b/mapbox-gl-native/include/mbgl/style/expression/format_expression.hpp new file mode 100644 index 000000000..248a3cf6d --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/format_expression.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +struct FormatExpressionSection { + explicit FormatExpressionSection(std::unique_ptr content_); + + void setTextSectionOptions(optional> fontScale_, + optional> textFont_, + optional> textColor_); + + // Content can be expression that evaluates to String or Image. + std::shared_ptr content; + + // Text related section options. + optional> fontScale; + optional> textFont; + optional> textColor; +}; + +class FormatExpression final : public Expression { +public: + explicit FormatExpression(std::vector sections); + + EvaluationResult evaluate(const EvaluationContext&) const override; + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + void eachChild(const std::function&) const override; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override { + // Technically the combinatoric set of all children + // Usually, this.text will be undefined anyway + return { nullopt }; + } + + const std::vector& getSections() const { return sections; } + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "format"; } +private: + std::vector sections; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/format_section_override.hpp b/mapbox-gl-native/include/mbgl/style/expression/format_section_override.hpp new file mode 100644 index 000000000..63382fda5 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/format_section_override.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +template +class FormatSectionOverride final : public Expression { +public: + FormatSectionOverride(const type::Type& type_, + PossiblyEvaluatedPropertyValue defaultValue_, + std::string propertyName_) : + Expression(Kind::FormatSectionOverride, type_), + defaultValue(std::move(defaultValue_)), + propertyName(std::move(propertyName_)) + {} + + EvaluationResult evaluate(const EvaluationContext& context) const final { + using Object = std::unordered_map; + if (context.formattedSection && + context.formattedSection->is()) { + const auto& section = context.formattedSection->get(); + if (section.find(propertyName) != section.end()) { + return section.at(propertyName); + } + } + + return defaultValue.match( + [&context] (const style::PropertyExpression& e) { return e.getExpression().evaluate(context); }, + [] (const T& t) -> EvaluationResult { return t; } + ); + } + + void eachChild(const std::function& fn) const final { + defaultValue.match([&fn] (const style::PropertyExpression& e) { fn(e.getExpression()); }, + [] (const T&) {}); + } + + bool operator==(const Expression& e) const final { + if (e.getKind() == Kind::FormatSectionOverride) { + const auto* other = static_cast(&e); + + if (getType() != other->getType() || propertyName != other->propertyName) { + return false; + } + + // Check that default values or property expressions are equal. + return defaultValue.match( + [other] (const style::PropertyExpression& thisExpr) { + return other->defaultValue.match([&thisExpr] (const style::PropertyExpression& otherExpr) { + return thisExpr == otherExpr; + }, + [] (const T&) { + return false; + }); + }, + [other] (const T& thisValue) { + return other->defaultValue.match([&thisValue] (const T& otherValue) { + return thisValue == otherValue; + }, + [] (const style::PropertyExpression&) { + return false; + }); + }); + } + + return false; + } + + std::vector> possibleOutputs() const final { + return {nullopt}; + } + + std::string getOperator() const final { return "format-section-override"; } + +private: + PossiblyEvaluatedPropertyValue defaultValue; + std::string propertyName; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/formatted.hpp b/mapbox-gl-native/include/mbgl/style/expression/formatted.hpp new file mode 100644 index 000000000..d089e12d9 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/formatted.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +extern const char* const kFormattedSectionFontScale; +extern const char* const kFormattedSectionTextFont; +extern const char* const kFormattedSectionTextColor; + +struct FormattedSection { + explicit FormattedSection(std::string text_, + optional fontScale_, + optional fontStack_, + optional textColor_) + : text(std::move(text_)), + fontScale(std::move(fontScale_)), + fontStack(std::move(fontStack_)), + textColor(std::move(textColor_)) {} + + explicit FormattedSection(Image image_) : image(std::move(image_)) {} + + std::string text; + optional image; + optional fontScale; + optional fontStack; + optional textColor; +}; + +class Formatted { +public: + Formatted() = default; + + Formatted(const char* plainU8String) { + sections.emplace_back(std::string(plainU8String), nullopt, nullopt, nullopt); + } + + Formatted(std::vector sections_) + : sections(std::move(sections_)) + {} + + bool operator==(const Formatted& ) const; + + std::string toString() const; + mbgl::Value toObject() const; + + bool empty() const; + + std::vector sections; +}; + +} // namespace expression + +namespace conversion { + +template <> +struct Converter { +public: + optional operator()(const Convertible& value, Error& error) const; +}; + +template <> +struct ValueFactory { + static Value make(const expression::Formatted& formatted) { return formatted.toObject(); } +}; + +} // namespace conversion + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/get_covering_stops.hpp b/mapbox-gl-native/include/mbgl/style/expression/get_covering_stops.hpp new file mode 100644 index 000000000..c9de9d196 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/get_covering_stops.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +// Return the smallest range of stops that covers the interval [lower, upper] +Range getCoveringStops(const std::map>& stops, double lower, double upper); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/image.hpp b/mapbox-gl-native/include/mbgl/style/expression/image.hpp new file mode 100644 index 000000000..0bc4794a2 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/image.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Image { +public: + Image() = default; + Image(const char* imageID); + Image(std::string imageID); + explicit Image(std::string imageID, bool available); + bool operator==(const Image&) const; + mbgl::Value toValue() const; + const std::string& id() const; + bool isAvailable() const; + bool empty() const; + +private: + std::string imageID; + bool available; +}; + +} // namespace expression + +namespace conversion { + +template <> +struct Converter { +public: + optional operator()(const Convertible& value, Error& error) const; +}; + +template <> +struct ValueFactory { + static Value make(const expression::Image& image) { return image.toValue(); } +}; + +} // namespace conversion + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/image_expression.hpp b/mapbox-gl-native/include/mbgl/style/expression/image_expression.hpp new file mode 100644 index 000000000..cd3d37528 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/image_expression.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace mbgl { +namespace style { +namespace expression { + +class ParsingContext; + +class ImageExpression final : public Expression { +public: + explicit ImageExpression(std::unique_ptr imageID); + + EvaluationResult evaluate(const EvaluationContext&) const override; + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + void eachChild(const std::function&) const override; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override { return {nullopt}; } + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "image"; } + +private: + std::shared_ptr imageID; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/in.hpp b/mapbox-gl-native/include/mbgl/style/expression/in.hpp new file mode 100644 index 000000000..a7de17c94 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/in.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class In final : public Expression { +public: + In(std::unique_ptr needle_, std::unique_ptr haystack_); + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function&) const override; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override; + + std::string getOperator() const override; + +private: + std::unique_ptr needle; + std::unique_ptr haystack; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/interpolate.hpp b/mapbox-gl-native/include/mbgl/style/expression/interpolate.hpp new file mode 100644 index 000000000..0e7850471 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/interpolate.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +ParseResult parseInterpolate(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + +class Interpolate : public Expression { +public: + Interpolate(const type::Type& type_, + Interpolator interpolator_, + std::unique_ptr input_, + std::map> stops_); + + const std::unique_ptr& getInput() const { return input; } + const Interpolator& getInterpolator() const { return interpolator; } + + void eachChild(const std::function& visit) const override { + visit(*input); + for (const auto& stop : stops) { + visit(*stop.second); + } + } + + void eachStop(const std::function& visit) const { + for (const auto& stop : stops) { + visit(stop.first, *stop.second); + } + } + + // Return the smallest range of stops that covers the interval [lower, upper] + Range getCoveringStops(const double lower, const double upper) const { + return ::mbgl::style::expression::getCoveringStops(stops, lower, upper); + } + + double interpolationFactor(const Range& inputLevels, const double inputValue) const { + return interpolator.match( + [&](const auto& interp) { return interp.interpolationFactor(inputLevels, inputValue); } + ); + } + + bool operator==(const Expression& e) const override { + if (e.getKind() == Kind::Interpolate) { + auto rhs = static_cast(&e); + if (interpolator != rhs->interpolator || + *input != *(rhs->input) || + stops.size() != rhs->stops.size()) + { + return false; + } + + return Expression::childrenEqual(stops, rhs->stops); + } + return false; + } + + std::vector> possibleOutputs() const override; + mbgl::Value serialize() const override; + std::string getOperator() const override { return "interpolate"; } + +protected: + const Interpolator interpolator; + const std::unique_ptr input; + const std::map> stops; +}; + +ParseResult createInterpolate(type::Type type, + Interpolator interpolator, + std::unique_ptr input, + std::map> stops, + ParsingContext& ctx); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/interpolator.hpp b/mapbox-gl-native/include/mbgl/style/expression/interpolator.hpp new file mode 100644 index 000000000..62b48c175 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/interpolator.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class ExponentialInterpolator { +public: + ExponentialInterpolator(double base_) : base(base_) {} + + double base; + + double interpolationFactor(const Range& inputLevels, const double input) const { + return util::interpolationFactor(base, + Range { + static_cast(inputLevels.min), + static_cast(inputLevels.max) + }, + input); + } + + bool operator==(const ExponentialInterpolator& rhs) const { + return base == rhs.base; + } +}; + +class CubicBezierInterpolator { +public: + CubicBezierInterpolator(double x1_, double y1_, double x2_, double y2_) : ub(x1_, y1_, x2_, y2_) {} + + double interpolationFactor(const Range& inputLevels, const double input) const { + return ub.solve(util::interpolationFactor(1.0, + Range { + static_cast(inputLevels.min), + static_cast(inputLevels.max) + }, + input), + 1e-6); + } + + bool operator==(const CubicBezierInterpolator& rhs) const { + return ub == rhs.ub; + } + + util::UnitBezier ub; +}; + +using Interpolator = variant; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/is_constant.hpp b/mapbox-gl-native/include/mbgl/style/expression/is_constant.hpp new file mode 100644 index 000000000..a9c41e48b --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/is_constant.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +template +bool isGlobalPropertyConstant(const Expression& expression, const T& properties) { + if (expression.getKind() == Kind::CompoundExpression) { + auto e = static_cast(&expression); + for (const std::string& property : properties) { + if (e->getOperator() == property) { + return false; + } + } + } + + bool isConstant = true; + expression.eachChild([&](const Expression& e) { + if (isConstant && !isGlobalPropertyConstant(e, properties)) { + isConstant = false; + } + }); + return isConstant; +} + +bool isFeatureConstant(const Expression& expression); +bool isZoomConstant(const Expression& e); + +// Returns true if expression does not depend on information provided by the runtime. +bool isRuntimeConstant(const Expression& e); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/is_expression.hpp b/mapbox-gl-native/include/mbgl/style/expression/is_expression.hpp new file mode 100644 index 000000000..77c489619 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/is_expression.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace mbgl { +namespace style { +namespace expression { + +bool isExpression(const conversion::Convertible& value); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/length.hpp b/mapbox-gl-native/include/mbgl/style/expression/length.hpp new file mode 100644 index 000000000..1d754f193 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/length.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Length : public Expression { +public: + Length(std::unique_ptr input); + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function& visit) const override; + bool operator==(const Expression& e) const override; + std::vector> possibleOutputs() const override; + std::string getOperator() const override { return "length"; } + +private: + std::unique_ptr input; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/let.hpp b/mapbox-gl-native/include/mbgl/style/expression/let.hpp new file mode 100644 index 000000000..a03c4b7d7 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/let.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Let : public Expression { +public: + using Bindings = std::map>; + + Let(Bindings bindings_, std::unique_ptr result_) : + Expression(Kind::Let, result_->getType()), + bindings(std::move(bindings_)), + result(std::move(result_)) + {} + + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function&) const override; + + bool operator==(const Expression& e) const override { + if (e.getKind() == Kind::Let) { + auto rhs = static_cast(&e); + return *result == *(rhs->result); + } + return false; + } + + std::vector> possibleOutputs() const override; + + Expression* getResult() const { + return result.get(); + } + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "let"; } +private: + Bindings bindings; + std::unique_ptr result; +}; + +class Var : public Expression { +public: + Var(std::string name_, std::shared_ptr value_) + : Expression(Kind::Var, value_->getType()), name(std::move(name_)), value(std::move(value_)) {} + + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function&) const override; + + bool operator==(const Expression& e) const override { + if (e.getKind() == Kind::Var) { + auto rhs = static_cast(&e); + return *value == *(rhs->value); + } + return false; + } + + std::vector> possibleOutputs() const override; + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "var"; } + + const std::shared_ptr& getBoundExpression() const { return value; } + +private: + std::string name; + std::shared_ptr value; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/literal.hpp b/mapbox-gl-native/include/mbgl/style/expression/literal.hpp new file mode 100644 index 000000000..e8f0a5d9e --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/literal.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Literal : public Expression { +public: + Literal(const Value& value_) : Expression(Kind::Literal, typeOf(value_)), value(value_) {} + + Literal(const type::Array& type_, std::vector value_) + : Expression(Kind::Literal, type_), value(std::move(value_)) {} + + EvaluationResult evaluate(const EvaluationContext&) const override { + return value; + } + + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + void eachChild(const std::function&) const override {} + + bool operator==(const Expression& e) const override { + if (e.getKind() == Kind::Literal) { + auto rhs = static_cast(&e); + return value == rhs->value; + } + return false; + } + + std::vector> possibleOutputs() const override { + return {{ value }}; + } + + Value getValue() const { + return value; + } + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "literal"; } + +private: + Value value; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/match.hpp b/mapbox-gl-native/include/mbgl/style/expression/match.hpp new file mode 100644 index 000000000..766eec8f5 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/match.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace expression { + +template +class Match : public Expression { +public: + using Branches = std::unordered_map>; + + Match(const type::Type& type_, + std::unique_ptr input_, + Branches branches_, + std::unique_ptr otherwise_) + : Expression(Kind::Match, type_), + input(std::move(input_)), + branches(std::move(branches_)), + otherwise(std::move(otherwise_)) {} + + EvaluationResult evaluate(const EvaluationContext& params) const override; + + void eachChild(const std::function& visit) const override; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override; + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "match"; } +private: + std::unique_ptr input; + Branches branches; + std::unique_ptr otherwise; +}; + +ParseResult parseMatch(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/number_format.hpp b/mapbox-gl-native/include/mbgl/style/expression/number_format.hpp new file mode 100644 index 000000000..288c144f2 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/number_format.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class NumberFormat final : public Expression { +public: + NumberFormat(std::unique_ptr number_, + std::unique_ptr locale_, + std::unique_ptr currency_, + std::unique_ptr minFractionDigits_, + std::unique_ptr maxFractionDigits_); + + ~NumberFormat() override; + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function& visit) const override; + bool operator==(const Expression& e) const override; + std::vector> possibleOutputs() const override; + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "number-format"; } + +private: + std::unique_ptr number; + std::unique_ptr locale; + std::unique_ptr currency; + std::unique_ptr minFractionDigits; + std::unique_ptr maxFractionDigits; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/parsing_context.hpp b/mapbox-gl-native/include/mbgl/style/expression/parsing_context.hpp new file mode 100644 index 000000000..31445271a --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/parsing_context.hpp @@ -0,0 +1,183 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Expression; + +struct ParsingError { + std::string message; + std::string key; + bool operator==(const ParsingError& rhs) const { return message == rhs.message && key == rhs.key; } +}; + +using ParseResult = optional>; + +namespace detail { + +class Scope { +public: + Scope(const std::map>& bindings_, std::shared_ptr parent_ = nullptr) : + bindings(bindings_), + parent(std::move(parent_)) + {} + + const std::map>& bindings; + std::shared_ptr parent; + + optional> get(const std::string& name) { + auto it = bindings.find(name); + if (it != bindings.end()) { + return {it->second}; + } else if (parent) { + return parent->get(name); + } else { + return optional>(); + } + } +}; + +} // namespace detail + +/* + Controls the annotation behavior of the parser when encountering an expression + whose type is not a subtype of the expected type. The default behavior, used + when optional is a nullopt, is as follows: + + When we expect a number, string, boolean, or array but have a value, wrap it in an assertion. + When we expect a color or formatted string, but have a string or value, wrap it in a coercion. + Otherwise, we do static type-checking. + + These behaviors are overridable for: + * The "coalesce" operator, which needs to omit type annotations. + * String-valued properties (e.g. `text-field`), where coercion is more convenient than assertion. +*/ +enum class TypeAnnotationOption { + coerce, + assert, + omit +}; + +class ParsingContext { +public: + ParsingContext() : errors(std::make_shared>()) {} + ParsingContext(std::string key_) : key(std::move(key_)), errors(std::make_shared>()) {} + explicit ParsingContext(type::Type expected_) + : expected(std::move(expected_)), + errors(std::make_shared>()) + {} + ParsingContext(ParsingContext&&) = default; + + ParsingContext(const ParsingContext&) = delete; + ParsingContext& operator=(const ParsingContext&) = delete; + + std::string getKey() const { return key; } + optional getExpected() const { return expected; } + const std::vector& getErrors() const { return *errors; } + std::string getCombinedErrors() const; + + /* + Parse the given style-spec JSON value as an expression. + */ + ParseResult parseExpression(const mbgl::style::conversion::Convertible& value, + const optional& = {}); + + /* + Parse the given style-spec JSON value as an expression intended to be used + in a layout or paint property. This entails checking additional constraints + that exist in that context but not, e.g., for filters. + */ + ParseResult parseLayerPropertyExpression(const mbgl::style::conversion::Convertible& value); + + /* + Parse a child expression. For use by individual Expression::parse() methods. + */ + ParseResult parse(const mbgl::style::conversion::Convertible&, + std::size_t, + optional = {}, + const optional& = {}); + + /* + Parse a child expression. For use by individual Expression::parse() methods. + */ + ParseResult parse(const mbgl::style::conversion::Convertible&, + std::size_t index, + optional, + const std::map>&); + + /* + Check whether `t` is a subtype of `expected`, collecting an error if not. + */ + optional checkType(const type::Type& t); + + optional> getBinding(const std::string& name) { + if (!scope) return optional>(); + return scope->get(name); + } + + void error(std::string message) { errors->push_back({std::move(message), key}); } + + void error(std::string message, std::size_t child) { + errors->push_back({std::move(message), key + "[" + util::toString(child) + "]"}); + } + + void error(std::string message, std::size_t child, std::size_t grandchild) { + errors->push_back( + {std::move(message), key + "[" + util::toString(child) + "][" + util::toString(grandchild) + "]"}); + } + + void appendErrors(ParsingContext&& ctx) { + errors->reserve(errors->size() + ctx.errors->size()); + std::move(ctx.errors->begin(), ctx.errors->end(), std::inserter(*errors, errors->end())); + ctx.errors->clear(); + } + + void clearErrors() { + errors->clear(); + } + +private: + ParsingContext(std::string key_, + std::shared_ptr> errors_, + optional expected_, + std::shared_ptr scope_) + : key(std::move(key_)), + expected(std::move(expected_)), + scope(std::move(scope_)), + errors(std::move(errors_)) + {} + + + /* + Parse the given style-spec JSON value into an Expression object. + Specifically, this function is responsible for determining the expression + type (either Literal, or the one named in value[0]) and dispatching to the + appropriate ParseXxxx::parse(const V&, ParsingContext) method. + */ + ParseResult parse(const mbgl::style::conversion::Convertible& value, const optional& = {}); + + std::string key; + optional expected; + std::shared_ptr scope; + std::shared_ptr> errors; +}; + +bool isExpression(const std::string&); + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/step.hpp b/mapbox-gl-native/include/mbgl/style/expression/step.hpp new file mode 100644 index 000000000..f241f7af6 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/step.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Step : public Expression { +public: + Step(const type::Type& type_, + std::unique_ptr input_, + std::map> stops_); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function& visit) const override; + void eachStop(const std::function& visit) const; + + const std::unique_ptr& getInput() const { return input; } + Range getCoveringStops(double lower, double upper) const; + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override; + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "step"; } + +private: + const std::unique_ptr input; + const std::map> stops; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/type.hpp b/mapbox-gl-native/include/mbgl/style/expression/type.hpp new file mode 100644 index 000000000..ab398741f --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/type.hpp @@ -0,0 +1,135 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { +namespace type { + +template +std::string toString(const T& t); + +struct NullType { + constexpr NullType() = default; + std::string getName() const { return "null"; } + bool operator==(const NullType&) const { return true; } +}; + +struct NumberType { + constexpr NumberType() = default; + std::string getName() const { return "number"; } + bool operator==(const NumberType&) const { return true; } +}; + +struct BooleanType { + constexpr BooleanType() = default; + std::string getName() const { return "boolean"; } + bool operator==(const BooleanType&) const { return true; } +}; + +struct StringType { + constexpr StringType() = default; + std::string getName() const { return "string"; } + bool operator==(const StringType&) const { return true; } +}; + +struct ColorType { + constexpr ColorType() = default; + std::string getName() const { return "color"; } + bool operator==(const ColorType&) const { return true; } +}; + +struct ObjectType { + constexpr ObjectType() = default; + std::string getName() const { return "object"; } + bool operator==(const ObjectType&) const { return true; } +}; + +struct ErrorType { + constexpr ErrorType() = default; + std::string getName() const { return "error"; } + bool operator==(const ErrorType&) const { return true; } +}; + +struct ValueType { + constexpr ValueType() = default; + std::string getName() const { return "value"; } + bool operator==(const ValueType&) const { return true; } +}; + +struct CollatorType { + constexpr CollatorType() = default; + std::string getName() const { return "collator"; } + bool operator==(const CollatorType&) const { return true; } +}; + +struct FormattedType { + constexpr FormattedType() = default; + std::string getName() const { return "formatted"; } + bool operator==(const FormattedType&) const { return true; } +}; + +struct ImageType { + constexpr ImageType() = default; + std::string getName() const { return "resolvedImage"; } + bool operator==(const ImageType&) const { return true; } +}; + +constexpr NullType Null; +constexpr NumberType Number; +constexpr StringType String; +constexpr BooleanType Boolean; +constexpr ColorType Color; +constexpr ValueType Value; +constexpr ObjectType Object; +constexpr CollatorType Collator; +constexpr FormattedType Formatted; +constexpr ErrorType Error; +constexpr ImageType Image; + +struct Array; + +using Type = variant, + CollatorType, + FormattedType, + ErrorType, + ImageType>; + +struct Array { + explicit Array(Type itemType_) : itemType(std::move(itemType_)) {} + Array(Type itemType_, std::size_t N_) : itemType(std::move(itemType_)), N(N_) {} + Array(Type itemType_, optional N_) : itemType(std::move(itemType_)), N(std::move(N_)) {} + std::string getName() const { + if (N) { + return "array<" + toString(itemType) + ", " + util::toString(*N) + ">"; + } else if (itemType == Value) { + return "array"; + } else { + return "array<" + toString(itemType) + ">"; + } + } + + bool operator==(const Array& rhs) const { return itemType == rhs.itemType && N == rhs.N; } + + Type itemType; + optional N; +}; + +template +std::string toString(const T& type) { return type.match([&] (const auto& t) { return t.getName(); }); } + +} // namespace type +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/value.hpp b/mapbox-gl-native/include/mbgl/style/expression/value.hpp new file mode 100644 index 000000000..ad11d1ceb --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/value.hpp @@ -0,0 +1,161 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +struct Value; + +using ValueBase = variant>, + mapbox::util::recursive_wrapper>>; +struct Value : ValueBase { + using ValueBase::ValueBase; + + // Javascript's Number.MAX_SAFE_INTEGER + static uint64_t maxSafeInteger() { return 9007199254740991ULL; } + + static bool isSafeInteger(uint64_t x) { return x <= maxSafeInteger(); }; + static bool isSafeInteger(int64_t x) { + return static_cast(x > 0 ? x : -x) <= maxSafeInteger(); + } + static bool isSafeInteger(double x) { + return static_cast(x > 0 ? x : -x) <= maxSafeInteger(); + } + +}; + +constexpr NullValue Null = NullValue(); + +type::Type typeOf(const Value& value); + +std::string toString(const Value& value); +std::string stringify(const Value& value); + +/* + Returns a Type object representing the expression type that corresponds to + the value type T. (Specialized for primitives and specific array types in + the .cpp.) +*/ +template +type::Type valueTypeToExpressionType(); + +/* + Conversions between style value types and expression::Value +*/ + +template +struct ValueConverter { + static Value toExpressionValue(const T& value) { + return Value(value); + } + + static optional fromExpressionValue(const Value& value) { + return value.template is() ? value.template get() : optional(); + } +}; + +template <> +struct ValueConverter { + static type::Type expressionType() { return type::Value; } + static Value toExpressionValue(const Value& value) { return value; } + static optional fromExpressionValue(const Value& value) { return value; } +}; + +template <> +struct ValueConverter { + static Value toExpressionValue(const mbgl::Value& value); + static mbgl::Value fromExpressionValue(const Value& value); +}; + +template <> +struct ValueConverter { + static type::Type expressionType() { return type::Number; } + static Value toExpressionValue(float value); + static optional fromExpressionValue(const Value& value); +}; + +template +struct ValueConverter> { + static type::Type expressionType() { + return type::Array(valueTypeToExpressionType(), N); + } + static Value toExpressionValue(const std::array& value); + static optional> fromExpressionValue(const Value& value); +}; + +template +struct ValueConverter> { + static type::Type expressionType() { + return type::Array(valueTypeToExpressionType()); + } + static Value toExpressionValue(const std::vector& value); + static optional> fromExpressionValue(const Value& value); +}; + +template <> +struct ValueConverter { + static type::Type expressionType() { return type::Array(type::Number, 3); } + static Value toExpressionValue(const mbgl::style::Position& value); + static optional fromExpressionValue(const Value& v); +}; + +template +struct ValueConverter::value >> { + static type::Type expressionType() { return type::String; } + static Value toExpressionValue(const T& value); + static optional fromExpressionValue(const Value& value); +}; + +template +Value toExpressionValue(const T& value) { + return ValueConverter::toExpressionValue(value); +} + +template +optional fromExpressionValue(const Value& value) { + return ValueConverter::fromExpressionValue(value); +} + +template +std::vector> fromExpressionValues(const std::vector>& values) { + std::vector> result; + result.reserve(values.size()); + for (const auto& value : values) { + result.push_back(value ? fromExpressionValue(*value) : nullopt); + } + return result; +} + +template <> +struct ValueConverter { + static type::Type expressionType() { return type::Number; } + static Value toExpressionValue(const mbgl::style::Rotation& value); + static optional fromExpressionValue(const Value& v); +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/expression/within.hpp b/mapbox-gl-native/include/mbgl/style/expression/within.hpp new file mode 100644 index 000000000..37e27f494 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/expression/within.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class Within final : public Expression { +public: + explicit Within(GeoJSON geojson, Feature::geometry_type geometries_); + + ~Within() override; + + EvaluationResult evaluate(const EvaluationContext&) const override; + + static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&); + + void eachChild(const std::function&) const override {} + + bool operator==(const Expression& e) const override; + + std::vector> possibleOutputs() const override; + + mbgl::Value serialize() const override; + std::string getOperator() const override; + +private: + GeoJSON geoJSONSource; + Feature::geometry_type geometries; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/filter.hpp b/mapbox-gl-native/include/mbgl/style/filter.hpp new file mode 100644 index 000000000..c3ce6833f --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/filter.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { +namespace style { + +class Filter { +public: + optional> expression; +private: + optional legacyFilter; +public: + Filter() = default; + + Filter(expression::ParseResult _expression, optional _filter = {}) + : expression(std::move(*_expression)), + legacyFilter(std::move(_filter)){ + assert(!expression || *expression != nullptr); + } + + bool operator()(const expression::EvaluationContext& context) const; + + operator bool() const { return expression || legacyFilter; } + + 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); + } + } + + friend bool operator!=(const Filter& lhs, const Filter& rhs) { + return !(lhs == rhs); + } + + mbgl::Value serialize() const { + if (legacyFilter) { + return *legacyFilter; + } + else if (expression) { + return (**expression).serialize(); + } + return NullValue(); + } +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/image.hpp b/mapbox-gl-native/include/mbgl/style/image.hpp new file mode 100644 index 000000000..5783fc246 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/image.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace mbgl { +namespace style { + +using ImageStretch = std::pair; +using ImageStretches = std::vector; + +class ImageContent { +public: + float left; + float top; + float right; + float bottom; + + bool operator==(const ImageContent& rhs) const { + return left == rhs.left && top == rhs.top && right == rhs.right && bottom == rhs.bottom; + } +}; + +class Image { +public: + Image(std::string id, + PremultipliedImage&&, + float pixelRatio, + bool sdf, + ImageStretches stretchX = {}, + ImageStretches stretchY = {}, + const optional& content = nullopt); + Image(std::string id, + PremultipliedImage&& image, + float pixelRatio, + ImageStretches stretchX = {}, + ImageStretches stretchY = {}, + const optional& content = nullopt) + : Image(std::move(id), std::move(image), pixelRatio, false, std::move(stretchX), std::move(stretchY), content) { + } + Image(const Image&); + + std::string getID() const; + + const PremultipliedImage& getImage() const; + + // Pixel ratio of the sprite image. + float getPixelRatio() const; + + // Whether this image should be interpreted as a signed distance field icon. + bool isSdf() const; + + // Stretch areas of this image. + const ImageStretches& getStretchX() const; + const ImageStretches& getStretchY() const; + + // The space where text can be fit into this image. + const optional& getContent() const; + + class Impl; + Immutable baseImpl; + explicit Image(Immutable baseImpl_) : baseImpl(std::move(baseImpl_)) {} +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/layer.hpp b/mapbox-gl-native/include/mbgl/style/layer.hpp new file mode 100644 index 000000000..9884ad0a3 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layer.hpp @@ -0,0 +1,157 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class LayerObserver; +class Filter; + +/** + * @brief Holds static data for a certain layer type. + */ +struct LayerTypeInfo { + /** + * @brief contains the layer type as defined in the style specification; + */ + const char* type; + + /** + * @brief contains \c Source::Required if the corresponding layer type + * requires source. Contains \c Source::NotRequired otherwise. + */ + const enum class Source { Required, NotRequired } source; + + /** + * @brief contains \c Pass3D::Required if the corresponding layer type + * requires 3D rendering pass. Contains \c Pass3D::NotRequired otherwise. + */ + const enum class Pass3D { Required, NotRequired } pass3d; + + /** + * @brief contains \c Layout::Required if the corresponding layer type + * requires layouting. * contains \c Layout::NotRequired otherwise. + */ + const enum class Layout { Required, NotRequired } layout; + + /** + * @brief contains \c FadingTiles::Required if the corresponding layer type + * requires rendering on fading tiles. Contains \c FadingTiles::NotRequired otherwise. + */ + const enum class FadingTiles { Required, NotRequired } fadingTiles; + + /** + * @brief contains \c CrossTileIndex::Required if the corresponding layer type + * requires cross-tile indexing and placement. Contains \c CrossTileIndex::NotRequired otherwise. + */ + const enum class CrossTileIndex { Required, NotRequired } crossTileIndex; + + /** + * @brief contains the Id of the supported tile type. Used for internal checks. + * The contained values correspond to \c Tile::Kind enum. + */ + const enum class TileKind : uint8_t { Geometry, Raster, RasterDEM, NotRequired } tileKind; +}; + +/** + * The runtime representation of a [layer](https://www.mapbox.com/mapbox-gl-style-spec/#layers) from the Mapbox Style + * Specification. + * + * `Layer` is an abstract base class; concrete derived classes are provided for each layer type. `Layer` contains + * functionality that is common to all layer types: + * + * * Runtime type information: type predicates and casting + * * Accessors for properties common to all layer types: ID, visibility, etc. + * * Cloning and copying + * + * All other functionality lives in the derived classes. To instantiate a layer, create an instance of the desired + * type, calling `LayerManager`: + * + * auto circleLayer = LayerManager::get()->createLayer("circle", ...); + */ +class Layer { +public: + Layer(const Layer& ) = delete; + Layer& operator=(const Layer&) = delete; + + virtual ~Layer(); + + std::string getID() const; + // Source + std::string getSourceID() const; + std::string getSourceLayer() const; + void setSourceLayer(const std::string& sourceLayer); + void setSourceID(const std::string& sourceID); + + // Filter + const Filter& getFilter() const; + void setFilter(const Filter&); + + // Visibility + VisibilityType getVisibility() const; + void setVisibility(VisibilityType); + + // Zoom range + float getMinZoom() const; + float getMaxZoom() const; + void setMinZoom(float); + void setMaxZoom(float); + + // Dynamic properties + optional setProperty(const std::string& name, const conversion::Convertible& value); + + virtual StyleProperty getProperty(const std::string&) const = 0; + virtual Value serialize() const; + + // Private implementation + // TODO : We should not have public mutable data members. + class Impl; + Immutable baseImpl; + + // Create a layer, copying all properties except id and paint properties from this layer. + virtual std::unique_ptr cloneRef(const std::string& id) const = 0; + + + void setObserver(LayerObserver*); + + // For use in SDK bindings, which store a reference to a platform-native peer + // object here, so that separately-obtained references to this object share + // identical platform-native peers. + mapbox::base::TypeWrapper peer; + Layer(Immutable); + + const LayerTypeInfo* getTypeInfo() const noexcept; + + mapbox::base::WeakPtr makeWeakPtr() { + return weakFactory.makeWeakPtr(); + } + +protected: + virtual Mutable mutableBaseImpl() const = 0; + void serializeProperty(Value&, const StyleProperty&, const char* propertyName, bool isPaint) const; + virtual optional setPropertyInternal(const std::string& name, + const conversion::Convertible& value) = 0; + LayerObserver* observer; + mapbox::base::WeakPtrFactory weakFactory {this}; + +private: + optional setVisibility(const conversion::Convertible& value); + optional setMinZoom(const conversion::Convertible& value); + optional setMaxZoom(const conversion::Convertible& value); +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/layer_properties.hpp b/mapbox-gl-native/include/mbgl/style/layer_properties.hpp new file mode 100644 index 000000000..024301f55 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layer_properties.hpp @@ -0,0 +1,37 @@ +#pragma once +#include + +namespace mbgl { + +namespace style { + +/** + * @brief An interface, wrapping evaluated layer properties. + * + * It is an abstract base class; concrete derived classes that hold the actual data are provided for each layer type. + */ +class LayerProperties { +public: + virtual ~LayerProperties() = default; + // Returns constants mask for the data-driven properties. + virtual unsigned long constantsMask() const { return 0u; } + Immutable baseImpl; + // Contains render passes used by the renderer, see `mbgl::RenderPass`. + uint8_t renderPasses = 0u; + +protected: + LayerProperties(Immutable impl) : baseImpl(std::move(impl)) {} +}; + +template +inline const auto& getEvaluated(const Immutable& properties) { + return static_cast(*properties).evaluated; +} + +template +inline const auto& getCrossfade(const Immutable& properties) { + return static_cast(*properties).crossfade; +} + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/layers/background_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/background_layer.hpp new file mode 100644 index 000000000..6217484dc --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/background_layer.hpp @@ -0,0 +1,64 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class BackgroundLayer final : public Layer { +public: + BackgroundLayer(const std::string& layerID); + ~BackgroundLayer() override; + + // Paint properties + + static PropertyValue getDefaultBackgroundColor(); + const PropertyValue& getBackgroundColor() const; + void setBackgroundColor(const PropertyValue&); + void setBackgroundColorTransition(const TransitionOptions&); + TransitionOptions getBackgroundColorTransition() const; + + static PropertyValue getDefaultBackgroundOpacity(); + const PropertyValue& getBackgroundOpacity() const; + void setBackgroundOpacity(const PropertyValue&); + void setBackgroundOpacityTransition(const TransitionOptions&); + TransitionOptions getBackgroundOpacityTransition() const; + + static PropertyValue getDefaultBackgroundPattern(); + const PropertyValue& getBackgroundPattern() const; + void setBackgroundPattern(const PropertyValue&); + void setBackgroundPatternTransition(const TransitionOptions&); + TransitionOptions getBackgroundPatternTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + BackgroundLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/circle_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/circle_layer.hpp new file mode 100644 index 000000000..bb1ccbfae --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/circle_layer.hpp @@ -0,0 +1,118 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class CircleLayer final : public Layer { +public: + CircleLayer(const std::string& layerID, const std::string& sourceID); + ~CircleLayer() override; + + // Layout properties + + static PropertyValue getDefaultCircleSortKey(); + const PropertyValue& getCircleSortKey() const; + void setCircleSortKey(const PropertyValue&); + + // Paint properties + + static PropertyValue getDefaultCircleBlur(); + const PropertyValue& getCircleBlur() const; + void setCircleBlur(const PropertyValue&); + void setCircleBlurTransition(const TransitionOptions&); + TransitionOptions getCircleBlurTransition() const; + + static PropertyValue getDefaultCircleColor(); + const PropertyValue& getCircleColor() const; + void setCircleColor(const PropertyValue&); + void setCircleColorTransition(const TransitionOptions&); + TransitionOptions getCircleColorTransition() const; + + static PropertyValue getDefaultCircleOpacity(); + const PropertyValue& getCircleOpacity() const; + void setCircleOpacity(const PropertyValue&); + void setCircleOpacityTransition(const TransitionOptions&); + TransitionOptions getCircleOpacityTransition() const; + + static PropertyValue getDefaultCirclePitchAlignment(); + const PropertyValue& getCirclePitchAlignment() const; + void setCirclePitchAlignment(const PropertyValue&); + void setCirclePitchAlignmentTransition(const TransitionOptions&); + TransitionOptions getCirclePitchAlignmentTransition() const; + + static PropertyValue getDefaultCirclePitchScale(); + const PropertyValue& getCirclePitchScale() const; + void setCirclePitchScale(const PropertyValue&); + void setCirclePitchScaleTransition(const TransitionOptions&); + TransitionOptions getCirclePitchScaleTransition() const; + + static PropertyValue getDefaultCircleRadius(); + const PropertyValue& getCircleRadius() const; + void setCircleRadius(const PropertyValue&); + void setCircleRadiusTransition(const TransitionOptions&); + TransitionOptions getCircleRadiusTransition() const; + + static PropertyValue getDefaultCircleStrokeColor(); + const PropertyValue& getCircleStrokeColor() const; + void setCircleStrokeColor(const PropertyValue&); + void setCircleStrokeColorTransition(const TransitionOptions&); + TransitionOptions getCircleStrokeColorTransition() const; + + static PropertyValue getDefaultCircleStrokeOpacity(); + const PropertyValue& getCircleStrokeOpacity() const; + void setCircleStrokeOpacity(const PropertyValue&); + void setCircleStrokeOpacityTransition(const TransitionOptions&); + TransitionOptions getCircleStrokeOpacityTransition() const; + + static PropertyValue getDefaultCircleStrokeWidth(); + const PropertyValue& getCircleStrokeWidth() const; + void setCircleStrokeWidth(const PropertyValue&); + void setCircleStrokeWidthTransition(const TransitionOptions&); + TransitionOptions getCircleStrokeWidthTransition() const; + + static PropertyValue> getDefaultCircleTranslate(); + const PropertyValue>& getCircleTranslate() const; + void setCircleTranslate(const PropertyValue>&); + void setCircleTranslateTransition(const TransitionOptions&); + TransitionOptions getCircleTranslateTransition() const; + + static PropertyValue getDefaultCircleTranslateAnchor(); + const PropertyValue& getCircleTranslateAnchor() const; + void setCircleTranslateAnchor(const PropertyValue&); + void setCircleTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getCircleTranslateAnchorTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + CircleLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/fill_extrusion_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/fill_extrusion_layer.hpp new file mode 100644 index 000000000..e823e63b8 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/fill_extrusion_layer.hpp @@ -0,0 +1,94 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class FillExtrusionLayer final : public Layer { +public: + FillExtrusionLayer(const std::string& layerID, const std::string& sourceID); + ~FillExtrusionLayer() override; + + // Paint properties + + static PropertyValue getDefaultFillExtrusionBase(); + const PropertyValue& getFillExtrusionBase() const; + void setFillExtrusionBase(const PropertyValue&); + void setFillExtrusionBaseTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionBaseTransition() const; + + static PropertyValue getDefaultFillExtrusionColor(); + const PropertyValue& getFillExtrusionColor() const; + void setFillExtrusionColor(const PropertyValue&); + void setFillExtrusionColorTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionColorTransition() const; + + static PropertyValue getDefaultFillExtrusionHeight(); + const PropertyValue& getFillExtrusionHeight() const; + void setFillExtrusionHeight(const PropertyValue&); + void setFillExtrusionHeightTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionHeightTransition() const; + + static PropertyValue getDefaultFillExtrusionOpacity(); + const PropertyValue& getFillExtrusionOpacity() const; + void setFillExtrusionOpacity(const PropertyValue&); + void setFillExtrusionOpacityTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionOpacityTransition() const; + + static PropertyValue getDefaultFillExtrusionPattern(); + const PropertyValue& getFillExtrusionPattern() const; + void setFillExtrusionPattern(const PropertyValue&); + void setFillExtrusionPatternTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionPatternTransition() const; + + static PropertyValue> getDefaultFillExtrusionTranslate(); + const PropertyValue>& getFillExtrusionTranslate() const; + void setFillExtrusionTranslate(const PropertyValue>&); + void setFillExtrusionTranslateTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionTranslateTransition() const; + + static PropertyValue getDefaultFillExtrusionTranslateAnchor(); + const PropertyValue& getFillExtrusionTranslateAnchor() const; + void setFillExtrusionTranslateAnchor(const PropertyValue&); + void setFillExtrusionTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionTranslateAnchorTransition() const; + + static PropertyValue getDefaultFillExtrusionVerticalGradient(); + const PropertyValue& getFillExtrusionVerticalGradient() const; + void setFillExtrusionVerticalGradient(const PropertyValue&); + void setFillExtrusionVerticalGradientTransition(const TransitionOptions&); + TransitionOptions getFillExtrusionVerticalGradientTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + FillExtrusionLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/fill_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/fill_layer.hpp new file mode 100644 index 000000000..a5f61a0c8 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/fill_layer.hpp @@ -0,0 +1,94 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class FillLayer final : public Layer { +public: + FillLayer(const std::string& layerID, const std::string& sourceID); + ~FillLayer() override; + + // Layout properties + + static PropertyValue getDefaultFillSortKey(); + const PropertyValue& getFillSortKey() const; + void setFillSortKey(const PropertyValue&); + + // Paint properties + + static PropertyValue getDefaultFillAntialias(); + const PropertyValue& getFillAntialias() const; + void setFillAntialias(const PropertyValue&); + void setFillAntialiasTransition(const TransitionOptions&); + TransitionOptions getFillAntialiasTransition() const; + + static PropertyValue getDefaultFillColor(); + const PropertyValue& getFillColor() const; + void setFillColor(const PropertyValue&); + void setFillColorTransition(const TransitionOptions&); + TransitionOptions getFillColorTransition() const; + + static PropertyValue getDefaultFillOpacity(); + const PropertyValue& getFillOpacity() const; + void setFillOpacity(const PropertyValue&); + void setFillOpacityTransition(const TransitionOptions&); + TransitionOptions getFillOpacityTransition() const; + + static PropertyValue getDefaultFillOutlineColor(); + const PropertyValue& getFillOutlineColor() const; + void setFillOutlineColor(const PropertyValue&); + void setFillOutlineColorTransition(const TransitionOptions&); + TransitionOptions getFillOutlineColorTransition() const; + + static PropertyValue getDefaultFillPattern(); + const PropertyValue& getFillPattern() const; + void setFillPattern(const PropertyValue&); + void setFillPatternTransition(const TransitionOptions&); + TransitionOptions getFillPatternTransition() const; + + static PropertyValue> getDefaultFillTranslate(); + const PropertyValue>& getFillTranslate() const; + void setFillTranslate(const PropertyValue>&); + void setFillTranslateTransition(const TransitionOptions&); + TransitionOptions getFillTranslateTransition() const; + + static PropertyValue getDefaultFillTranslateAnchor(); + const PropertyValue& getFillTranslateAnchor() const; + void setFillTranslateAnchor(const PropertyValue&); + void setFillTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getFillTranslateAnchorTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + FillLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/heatmap_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/heatmap_layer.hpp new file mode 100644 index 000000000..cbe353a6c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/heatmap_layer.hpp @@ -0,0 +1,77 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class HeatmapLayer final : public Layer { +public: + HeatmapLayer(const std::string& layerID, const std::string& sourceID); + ~HeatmapLayer() override; + + // Paint properties + + static ColorRampPropertyValue getDefaultHeatmapColor(); + const ColorRampPropertyValue& getHeatmapColor() const; + void setHeatmapColor(const ColorRampPropertyValue&); + void setHeatmapColorTransition(const TransitionOptions&); + TransitionOptions getHeatmapColorTransition() const; + + static PropertyValue getDefaultHeatmapIntensity(); + const PropertyValue& getHeatmapIntensity() const; + void setHeatmapIntensity(const PropertyValue&); + void setHeatmapIntensityTransition(const TransitionOptions&); + TransitionOptions getHeatmapIntensityTransition() const; + + static PropertyValue getDefaultHeatmapOpacity(); + const PropertyValue& getHeatmapOpacity() const; + void setHeatmapOpacity(const PropertyValue&); + void setHeatmapOpacityTransition(const TransitionOptions&); + TransitionOptions getHeatmapOpacityTransition() const; + + static PropertyValue getDefaultHeatmapRadius(); + const PropertyValue& getHeatmapRadius() const; + void setHeatmapRadius(const PropertyValue&); + void setHeatmapRadiusTransition(const TransitionOptions&); + TransitionOptions getHeatmapRadiusTransition() const; + + static PropertyValue getDefaultHeatmapWeight(); + const PropertyValue& getHeatmapWeight() const; + void setHeatmapWeight(const PropertyValue&); + void setHeatmapWeightTransition(const TransitionOptions&); + TransitionOptions getHeatmapWeightTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + HeatmapLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/hillshade_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/hillshade_layer.hpp new file mode 100644 index 000000000..2f50f9264 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/hillshade_layer.hpp @@ -0,0 +1,82 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class HillshadeLayer final : public Layer { +public: + HillshadeLayer(const std::string& layerID, const std::string& sourceID); + ~HillshadeLayer() override; + + // Paint properties + + static PropertyValue getDefaultHillshadeAccentColor(); + const PropertyValue& getHillshadeAccentColor() const; + void setHillshadeAccentColor(const PropertyValue&); + void setHillshadeAccentColorTransition(const TransitionOptions&); + TransitionOptions getHillshadeAccentColorTransition() const; + + static PropertyValue getDefaultHillshadeExaggeration(); + const PropertyValue& getHillshadeExaggeration() const; + void setHillshadeExaggeration(const PropertyValue&); + void setHillshadeExaggerationTransition(const TransitionOptions&); + TransitionOptions getHillshadeExaggerationTransition() const; + + static PropertyValue getDefaultHillshadeHighlightColor(); + const PropertyValue& getHillshadeHighlightColor() const; + void setHillshadeHighlightColor(const PropertyValue&); + void setHillshadeHighlightColorTransition(const TransitionOptions&); + TransitionOptions getHillshadeHighlightColorTransition() const; + + static PropertyValue getDefaultHillshadeIlluminationAnchor(); + const PropertyValue& getHillshadeIlluminationAnchor() const; + void setHillshadeIlluminationAnchor(const PropertyValue&); + void setHillshadeIlluminationAnchorTransition(const TransitionOptions&); + TransitionOptions getHillshadeIlluminationAnchorTransition() const; + + static PropertyValue getDefaultHillshadeIlluminationDirection(); + const PropertyValue& getHillshadeIlluminationDirection() const; + void setHillshadeIlluminationDirection(const PropertyValue&); + void setHillshadeIlluminationDirectionTransition(const TransitionOptions&); + TransitionOptions getHillshadeIlluminationDirectionTransition() const; + + static PropertyValue getDefaultHillshadeShadowColor(); + const PropertyValue& getHillshadeShadowColor() const; + void setHillshadeShadowColor(const PropertyValue&); + void setHillshadeShadowColorTransition(const TransitionOptions&); + TransitionOptions getHillshadeShadowColorTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + HillshadeLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/layer.hpp.ejs b/mapbox-gl-native/include/mbgl/style/layers/layer.hpp.ejs new file mode 100644 index 000000000..114fb1e34 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/layer.hpp.ejs @@ -0,0 +1,86 @@ +<% + const type = locals.type; + const layoutProperties = locals.layoutProperties; + const paintProperties = locals.paintProperties; +-%> +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +<% if (type === 'heatmap' || type === 'line') { -%> +#include +<% } -%> +#include +#include +#include +<% if (type === 'location-indicator') { -%> +#include +<% } -%> +<% if (type === 'symbol') { -%> +#include +<% } -%> +#include + +<% if (type === 'line' || type === 'symbol') { -%> +#include + +<% } -%> +namespace mbgl { +namespace style { + +class TransitionOptions; + +class <%- camelize(type) %>Layer final : public Layer { +public: +<% if ((type === 'background') || (type === 'location-indicator')) { -%> + <%- camelize(type) %>Layer(const std::string& layerID); +<% } else { -%> + <%- camelize(type) %>Layer(const std::string& layerID, const std::string& sourceID); +<% } -%> + ~<%- camelize(type) %>Layer() override; + +<% if (layoutProperties.length) { -%> + // Layout properties + +<% for (const property of layoutProperties) { -%> + static <%- propertyValueType(property) %> getDefault<%- camelize(property.name) %>(); + const <%- propertyValueType(property) %>& get<%- camelize(property.name) %>() const; + void set<%- camelize(property.name) %>(const <%- propertyValueType(property) %>&); + +<% } -%> +<% } -%> + // Paint properties + +<% for (const property of paintProperties) { -%> + static <%- propertyValueType(property) %> getDefault<%- camelize(property.name) %>(); + const <%- propertyValueType(property) %>& get<%- camelize(property.name) %>() const; + void set<%- camelize(property.name) %>(const <%- propertyValueType(property) %>&); + void set<%- camelize(property.name) %>Transition(const TransitionOptions&); + TransitionOptions get<%- camelize(property.name) %>Transition() const; + +<% } -%> + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + <%- camelize(type) %>Layer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/line_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/line_layer.hpp new file mode 100644 index 000000000..1f3e8b04c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/line_layer.hpp @@ -0,0 +1,137 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class LineLayer final : public Layer { +public: + LineLayer(const std::string& layerID, const std::string& sourceID); + ~LineLayer() override; + + // Layout properties + + static PropertyValue getDefaultLineCap(); + const PropertyValue& getLineCap() const; + void setLineCap(const PropertyValue&); + + static PropertyValue getDefaultLineJoin(); + const PropertyValue& getLineJoin() const; + void setLineJoin(const PropertyValue&); + + static PropertyValue getDefaultLineMiterLimit(); + const PropertyValue& getLineMiterLimit() const; + void setLineMiterLimit(const PropertyValue&); + + static PropertyValue getDefaultLineRoundLimit(); + const PropertyValue& getLineRoundLimit() const; + void setLineRoundLimit(const PropertyValue&); + + static PropertyValue getDefaultLineSortKey(); + const PropertyValue& getLineSortKey() const; + void setLineSortKey(const PropertyValue&); + + // Paint properties + + static PropertyValue getDefaultLineBlur(); + const PropertyValue& getLineBlur() const; + void setLineBlur(const PropertyValue&); + void setLineBlurTransition(const TransitionOptions&); + TransitionOptions getLineBlurTransition() const; + + static PropertyValue getDefaultLineColor(); + const PropertyValue& getLineColor() const; + void setLineColor(const PropertyValue&); + void setLineColorTransition(const TransitionOptions&); + TransitionOptions getLineColorTransition() const; + + static PropertyValue> getDefaultLineDasharray(); + const PropertyValue>& getLineDasharray() const; + void setLineDasharray(const PropertyValue>&); + void setLineDasharrayTransition(const TransitionOptions&); + TransitionOptions getLineDasharrayTransition() const; + + static PropertyValue getDefaultLineGapWidth(); + const PropertyValue& getLineGapWidth() const; + void setLineGapWidth(const PropertyValue&); + void setLineGapWidthTransition(const TransitionOptions&); + TransitionOptions getLineGapWidthTransition() const; + + static ColorRampPropertyValue getDefaultLineGradient(); + const ColorRampPropertyValue& getLineGradient() const; + void setLineGradient(const ColorRampPropertyValue&); + void setLineGradientTransition(const TransitionOptions&); + TransitionOptions getLineGradientTransition() const; + + static PropertyValue getDefaultLineOffset(); + const PropertyValue& getLineOffset() const; + void setLineOffset(const PropertyValue&); + void setLineOffsetTransition(const TransitionOptions&); + TransitionOptions getLineOffsetTransition() const; + + static PropertyValue getDefaultLineOpacity(); + const PropertyValue& getLineOpacity() const; + void setLineOpacity(const PropertyValue&); + void setLineOpacityTransition(const TransitionOptions&); + TransitionOptions getLineOpacityTransition() const; + + static PropertyValue getDefaultLinePattern(); + const PropertyValue& getLinePattern() const; + void setLinePattern(const PropertyValue&); + void setLinePatternTransition(const TransitionOptions&); + TransitionOptions getLinePatternTransition() const; + + static PropertyValue> getDefaultLineTranslate(); + const PropertyValue>& getLineTranslate() const; + void setLineTranslate(const PropertyValue>&); + void setLineTranslateTransition(const TransitionOptions&); + TransitionOptions getLineTranslateTransition() const; + + static PropertyValue getDefaultLineTranslateAnchor(); + const PropertyValue& getLineTranslateAnchor() const; + void setLineTranslateAnchor(const PropertyValue&); + void setLineTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getLineTranslateAnchorTransition() const; + + static PropertyValue getDefaultLineWidth(); + const PropertyValue& getLineWidth() const; + void setLineWidth(const PropertyValue&); + void setLineWidthTransition(const TransitionOptions&); + TransitionOptions getLineWidthTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + LineLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/location_indicator_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/location_indicator_layer.hpp new file mode 100644 index 000000000..8d70bc18d --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/location_indicator_layer.hpp @@ -0,0 +1,121 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class LocationIndicatorLayer final : public Layer { +public: + LocationIndicatorLayer(const std::string& layerID); + ~LocationIndicatorLayer() override; + + // Layout properties + + static PropertyValue getDefaultBearingImage(); + const PropertyValue& getBearingImage() const; + void setBearingImage(const PropertyValue&); + + static PropertyValue getDefaultShadowImage(); + const PropertyValue& getShadowImage() const; + void setShadowImage(const PropertyValue&); + + static PropertyValue getDefaultTopImage(); + const PropertyValue& getTopImage() const; + void setTopImage(const PropertyValue&); + + // Paint properties + + static PropertyValue getDefaultAccuracyRadius(); + const PropertyValue& getAccuracyRadius() const; + void setAccuracyRadius(const PropertyValue&); + void setAccuracyRadiusTransition(const TransitionOptions&); + TransitionOptions getAccuracyRadiusTransition() const; + + static PropertyValue getDefaultAccuracyRadiusBorderColor(); + const PropertyValue& getAccuracyRadiusBorderColor() const; + void setAccuracyRadiusBorderColor(const PropertyValue&); + void setAccuracyRadiusBorderColorTransition(const TransitionOptions&); + TransitionOptions getAccuracyRadiusBorderColorTransition() const; + + static PropertyValue getDefaultAccuracyRadiusColor(); + const PropertyValue& getAccuracyRadiusColor() const; + void setAccuracyRadiusColor(const PropertyValue&); + void setAccuracyRadiusColorTransition(const TransitionOptions&); + TransitionOptions getAccuracyRadiusColorTransition() const; + + static PropertyValue getDefaultBearing(); + const PropertyValue& getBearing() const; + void setBearing(const PropertyValue&); + void setBearingTransition(const TransitionOptions&); + TransitionOptions getBearingTransition() const; + + static PropertyValue getDefaultBearingImageSize(); + const PropertyValue& getBearingImageSize() const; + void setBearingImageSize(const PropertyValue&); + void setBearingImageSizeTransition(const TransitionOptions&); + TransitionOptions getBearingImageSizeTransition() const; + + static PropertyValue getDefaultImageTiltDisplacement(); + const PropertyValue& getImageTiltDisplacement() const; + void setImageTiltDisplacement(const PropertyValue&); + void setImageTiltDisplacementTransition(const TransitionOptions&); + TransitionOptions getImageTiltDisplacementTransition() const; + + static PropertyValue> getDefaultLocation(); + const PropertyValue>& getLocation() const; + void setLocation(const PropertyValue>&); + void setLocationTransition(const TransitionOptions&); + TransitionOptions getLocationTransition() const; + + static PropertyValue getDefaultPerspectiveCompensation(); + const PropertyValue& getPerspectiveCompensation() const; + void setPerspectiveCompensation(const PropertyValue&); + void setPerspectiveCompensationTransition(const TransitionOptions&); + TransitionOptions getPerspectiveCompensationTransition() const; + + static PropertyValue getDefaultShadowImageSize(); + const PropertyValue& getShadowImageSize() const; + void setShadowImageSize(const PropertyValue&); + void setShadowImageSizeTransition(const TransitionOptions&); + TransitionOptions getShadowImageSizeTransition() const; + + static PropertyValue getDefaultTopImageSize(); + const PropertyValue& getTopImageSize() const; + void setTopImageSize(const PropertyValue&); + void setTopImageSizeTransition(const TransitionOptions&); + TransitionOptions getTopImageSizeTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + LocationIndicatorLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/raster_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/raster_layer.hpp new file mode 100644 index 000000000..2f10b7cbe --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/raster_layer.hpp @@ -0,0 +1,94 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class RasterLayer final : public Layer { +public: + RasterLayer(const std::string& layerID, const std::string& sourceID); + ~RasterLayer() override; + + // Paint properties + + static PropertyValue getDefaultRasterBrightnessMax(); + const PropertyValue& getRasterBrightnessMax() const; + void setRasterBrightnessMax(const PropertyValue&); + void setRasterBrightnessMaxTransition(const TransitionOptions&); + TransitionOptions getRasterBrightnessMaxTransition() const; + + static PropertyValue getDefaultRasterBrightnessMin(); + const PropertyValue& getRasterBrightnessMin() const; + void setRasterBrightnessMin(const PropertyValue&); + void setRasterBrightnessMinTransition(const TransitionOptions&); + TransitionOptions getRasterBrightnessMinTransition() const; + + static PropertyValue getDefaultRasterContrast(); + const PropertyValue& getRasterContrast() const; + void setRasterContrast(const PropertyValue&); + void setRasterContrastTransition(const TransitionOptions&); + TransitionOptions getRasterContrastTransition() const; + + static PropertyValue getDefaultRasterFadeDuration(); + const PropertyValue& getRasterFadeDuration() const; + void setRasterFadeDuration(const PropertyValue&); + void setRasterFadeDurationTransition(const TransitionOptions&); + TransitionOptions getRasterFadeDurationTransition() const; + + static PropertyValue getDefaultRasterHueRotate(); + const PropertyValue& getRasterHueRotate() const; + void setRasterHueRotate(const PropertyValue&); + void setRasterHueRotateTransition(const TransitionOptions&); + TransitionOptions getRasterHueRotateTransition() const; + + static PropertyValue getDefaultRasterOpacity(); + const PropertyValue& getRasterOpacity() const; + void setRasterOpacity(const PropertyValue&); + void setRasterOpacityTransition(const TransitionOptions&); + TransitionOptions getRasterOpacityTransition() const; + + static PropertyValue getDefaultRasterResampling(); + const PropertyValue& getRasterResampling() const; + void setRasterResampling(const PropertyValue&); + void setRasterResamplingTransition(const TransitionOptions&); + TransitionOptions getRasterResamplingTransition() const; + + static PropertyValue getDefaultRasterSaturation(); + const PropertyValue& getRasterSaturation() const; + void setRasterSaturation(const PropertyValue&); + void setRasterSaturationTransition(const TransitionOptions&); + TransitionOptions getRasterSaturationTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + RasterLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/layers/symbol_layer.hpp b/mapbox-gl-native/include/mbgl/style/layers/symbol_layer.hpp new file mode 100644 index 000000000..df54b7ad0 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/layers/symbol_layer.hpp @@ -0,0 +1,299 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace style { + +class TransitionOptions; + +class SymbolLayer final : public Layer { +public: + SymbolLayer(const std::string& layerID, const std::string& sourceID); + ~SymbolLayer() override; + + // Layout properties + + static PropertyValue getDefaultIconAllowOverlap(); + const PropertyValue& getIconAllowOverlap() const; + void setIconAllowOverlap(const PropertyValue&); + + static PropertyValue getDefaultIconAnchor(); + const PropertyValue& getIconAnchor() const; + void setIconAnchor(const PropertyValue&); + + static PropertyValue getDefaultIconIgnorePlacement(); + const PropertyValue& getIconIgnorePlacement() const; + void setIconIgnorePlacement(const PropertyValue&); + + static PropertyValue getDefaultIconImage(); + const PropertyValue& getIconImage() const; + void setIconImage(const PropertyValue&); + + static PropertyValue getDefaultIconKeepUpright(); + const PropertyValue& getIconKeepUpright() const; + void setIconKeepUpright(const PropertyValue&); + + static PropertyValue> getDefaultIconOffset(); + const PropertyValue>& getIconOffset() const; + void setIconOffset(const PropertyValue>&); + + static PropertyValue getDefaultIconOptional(); + const PropertyValue& getIconOptional() const; + void setIconOptional(const PropertyValue&); + + static PropertyValue getDefaultIconPadding(); + const PropertyValue& getIconPadding() const; + void setIconPadding(const PropertyValue&); + + static PropertyValue getDefaultIconPitchAlignment(); + const PropertyValue& getIconPitchAlignment() const; + void setIconPitchAlignment(const PropertyValue&); + + static PropertyValue getDefaultIconRotate(); + const PropertyValue& getIconRotate() const; + void setIconRotate(const PropertyValue&); + + static PropertyValue getDefaultIconRotationAlignment(); + const PropertyValue& getIconRotationAlignment() const; + void setIconRotationAlignment(const PropertyValue&); + + static PropertyValue getDefaultIconSize(); + const PropertyValue& getIconSize() const; + void setIconSize(const PropertyValue&); + + static PropertyValue getDefaultIconTextFit(); + const PropertyValue& getIconTextFit() const; + void setIconTextFit(const PropertyValue&); + + static PropertyValue> getDefaultIconTextFitPadding(); + const PropertyValue>& getIconTextFitPadding() const; + void setIconTextFitPadding(const PropertyValue>&); + + static PropertyValue getDefaultSymbolAvoidEdges(); + const PropertyValue& getSymbolAvoidEdges() const; + void setSymbolAvoidEdges(const PropertyValue&); + + static PropertyValue getDefaultSymbolPlacement(); + const PropertyValue& getSymbolPlacement() const; + void setSymbolPlacement(const PropertyValue&); + + static PropertyValue getDefaultSymbolSortKey(); + const PropertyValue& getSymbolSortKey() const; + void setSymbolSortKey(const PropertyValue&); + + static PropertyValue getDefaultSymbolSpacing(); + const PropertyValue& getSymbolSpacing() const; + void setSymbolSpacing(const PropertyValue&); + + static PropertyValue getDefaultSymbolZOrder(); + const PropertyValue& getSymbolZOrder() const; + void setSymbolZOrder(const PropertyValue&); + + static PropertyValue getDefaultTextAllowOverlap(); + const PropertyValue& getTextAllowOverlap() const; + void setTextAllowOverlap(const PropertyValue&); + + static PropertyValue getDefaultTextAnchor(); + const PropertyValue& getTextAnchor() const; + void setTextAnchor(const PropertyValue&); + + static PropertyValue getDefaultTextField(); + const PropertyValue& getTextField() const; + void setTextField(const PropertyValue&); + + static PropertyValue> getDefaultTextFont(); + const PropertyValue>& getTextFont() const; + void setTextFont(const PropertyValue>&); + + static PropertyValue getDefaultTextIgnorePlacement(); + const PropertyValue& getTextIgnorePlacement() const; + void setTextIgnorePlacement(const PropertyValue&); + + static PropertyValue getDefaultTextJustify(); + const PropertyValue& getTextJustify() const; + void setTextJustify(const PropertyValue&); + + static PropertyValue getDefaultTextKeepUpright(); + const PropertyValue& getTextKeepUpright() const; + void setTextKeepUpright(const PropertyValue&); + + static PropertyValue getDefaultTextLetterSpacing(); + const PropertyValue& getTextLetterSpacing() const; + void setTextLetterSpacing(const PropertyValue&); + + static PropertyValue getDefaultTextLineHeight(); + const PropertyValue& getTextLineHeight() const; + void setTextLineHeight(const PropertyValue&); + + static PropertyValue getDefaultTextMaxAngle(); + const PropertyValue& getTextMaxAngle() const; + void setTextMaxAngle(const PropertyValue&); + + static PropertyValue getDefaultTextMaxWidth(); + const PropertyValue& getTextMaxWidth() const; + void setTextMaxWidth(const PropertyValue&); + + static PropertyValue> getDefaultTextOffset(); + const PropertyValue>& getTextOffset() const; + void setTextOffset(const PropertyValue>&); + + static PropertyValue getDefaultTextOptional(); + const PropertyValue& getTextOptional() const; + void setTextOptional(const PropertyValue&); + + static PropertyValue getDefaultTextPadding(); + const PropertyValue& getTextPadding() const; + void setTextPadding(const PropertyValue&); + + static PropertyValue getDefaultTextPitchAlignment(); + const PropertyValue& getTextPitchAlignment() const; + void setTextPitchAlignment(const PropertyValue&); + + static PropertyValue getDefaultTextRadialOffset(); + const PropertyValue& getTextRadialOffset() const; + void setTextRadialOffset(const PropertyValue&); + + static PropertyValue getDefaultTextRotate(); + const PropertyValue& getTextRotate() const; + void setTextRotate(const PropertyValue&); + + static PropertyValue getDefaultTextRotationAlignment(); + const PropertyValue& getTextRotationAlignment() const; + void setTextRotationAlignment(const PropertyValue&); + + static PropertyValue getDefaultTextSize(); + const PropertyValue& getTextSize() const; + void setTextSize(const PropertyValue&); + + static PropertyValue getDefaultTextTransform(); + const PropertyValue& getTextTransform() const; + void setTextTransform(const PropertyValue&); + + static PropertyValue> getDefaultTextVariableAnchor(); + const PropertyValue>& getTextVariableAnchor() const; + void setTextVariableAnchor(const PropertyValue>&); + + static PropertyValue> getDefaultTextWritingMode(); + const PropertyValue>& getTextWritingMode() const; + void setTextWritingMode(const PropertyValue>&); + + // Paint properties + + static PropertyValue getDefaultIconColor(); + const PropertyValue& getIconColor() const; + void setIconColor(const PropertyValue&); + void setIconColorTransition(const TransitionOptions&); + TransitionOptions getIconColorTransition() const; + + static PropertyValue getDefaultIconHaloBlur(); + const PropertyValue& getIconHaloBlur() const; + void setIconHaloBlur(const PropertyValue&); + void setIconHaloBlurTransition(const TransitionOptions&); + TransitionOptions getIconHaloBlurTransition() const; + + static PropertyValue getDefaultIconHaloColor(); + const PropertyValue& getIconHaloColor() const; + void setIconHaloColor(const PropertyValue&); + void setIconHaloColorTransition(const TransitionOptions&); + TransitionOptions getIconHaloColorTransition() const; + + static PropertyValue getDefaultIconHaloWidth(); + const PropertyValue& getIconHaloWidth() const; + void setIconHaloWidth(const PropertyValue&); + void setIconHaloWidthTransition(const TransitionOptions&); + TransitionOptions getIconHaloWidthTransition() const; + + static PropertyValue getDefaultIconOpacity(); + const PropertyValue& getIconOpacity() const; + void setIconOpacity(const PropertyValue&); + void setIconOpacityTransition(const TransitionOptions&); + TransitionOptions getIconOpacityTransition() const; + + static PropertyValue> getDefaultIconTranslate(); + const PropertyValue>& getIconTranslate() const; + void setIconTranslate(const PropertyValue>&); + void setIconTranslateTransition(const TransitionOptions&); + TransitionOptions getIconTranslateTransition() const; + + static PropertyValue getDefaultIconTranslateAnchor(); + const PropertyValue& getIconTranslateAnchor() const; + void setIconTranslateAnchor(const PropertyValue&); + void setIconTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getIconTranslateAnchorTransition() const; + + static PropertyValue getDefaultTextColor(); + const PropertyValue& getTextColor() const; + void setTextColor(const PropertyValue&); + void setTextColorTransition(const TransitionOptions&); + TransitionOptions getTextColorTransition() const; + + static PropertyValue getDefaultTextHaloBlur(); + const PropertyValue& getTextHaloBlur() const; + void setTextHaloBlur(const PropertyValue&); + void setTextHaloBlurTransition(const TransitionOptions&); + TransitionOptions getTextHaloBlurTransition() const; + + static PropertyValue getDefaultTextHaloColor(); + const PropertyValue& getTextHaloColor() const; + void setTextHaloColor(const PropertyValue&); + void setTextHaloColorTransition(const TransitionOptions&); + TransitionOptions getTextHaloColorTransition() const; + + static PropertyValue getDefaultTextHaloWidth(); + const PropertyValue& getTextHaloWidth() const; + void setTextHaloWidth(const PropertyValue&); + void setTextHaloWidthTransition(const TransitionOptions&); + TransitionOptions getTextHaloWidthTransition() const; + + static PropertyValue getDefaultTextOpacity(); + const PropertyValue& getTextOpacity() const; + void setTextOpacity(const PropertyValue&); + void setTextOpacityTransition(const TransitionOptions&); + TransitionOptions getTextOpacityTransition() const; + + static PropertyValue> getDefaultTextTranslate(); + const PropertyValue>& getTextTranslate() const; + void setTextTranslate(const PropertyValue>&); + void setTextTranslateTransition(const TransitionOptions&); + TransitionOptions getTextTranslateTransition() const; + + static PropertyValue getDefaultTextTranslateAnchor(); + const PropertyValue& getTextTranslateAnchor() const; + void setTextTranslateAnchor(const PropertyValue&); + void setTextTranslateAnchorTransition(const TransitionOptions&); + TransitionOptions getTextTranslateAnchorTransition() const; + + // Private implementation + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + SymbolLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + +protected: + // Dynamic properties + optional setPropertyInternal(const std::string& name, const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/light.hpp b/mapbox-gl-native/include/mbgl/style/light.hpp new file mode 100644 index 000000000..dc54f9807 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/light.hpp @@ -0,0 +1,64 @@ +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class LightObserver; + +class Light { +public: + Light(); + ~Light(); + + // Dynamic properties + optional setProperty(const std::string& name, const conversion::Convertible& value); + StyleProperty getProperty(const std::string&) const; + + static LightAnchorType getDefaultAnchor(); + PropertyValue getAnchor() const; + void setAnchor(PropertyValue); + void setAnchorTransition(const TransitionOptions&); + TransitionOptions getAnchorTransition() const; + + static Color getDefaultColor(); + PropertyValue getColor() const; + void setColor(PropertyValue); + void setColorTransition(const TransitionOptions&); + TransitionOptions getColorTransition() const; + + static float getDefaultIntensity(); + PropertyValue getIntensity() const; + void setIntensity(PropertyValue); + void setIntensityTransition(const TransitionOptions&); + TransitionOptions getIntensityTransition() const; + + static Position getDefaultPosition(); + PropertyValue getPosition() const; + void setPosition(PropertyValue); + void setPositionTransition(const TransitionOptions&); + TransitionOptions getPositionTransition() const; + + class Impl; + Immutable impl; + explicit Light(Immutable); + Mutable mutableImpl() const; + + LightObserver* observer = nullptr; + void setObserver(LightObserver*); +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/light.hpp.ejs b/mapbox-gl-native/include/mbgl/style/light.hpp.ejs new file mode 100644 index 000000000..071647965 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/light.hpp.ejs @@ -0,0 +1,51 @@ +<% + const properties = locals.properties; +-%> +// clang-format off + +// This file is generated. Do not edit. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class LightObserver; + +class Light { +public: + Light(); + ~Light(); + + // Dynamic properties + optional setProperty(const std::string& name, const conversion::Convertible& value); + StyleProperty getProperty(const std::string&) const; + +<% for (const property of properties) { -%> + static <%- evaluatedType(property) %> getDefault<%- camelize(property.name) %>(); + <%- propertyValueType(property) %> get<%- camelize(property.name) %>() const; + void set<%- camelize(property.name) %>(<%- propertyValueType(property) %>); + void set<%- camelize(property.name) %>Transition(const TransitionOptions&); + TransitionOptions get<%- camelize(property.name) %>Transition() const; + +<% } -%> + class Impl; + Immutable impl; + explicit Light(Immutable); + Mutable mutableImpl() const; + + LightObserver* observer = nullptr; + void setObserver(LightObserver*); +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/mapbox-gl-native/include/mbgl/style/position.hpp b/mapbox-gl-native/include/mbgl/style/position.hpp new file mode 100644 index 000000000..cf8db73cd --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/position.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace style { +class Position { +public: + Position() = default; + Position(std::array& position_) + : radial(position_[0]), azimuthal(position_[1]), polar(position_[2]) { + calculateCartesian(); + }; + + friend bool operator==(const Position& lhs, const Position& rhs) { + return lhs.radial == rhs.radial && lhs.azimuthal == rhs.azimuthal && lhs.polar == rhs.polar; + // TODO this doesn't address wrapping, which would be better addressed by comparing cartesian coordinates but being calculated floats are ont to be trusted. + } + + friend bool operator!=(const Position& lhs, const Position& rhs) { + return !(lhs == rhs); + } + + std::array getCartesian() const { return {{x, y, z}}; }; + + std::array getSpherical() const { return {{radial, azimuthal, polar}}; }; + + void set(std::array& position_) { + radial = position_[0]; + azimuthal = position_[1]; + polar = position_[2]; + calculateCartesian(); + }; + + // Utility function to be used only during interpolation; this leaves spherical coordinates undefined. + void setCartesian(std::array& position_) { + x = position_[0]; + y = position_[1]; + z = position_[2]; + } + +private: + float radial; + float azimuthal; + float polar; + float x; + float y; + float z; + + void calculateCartesian() { + // We abstract "north"/"up" (compass-wise) to be 0° when really this is 90° (π/2): we + // correct for that here + const float _a = (azimuthal + 90) * util::DEG2RAD; + const float _p = polar * util::DEG2RAD; + + x = radial * std::cos(_a) * std::sin(_p); + y = radial * std::sin(_a) * std::sin(_p); + z = radial * std::cos(_p); + }; +}; +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/property_expression.hpp b/mapbox-gl-native/include/mbgl/style/property_expression.hpp new file mode 100644 index 000000000..95f3cbab2 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/property_expression.hpp @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class PropertyExpressionBase { +public: + explicit PropertyExpressionBase(std::unique_ptr); + + bool isZoomConstant() const noexcept; + bool isFeatureConstant() const noexcept; + bool isRuntimeConstant() const noexcept; + float interpolationFactor(const Range&, float) const noexcept; + Range getCoveringStops(float, float) const noexcept; + const expression::Expression& getExpression() const noexcept; + + // Can be used for aggregating property expressions from multiple + // properties(layers) into single match / case expression. Method may + // be removed if a better way of aggregation is found. + std::shared_ptr getSharedExpression() const noexcept; + + bool useIntegerZoom = false; + +protected: + std::shared_ptr expression; + variant zoomCurve; + bool isZoomConstant_; + bool isFeatureConstant_; + bool isRuntimeConstant_; +}; + +template +class PropertyExpression final : public PropertyExpressionBase { +public: + // Second parameter to be used only for conversions from legacy functions. + PropertyExpression(std::unique_ptr expression_, optional defaultValue_ = nullopt) + : PropertyExpressionBase(std::move(expression_)), + defaultValue(std::move(defaultValue_)) { + } + + T evaluate(const expression::EvaluationContext& context, T finalDefaultValue = T()) const { + const expression::EvaluationResult result = expression->evaluate(context); + if (result) { + const optional typed = expression::fromExpressionValue(*result); + return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue; + } + return defaultValue ? *defaultValue : finalDefaultValue; + } + + T evaluate(float zoom) const { + assert(!isZoomConstant()); + assert(isFeatureConstant()); + return evaluate(expression::EvaluationContext(zoom)); + } + + T evaluate(const GeometryTileFeature& feature, T finalDefaultValue) const { + assert(isZoomConstant()); + assert(!isFeatureConstant()); + return evaluate(expression::EvaluationContext(&feature), finalDefaultValue); + } + + T evaluate(const GeometryTileFeature& feature, + const std::set& availableImages, + T finalDefaultValue) const { + return evaluate(expression::EvaluationContext(&feature).withAvailableImages(&availableImages), + finalDefaultValue); + } + + T evaluate(const GeometryTileFeature& feature, const CanonicalTileID& canonical, T finalDefaultValue) const { + return evaluate(expression::EvaluationContext(&feature).withCanonicalTileID(&canonical), finalDefaultValue); + } + + T evaluate(const GeometryTileFeature& feature, + const std::set& availableImages, + const CanonicalTileID& canonical, + T finalDefaultValue) const { + return evaluate(expression::EvaluationContext(&feature) + .withAvailableImages(&availableImages) + .withCanonicalTileID(&canonical), + finalDefaultValue); + } + + T evaluate(float zoom, const GeometryTileFeature& feature, T finalDefaultValue) const { + return evaluate(expression::EvaluationContext(zoom, &feature), finalDefaultValue); + } + + T evaluate(float zoom, + const GeometryTileFeature& feature, + const std::set& availableImages, + T finalDefaultValue) const { + return evaluate(expression::EvaluationContext(zoom, &feature).withAvailableImages(&availableImages), + finalDefaultValue); + } + + T evaluate(float zoom, + const GeometryTileFeature& feature, + const std::set& availableImages, + const CanonicalTileID& canonical, + T finalDefaultValue) const { + return evaluate(expression::EvaluationContext(zoom, &feature) + .withAvailableImages(&availableImages) + .withCanonicalTileID(&canonical), + finalDefaultValue); + } + + T evaluate(float zoom, + const GeometryTileFeature& feature, + const CanonicalTileID& canonical, + T finalDefaultValue) const { + return evaluate(expression::EvaluationContext(zoom, &feature).withCanonicalTileID(&canonical), + finalDefaultValue); + } + + T evaluate(float zoom, const GeometryTileFeature& feature, const FeatureState& state, T finalDefaultValue) const { + assert(!isFeatureConstant()); + return evaluate(expression::EvaluationContext(zoom, &feature, &state), finalDefaultValue); + } + + std::vector> possibleOutputs() const { + return expression::fromExpressionValues(expression->possibleOutputs()); + } + + friend bool operator==(const PropertyExpression& lhs, + const PropertyExpression& rhs) { + return *lhs.expression == *rhs.expression; + } + +private: + optional defaultValue; +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/property_value.hpp b/mapbox-gl-native/include/mbgl/style/property_value.hpp new file mode 100644 index 000000000..86812e230 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/property_value.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { + +template +class PropertyValue { +private: + using Value = variant< + Undefined, + T, + PropertyExpression>; + + Value value; + + friend bool operator==(const PropertyValue& lhs, + const PropertyValue& rhs) { + return lhs.value == rhs.value; + } + + friend bool operator!=(const PropertyValue& lhs, + const PropertyValue& rhs) { + return !(lhs == rhs); + } + +public: + PropertyValue() = default; + + PropertyValue(T constant) + : value(std::move(constant)) {} + + PropertyValue(PropertyExpression expression) + : value(std::move(expression)) {} + + bool isUndefined() const { + return value.template is(); + } + + bool isConstant() const { + return value.template is(); + } + + bool isExpression() const { + return value.template is>(); + } + + bool isDataDriven() const { + return value.match( + [] (const Undefined&) { return false; }, + [] (const T&) { return false; }, + [] (const PropertyExpression& fn) { return !fn.isFeatureConstant(); } + ); + } + + bool isZoomConstant() const { + return value.match( + [] (const Undefined&) { return true; }, + [] (const T&) { return true; }, + [] (const PropertyExpression& fn) { return fn.isZoomConstant(); } + ); + } + + const T& asConstant() const { + return value.template get(); + } + + const PropertyExpression& asExpression() const { + return value.template get>(); + } + + template + auto match(Ts&&... ts) const { + return value.match(std::forward(ts)...); + } + + template + auto evaluate(const Evaluator& evaluator, TimePoint = {}) const { + return Value::visit(value, evaluator); + } + + bool hasDataDrivenPropertyDifference(const PropertyValue& other) const { + return *this != other && (isDataDriven() || other.isDataDriven()); + } +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/rotation.hpp b/mapbox-gl-native/include/mbgl/style/rotation.hpp new file mode 100644 index 000000000..96b047c68 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/rotation.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +namespace mbgl { +namespace style { + +// Could be made a template class if needed +// template +// size_t period() const noexcept { return Period; } +class Rotation { +public: + Rotation() = default; + Rotation(double angle_) : angle(mbgl::util::wrap(angle_, 0, period())) {} + constexpr double period() const noexcept { return 360.0; } + double getAngle() const noexcept { return angle; } + + friend bool operator==(const Rotation& lhs, const Rotation& rhs) { return lhs.angle == rhs.angle; } + + friend bool operator!=(const Rotation& lhs, const Rotation& rhs) { return !(lhs == rhs); } + +private: + double angle; +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/source.hpp b/mapbox-gl-native/include/mbgl/style/source.hpp new file mode 100644 index 000000000..eada63d12 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/source.hpp @@ -0,0 +1,125 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace mbgl { + +class FileSource; + +namespace style { + +class VectorSource; +class RasterSource; +class RasterDEMSource; +class GeoJSONSource; +class SourceObserver; +struct LayerTypeInfo; + +/** + * The runtime representation of a [source](https://www.mapbox.com/mapbox-gl-style-spec/#sources) from the Mapbox Style + * Specification. + * + * `Source` is an abstract base class; concrete derived classes are provided for each source type. `Source` contains + * functionality that is common to all layer types: + * + * * Runtime type information: type predicates and casting + * * Accessors for properties common to all source types: ID, etc. + * * Cloning and copying + * + * All other functionality lives in the derived classes. To instantiate a source, create an instance of the desired + * type, passing the ID: + * + * auto vectorSource = std::make_unique("my-vector-source"); + */ +class Source { +public: + Source(const Source&) = delete; + Source& operator=(const Source&) = delete; + + virtual ~Source(); + + // Check whether this source is of the given subtype. + template + bool is() const; + + // Dynamically cast this source to the given subtype. + template + T* as() { + return is() ? reinterpret_cast(this) : nullptr; + } + + template + const T* as() const { + return is() ? reinterpret_cast(this) : nullptr; + } + + SourceType getType() const; + std::string getID() const; + optional getAttribution() const; + + // The data from the volatile sources are not stored in a persistent storage. + bool isVolatile() const noexcept; + void setVolatile(bool) noexcept; + + // Private implementation + class Impl; + Immutable baseImpl; + + void setObserver(SourceObserver*); + SourceObserver* observer = nullptr; + + virtual void loadDescription(FileSource&) = 0; + void setPrefetchZoomDelta(optional delta) noexcept; + optional getPrefetchZoomDelta() const noexcept; + + // If the given source supports loading tiles from a server, + // sets the minimum tile update interval. + // Update network requests that are more frequent than the + // minimum tile update interval are suppressed. + // + // Default value is `Duration::zero()`. + void setMinimumTileUpdateInterval(Duration) noexcept; + Duration getMinimumTileUpdateInterval() const noexcept; + + // Sets a limit for how much a parent tile can be overscaled. + // + // When a set of tiles for a current zoom level is being rendered and some of the + // ideal tiles that cover the screen are not yet loaded, parent tile could be + // used instead. This might introduce unwanted rendering side-effects, especially + // for raster tiles that are overscaled multiple times. + // + // For example, an overscale factor of 3 would mean that on zoom level 3, the + // minimum zoom level of a parent tile that could be used in place of an ideal + // tile during rendering would be zoom 0. By default, no limit is set, so any + // parent tile may be used. + void setMaxOverscaleFactorForParentTiles(optional overscaleFactor) noexcept; + optional getMaxOverscaleFactorForParentTiles() const noexcept; + void dumpDebugLogs() const; + + virtual bool supportsLayerType(const mbgl::style::LayerTypeInfo*) const = 0; + + bool loaded = false; + + // For use in SDK bindings, which store a reference to a platform-native peer + // object here, so that separately-obtained references to this object share + // identical platform-native peers. + mapbox::base::TypeWrapper peer; + + virtual mapbox::base::WeakPtr makeWeakPtr() = 0; + +protected: + explicit Source(Immutable); + virtual Mutable createMutable() const noexcept = 0; +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/sources/custom_geometry_source.hpp b/mapbox-gl-native/include/mbgl/style/sources/custom_geometry_source.hpp new file mode 100644 index 000000000..250886c20 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/sources/custom_geometry_source.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { + +class OverscaledTileID; +class CanonicalTileID; +template +class Actor; +class ThreadPool; + +namespace style { + +using TileFunction = std::function; + +class CustomTileLoader; + +class CustomGeometrySource final : public Source { +public: + struct TileOptions { + double tolerance = 0.375; + uint16_t tileSize = util::tileSize; + uint16_t buffer = 128; + bool clip = false; + bool wrap = false; + }; + + struct Options { + TileFunction fetchTileFunction; + TileFunction cancelTileFunction; + Range zoomRange = { 0, 18}; + TileOptions tileOptions; + }; +public: + CustomGeometrySource(std::string id, const CustomGeometrySource::Options& options); + ~CustomGeometrySource() final; + void loadDescription(FileSource&) final; + void setTileData(const CanonicalTileID&, const GeoJSON&); + void invalidateTile(const CanonicalTileID&); + void invalidateRegion(const LatLngBounds&); + // Private implementation + class Impl; + const Impl& impl() const; + bool supportsLayerType(const mbgl::style::LayerTypeInfo*) const override; + mapbox::base::WeakPtr makeWeakPtr() override { + return weakFactory.makeWeakPtr(); + } + +protected: + Mutable createMutable() const noexcept final; + +private: + std::shared_ptr threadPool; + std::unique_ptr> loader; + mapbox::base::WeakPtrFactory weakFactory {this}; +}; + +template <> +inline bool Source::is() const { + return getType() == SourceType::CustomVector; +} + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/sources/geojson_source.hpp b/mapbox-gl-native/include/mbgl/style/sources/geojson_source.hpp new file mode 100644 index 000000000..b728b340e --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/sources/geojson_source.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +class AsyncRequest; +class Scheduler; +namespace style { + +struct GeoJSONOptions { + // GeoJSON-VT options + uint8_t minzoom = 0; + uint8_t maxzoom = 18; + uint16_t tileSize = util::tileSize; + uint16_t buffer = 128; + double tolerance = 0.375; + bool lineMetrics = false; + + // Supercluster options + bool cluster = false; + uint16_t clusterRadius = 50; + uint8_t clusterMaxZoom = 17; + using ClusterExpression = std::pair, + std::shared_ptr>; + using ClusterProperties = std::map; + ClusterProperties clusterProperties; + + static Immutable defaultOptions(); +}; +class GeoJSONData { +public: + using TileFeatures = mapbox::feature::feature_collection; + using Features = mapbox::feature::feature_collection; + static std::shared_ptr create(const GeoJSON&, + const Immutable& = GeoJSONOptions::defaultOptions(), + std::shared_ptr scheduler = nullptr); + + virtual ~GeoJSONData() = default; + virtual void getTile(const CanonicalTileID&, const std::function&) = 0; + + // SuperclusterData + virtual Features getChildren(std::uint32_t) = 0; + virtual Features getLeaves(std::uint32_t, std::uint32_t limit, std::uint32_t offset) = 0; + virtual std::uint8_t getClusterExpansionZoom(std::uint32_t) = 0; + + virtual std::shared_ptr getScheduler() { return nullptr; } +}; + +class GeoJSONSource final : public Source { +public: + GeoJSONSource(std::string id, Immutable = GeoJSONOptions::defaultOptions()); + ~GeoJSONSource() final; + + void setURL(const std::string& url); + void setGeoJSON(const GeoJSON&); + void setGeoJSONData(std::shared_ptr); + + optional getURL() const; + const GeoJSONOptions& getOptions() const; + + class Impl; + const Impl& impl() const; + + void loadDescription(FileSource&) final; + + bool supportsLayerType(const mbgl::style::LayerTypeInfo*) const override; + + mapbox::base::WeakPtr makeWeakPtr() override { + return weakFactory.makeWeakPtr(); + } + +protected: + Mutable createMutable() const noexcept final; + +private: + optional url; + std::unique_ptr req; + std::shared_ptr threadPool; + mapbox::base::WeakPtrFactory weakFactory {this}; +}; + +template <> +inline bool Source::is() const { + return getType() == SourceType::GeoJSON; +} + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/sources/image_source.hpp b/mapbox-gl-native/include/mbgl/style/sources/image_source.hpp new file mode 100644 index 000000000..32971ef3d --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/sources/image_source.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +class LatLng; +class AsyncRequest; + +namespace style { + +class ImageSource final : public Source { +public: + ImageSource(std::string id, std::array); + ~ImageSource() override; + + optional getURL() const; + void setURL(const std::string& url); + + void setImage(PremultipliedImage&&); + + void setCoordinates(const std::array&); + std::array getCoordinates() const; + + class Impl; + const Impl& impl() const; + + void loadDescription(FileSource&) final; + + bool supportsLayerType(const mbgl::style::LayerTypeInfo*) const override; + + mapbox::base::WeakPtr makeWeakPtr() override { + return weakFactory.makeWeakPtr(); + } + +protected: + Mutable createMutable() const noexcept final; + +private: + optional url; + std::unique_ptr req; + mapbox::base::WeakPtrFactory weakFactory {this}; +}; + +template <> +inline bool Source::is() const { + return getType() == SourceType::Image; +} + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/sources/raster_dem_source.hpp b/mapbox-gl-native/include/mbgl/style/sources/raster_dem_source.hpp new file mode 100644 index 000000000..42e27cd07 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/sources/raster_dem_source.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + +class AsyncRequest; + +namespace style { + +class RasterDEMSource : public RasterSource { +public: + RasterDEMSource(std::string id, variant urlOrTileset, uint16_t tileSize); + bool supportsLayerType(const mbgl::style::LayerTypeInfo*) const override; +}; + +template <> +inline bool Source::is() const { + return getType() == SourceType::RasterDEM; +} + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/sources/raster_source.hpp b/mapbox-gl-native/include/mbgl/style/sources/raster_source.hpp new file mode 100644 index 000000000..e06c3404e --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/sources/raster_source.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + +class AsyncRequest; + +namespace style { + +class RasterSource : public Source { +public: + RasterSource(std::string id, variant urlOrTileset, uint16_t tileSize, SourceType sourceType = SourceType::Raster); + ~RasterSource() override; + + const variant& getURLOrTileset() const; + optional getURL() const; + + uint16_t getTileSize() const; + + class Impl; + const Impl& impl() const; + + void loadDescription(FileSource&) final; + + bool supportsLayerType(const mbgl::style::LayerTypeInfo*) const override; + + mapbox::base::WeakPtr makeWeakPtr() final { + return weakFactory.makeWeakPtr(); + } + +protected: + Mutable createMutable() const noexcept final; + +private: + const variant urlOrTileset; + std::unique_ptr req; + mapbox::base::WeakPtrFactory weakFactory {this}; +}; + +template <> +inline bool Source::is() const { + return getType() == SourceType::Raster; +} + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/sources/vector_source.hpp b/mapbox-gl-native/include/mbgl/style/sources/vector_source.hpp new file mode 100644 index 000000000..83fcae95d --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/sources/vector_source.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + +class AsyncRequest; + +namespace style { + +class VectorSource final : public Source { +public: + VectorSource(std::string id, variant urlOrTileset, optional maxZoom = nullopt, + optional minZoom = nullopt); + ~VectorSource() final; + + const variant& getURLOrTileset() const; + optional getURL() const; + + class Impl; + const Impl& impl() const; + + void loadDescription(FileSource&) final; + + bool supportsLayerType(const mbgl::style::LayerTypeInfo*) const override; + + mapbox::base::WeakPtr makeWeakPtr() override { + return weakFactory.makeWeakPtr(); + } + +protected: + Mutable createMutable() const noexcept final; + +private: + const variant urlOrTileset; + std::unique_ptr req; + mapbox::base::WeakPtrFactory weakFactory {this}; + optional maxZoom; + optional minZoom; +}; + +template <> +inline bool Source::is() const { + return getType() == SourceType::Vector; +} + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/style.hpp b/mapbox-gl-native/include/mbgl/style/style.hpp new file mode 100644 index 000000000..c5e8d7f15 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/style.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +class FileSource; + +namespace style { + +class Light; +class Source; +class Layer; + +class Style { +public: + Style(std::shared_ptr, float pixelRatio); + ~Style(); + + void loadJSON(const std::string&); + void loadURL(const std::string&); + + std::string getJSON() const; + std::string getURL() const; + + // Defaults + std::string getName() const; + CameraOptions getDefaultCamera() const; + + // TransitionOptions + TransitionOptions getTransitionOptions() const; + void setTransitionOptions(const TransitionOptions&); + + // Light + Light* getLight(); + const Light* getLight() const; + + void setLight(std::unique_ptr); + + // Images + optional getImage(const std::string&) const; + void addImage(std::unique_ptr); + void removeImage(const std::string&); + + // Sources + std::vector< Source*> getSources(); + std::vector getSources() const; + + Source* getSource(const std::string&); + const Source* getSource(const std::string&) const; + + void addSource(std::unique_ptr); + std::unique_ptr removeSource(const std::string& sourceID); + + // Layers + std::vector< Layer*> getLayers(); + std::vector getLayers() const; + + Layer* getLayer(const std::string&); + const Layer* getLayer(const std::string&) const; + + void addLayer(std::unique_ptr, const optional& beforeLayerID = {}); + std::unique_ptr removeLayer(const std::string& layerID); + + // Private implementation + class Impl; + const std::unique_ptr impl; +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/style_property.hpp b/mapbox-gl-native/include/mbgl/style/style_property.hpp new file mode 100644 index 000000000..fc34078de --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/style_property.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace mbgl { +namespace style { + +/** + * @brief Generic representation of a style property. + */ +class StyleProperty { +public: + enum class Kind : uint8_t { Undefined, Constant, Expression, Transition }; + StyleProperty(Value value_, Kind kind_) : value(std::move(value_)), kind(kind_) {} + StyleProperty() = default; + const Value& getValue() const { return value; } + Value& getValue() { return value; } + Kind getKind() const { return kind; } + +private: + Value value; + Kind kind = Kind::Undefined; +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/transition_options.hpp b/mapbox-gl-native/include/mbgl/style/transition_options.hpp new file mode 100644 index 000000000..8ead79eb2 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/transition_options.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { +namespace style { + +class TransitionOptions { +public: + optional duration; + optional delay; + bool enablePlacementTransitions; + + TransitionOptions(optional duration_ = {}, + optional delay_ = {}, + bool enablePlacementTransitions_ = true) + : duration(std::move(duration_)), + delay(std::move(delay_)), + enablePlacementTransitions(enablePlacementTransitions_) {} + + TransitionOptions reverseMerge(const TransitionOptions& defaults) const { + return { + duration ? duration : defaults.duration, + delay ? delay : defaults.delay, + enablePlacementTransitions + }; + } + + bool isDefined() const { + return duration || delay; + } + + mapbox::base::Value serialize() const { + mapbox::base::ValueObject result; + if (duration) { + result.emplace("duration", std::chrono::duration_cast(*duration).count()); + } + if (delay) { + result.emplace("delay", std::chrono::duration_cast(*delay).count()); + } + return result; + } +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/types.hpp b/mapbox-gl-native/include/mbgl/style/types.hpp new file mode 100644 index 000000000..5d88dafb3 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/types.hpp @@ -0,0 +1,129 @@ +#pragma once + +#include + +namespace mbgl { + +namespace style { + +// TODO: should be in public source.hpp header +enum class SourceType : uint8_t { + Vector, + Raster, + RasterDEM, + GeoJSON, + Video, + Annotations, + Image, + CustomVector +}; + +enum class VisibilityType : bool { + Visible, + None, +}; + +enum class LineCapType : uint8_t { + Round, + Butt, + Square, +}; + +enum class LineJoinType : uint8_t { + Miter, + Bevel, + Round, + // the following two types are for internal use only + FakeRound, + FlipBevel +}; + +enum class RasterResamplingType : bool { + Linear, + Nearest +}; + +enum class HillshadeIlluminationAnchorType : bool { + Map, + Viewport +}; + +enum class TranslateAnchorType : bool { + Map, + Viewport +}; + +enum class RotateAnchorType : bool { + Map, + Viewport, +}; + +enum class CirclePitchScaleType : bool { + Map, + Viewport, +}; + +enum class SymbolPlacementType : uint8_t { + Point, + Line, + LineCenter +}; + +enum class SymbolZOrderType : uint8_t { + Auto, + ViewportY, + Source +}; + +enum class AlignmentType : uint8_t { + Map, + Viewport, + Auto, +}; + +enum class TextJustifyType : uint8_t { + Auto, + Center, + Left, + Right +}; + +enum class SymbolAnchorType : uint8_t { + Center, + Left, + Right, + Top, + Bottom, + TopLeft, + TopRight, + BottomLeft, + BottomRight +}; + +using TextVariableAnchorType = SymbolAnchorType; + +enum class TextTransformType : uint8_t { + None, + Uppercase, + Lowercase, +}; + +enum class IconTextFitType : uint8_t { + None, + Both, + Width, + Height +}; + +enum class TextWritingModeType : uint8_t { + Horizontal, + Vertical +}; + +enum class LightAnchorType: bool { + Map, + Viewport +}; + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/style/undefined.hpp b/mapbox-gl-native/include/mbgl/style/undefined.hpp new file mode 100644 index 000000000..e43f132a8 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/style/undefined.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace mbgl { +namespace style { + +class Undefined {}; + +inline bool operator==(const Undefined&, const Undefined&) { return true; } +inline bool operator!=(const Undefined&, const Undefined&) { return false; } + +} // namespace style +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/tile/tile_id.hpp b/mapbox-gl-native/include/mbgl/tile/tile_id.hpp new file mode 100644 index 000000000..c3bdbea5c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/tile/tile_id.hpp @@ -0,0 +1,273 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +class OverscaledTileID; +class CanonicalTileID; +class UnwrappedTileID; + +// Has integer z/x/y coordinates +// All tiles must be derived from 0/0/0 (=no tiles outside of the main tile pyramid) +// Used for requesting data; represents data tiles that exist out there. +// z is never larger than the source's maxzoom +class CanonicalTileID { +public: + CanonicalTileID(uint8_t z, uint32_t x, uint32_t y); + bool operator==(const CanonicalTileID&) const; + bool operator!=(const CanonicalTileID&) const; + bool operator<(const CanonicalTileID&) const; + bool isChildOf(const CanonicalTileID&) const; + CanonicalTileID scaledTo(uint8_t z) const; + std::array children() const; + + uint8_t z; + uint32_t x; + uint32_t y; +}; + +::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs); +namespace util { +std::string toString(const CanonicalTileID&); +} // namespace util + +// Has integer z/x/y coordinates +// overscaledZ describes the zoom level this tile is intented to represent, e.g. when parsing data +// z is never larger than the source's maxzoom +// z/x/y describe the +class OverscaledTileID { +public: + OverscaledTileID(uint8_t overscaledZ, int16_t wrap, CanonicalTileID); + OverscaledTileID(uint8_t overscaledZ, int16_t wrap, uint8_t z, uint32_t x, uint32_t y); + OverscaledTileID(uint8_t z, uint32_t x, uint32_t y); + explicit OverscaledTileID(const CanonicalTileID&); + explicit OverscaledTileID(CanonicalTileID&&); + bool operator==(const OverscaledTileID&) const; + bool operator!=(const OverscaledTileID&) const; + bool operator<(const OverscaledTileID&) const; + bool isChildOf(const OverscaledTileID&) const; + uint32_t overscaleFactor() const; + OverscaledTileID scaledTo(uint8_t z) const; + UnwrappedTileID toUnwrapped() const; + OverscaledTileID unwrapTo(int16_t wrap) const; + + uint8_t overscaledZ; + int16_t wrap; + CanonicalTileID canonical; +}; + +::std::ostream& operator<<(::std::ostream& os, const OverscaledTileID& rhs); +namespace util { +std::string toString(const OverscaledTileID&); +} // namespace util + +// Has integer z/x/y coordinates +// wrap describes tiles that are left/right of the main tile pyramid, e.g. when wrapping the world +// Used for describing what position tiles are getting rendered at (= calc the matrix) +// z is never larger than the source's maxzoom +class UnwrappedTileID { +public: + UnwrappedTileID(uint8_t z, int64_t x, int64_t y); + UnwrappedTileID(int16_t wrap, CanonicalTileID); + bool operator==(const UnwrappedTileID&) const; + bool operator!=(const UnwrappedTileID&) const; + bool operator<(const UnwrappedTileID&) const; + bool isChildOf(const UnwrappedTileID&) const; + std::array children() const; + OverscaledTileID overscaleTo(uint8_t z) const; + float pixelsToTileUnits(float pixelValue, float zoom) const; + UnwrappedTileID unwrapTo(int16_t wrap) const; + + int16_t wrap; + CanonicalTileID canonical; +}; + +::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs); +namespace util { +std::string toString(const UnwrappedTileID&); +} // namespace util + +inline CanonicalTileID::CanonicalTileID(uint8_t z_, uint32_t x_, uint32_t y_) : z(z_), x(x_), y(y_) { + assert(z <= 32); + assert(x < (1ull << z)); + assert(y < (1ull << z)); +} + +inline bool CanonicalTileID::operator==(const CanonicalTileID& rhs) const { + return z == rhs.z && x == rhs.x && y == rhs.y; +} + +inline bool CanonicalTileID::operator!=(const CanonicalTileID& rhs) const { + return z != rhs.z || x != rhs.x || y != rhs.y; +} + +inline bool CanonicalTileID::operator<(const CanonicalTileID& rhs) const { + return std::tie(z, x, y) < std::tie(rhs.z, rhs.x, rhs.y); +} + +inline bool CanonicalTileID::isChildOf(const CanonicalTileID& parent) const { + // We're first testing for z == 0, to avoid a 32 bit shift, which is undefined. + return parent.z == 0 || + (parent.z < z && parent.x == (x >> (z - parent.z)) && parent.y == (y >> (z - parent.z))); +} + +inline CanonicalTileID CanonicalTileID::scaledTo(uint8_t targetZ) const { + if (targetZ <= z) { + return { targetZ, x >> (z - targetZ), y >> (z - targetZ) }; // parent or same + } else { + return { targetZ, x << (targetZ - z), y << (targetZ - z) }; // child + } +} + +inline std::array CanonicalTileID::children() const { + const uint8_t childZ = z + 1; + const uint32_t childX = x * 2; + const uint32_t childY = y * 2; + return { { + { childZ, childX, childY }, + { childZ, childX, childY + 1 }, + { childZ, childX + 1, childY }, + { childZ, childX + 1, childY + 1 }, + } }; +} + +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, CanonicalTileID canonical_) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(canonical_) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, uint8_t z, uint32_t x, uint32_t y) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(z, x, y) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y) + : overscaledZ(z), wrap(0), canonical(z, x, y) { +} + +inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_) + : overscaledZ(canonical_.z), wrap(0), canonical(canonical_) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_) + : overscaledZ(canonical_.z), wrap(0), canonical(std::forward(canonical_)) { + assert(overscaledZ >= canonical.z); +} + +inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const { + return overscaledZ == rhs.overscaledZ && wrap == rhs.wrap &&canonical == rhs.canonical; +} + +inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const { + return overscaledZ != rhs.overscaledZ || wrap != rhs.wrap || canonical != rhs.canonical; +} + +inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const { + return std::tie(overscaledZ, wrap, canonical) < std::tie(rhs.overscaledZ, rhs.wrap, rhs.canonical); +} + +inline uint32_t OverscaledTileID::overscaleFactor() const { + return 1u << (overscaledZ - canonical.z); +} + +inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const { + return wrap == rhs.wrap && + overscaledZ > rhs.overscaledZ && + (canonical == rhs.canonical || canonical.isChildOf(rhs.canonical)); +} + +inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const { + return { z, wrap, z >= canonical.z ? canonical : canonical.scaledTo(z) }; +} + +inline UnwrappedTileID OverscaledTileID::toUnwrapped() const { + return { wrap, canonical }; +} + +inline OverscaledTileID OverscaledTileID::unwrapTo(int16_t newWrap) const { + return { overscaledZ, newWrap, canonical }; +} + +inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_) + : wrap((x_ < 0 ? x_ - (1ll << z_) + 1 : x_) / (1ll << z_)), + canonical( + z_, + static_cast(x_ - wrap * (1ll << z_)), + y_ < 0 ? 0 : std::min(static_cast(y_), static_cast(1ull << z_) - 1)) { +} + +inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID canonical_) + : wrap(wrap_), canonical(canonical_) {} + +inline bool UnwrappedTileID::operator==(const UnwrappedTileID& rhs) const { + return wrap == rhs.wrap && canonical == rhs.canonical; +} + +inline bool UnwrappedTileID::operator!=(const UnwrappedTileID& rhs) const { + return wrap != rhs.wrap || canonical != rhs.canonical; +} + +inline bool UnwrappedTileID::operator<(const UnwrappedTileID& rhs) const { + return std::tie(wrap, canonical) < std::tie(rhs.wrap, rhs.canonical); +} + +inline UnwrappedTileID UnwrappedTileID::unwrapTo(int16_t newWrap) const { + return { newWrap, canonical }; +} + +inline bool UnwrappedTileID::isChildOf(const UnwrappedTileID& parent) const { + return wrap == parent.wrap && canonical.isChildOf(parent.canonical); +} + +inline std::array UnwrappedTileID::children() const { + const uint8_t childZ = canonical.z + 1; + const uint32_t childX = canonical.x * 2; + const uint32_t childY = canonical.y * 2; + return { { + { wrap, { childZ, childX, childY } }, + { wrap, { childZ, childX, childY + 1 } }, + { wrap, { childZ, childX + 1, childY } }, + { wrap, { childZ, childX + 1, childY + 1 } }, + } }; +} + +inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const { + assert(overscaledZ >= canonical.z); + return { overscaledZ, wrap, canonical }; +} + +inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const { + return pixelValue * (util::EXTENT / (util::tileSize * std::pow(2, zoom - canonical.z))); +} + +} // namespace mbgl + +namespace std { + +template <> +struct hash { + size_t operator()(const mbgl::CanonicalTileID& id) const; +}; + +template <> +struct hash { + size_t operator()(const mbgl::UnwrappedTileID& id) const; +}; + +template <> +struct hash { + size_t operator()(const mbgl::OverscaledTileID& id) const; +}; + +} // namespace std + diff --git a/mapbox-gl-native/include/mbgl/tile/tile_necessity.hpp b/mapbox-gl-native/include/mbgl/tile/tile_necessity.hpp new file mode 100644 index 000000000..e51bf51d1 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/tile/tile_necessity.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace mbgl { + +// Tiles can have two states: optional or required. +// - optional means that only low-cost actions should be taken to obtain the data +// (e.g. load from cache, but accept stale data) +// - required means that every effort should be taken to obtain the data (e.g. load +// from internet and keep the data fresh if it expires) +enum class TileNecessity : bool { + Optional = false, + Required = true, +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/async_request.hpp b/mapbox-gl-native/include/mbgl/util/async_request.hpp new file mode 100644 index 000000000..9e8509496 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/async_request.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace mbgl { + +class AsyncRequest : private util::noncopyable { +public: + virtual ~AsyncRequest() = default; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/async_task.hpp b/mapbox-gl-native/include/mbgl/util/async_task.hpp new file mode 100644 index 000000000..69746c3eb --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/async_task.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { +namespace util { + +class AsyncTask : private util::noncopyable { +public: + AsyncTask(std::function&&); + ~AsyncTask(); + + void send(); + +private: + class Impl; + std::unique_ptr impl; +}; + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/bitmask_operations.hpp b/mapbox-gl-native/include/mbgl/util/bitmask_operations.hpp new file mode 100644 index 000000000..014fabdea --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/bitmask_operations.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace mbgl { + +template +MBGL_CONSTEXPR Enum operator|(Enum a, Enum b) { + static_assert(std::is_enum::value, "Enum must be an enum type"); + return Enum(mbgl::underlying_type(a) | mbgl::underlying_type(b)); +} + +template +MBGL_CONSTEXPR Enum& operator|=(Enum& a, Enum b) { + static_assert(std::is_enum::value, "Enum must be an enum type"); + return (a = a | b); +} + +template +MBGL_CONSTEXPR bool operator&(Enum a, Enum b) { + static_assert(std::is_enum::value, "Enum must be an enum type"); + return bool(mbgl::underlying_type(a) & mbgl::underlying_type(b)); +} + +template +MBGL_CONSTEXPR Enum operator~(Enum value) { + static_assert(std::is_enum::value, "Enum must be an enum type"); + return Enum(~mbgl::underlying_type(value)); +} + + +} // namespace mbgl \ No newline at end of file diff --git a/mapbox-gl-native/include/mbgl/util/char_array_buffer.hpp b/mapbox-gl-native/include/mbgl/util/char_array_buffer.hpp new file mode 100644 index 000000000..80eeb2db6 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/char_array_buffer.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include + +namespace mbgl { +namespace util { + +// ref https://artofcode.wordpress.com/2010/12/12/deriving-from-stdstreambuf/ + +class CharArrayBuffer : public std::streambuf +{ +public: + CharArrayBuffer(char const* data, std::size_t size) + : begin_(data), end_(data + size), current_(data) {} + +private: + int_type underflow() final { + if (current_ == end_) { + return traits_type::eof(); + } + return traits_type::to_int_type(*current_); + } + + int_type uflow() final { + if (current_ == end_) { + return traits_type::eof(); + } + return traits_type::to_int_type(*current_++); + } + + int_type pbackfail(int_type ch) final { + if (current_ == begin_ || (ch != traits_type::eof() && ch != current_[-1])) { + return traits_type::eof(); + } + return traits_type::to_int_type(*--current_); + } + + std::streamsize showmanyc() final { + return end_ - current_; + } + + pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode) final { + if (dir == std::ios_base::beg) current_ = std::min(begin_ + off, end_); + else if (dir == std::ios_base::cur) current_ = std::min(current_ + off, end_); + else current_ = std::max(end_ - off, begin_); // dir == std::ios_base::end + return {off_type(current_ - begin_)}; + } + + char const * const begin_; + char const * const end_; + char const * current_; +}; + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/chrono.hpp b/mapbox-gl-native/include/mbgl/util/chrono.hpp new file mode 100644 index 000000000..d736b4f82 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/chrono.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { + +using Clock = std::chrono::steady_clock; + +using Seconds = std::chrono::seconds; +using Milliseconds = std::chrono::milliseconds; + +using TimePoint = Clock::time_point; +using Duration = Clock::duration; + +// Used to measure second-precision times, such as times gathered from HTTP responses. +using Timestamp = std::chrono::time_point; + +namespace util { + +inline Timestamp now() { + return platform::now(); +} + +// Returns the RFC1123 formatted date. E.g. "Tue, 04 Nov 2014 02:13:24 GMT" +std::string rfc1123(Timestamp); + +// YYYY-mm-dd HH:MM:SS e.g. "2015-11-26 16:11:23" +std::string iso8601(Timestamp); + +Timestamp parseTimestamp(const char *); + +Timestamp parseTimestamp(int32_t timestamp); + +// C++17 polyfill +template ::min() < std::chrono::duration::zero()>> +constexpr std::chrono::duration abs(std::chrono::duration d) +{ + return d >= d.zero() ? d : -d; +} + +} // namespace util + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/color.hpp b/mapbox-gl-native/include/mbgl/util/color.hpp new file mode 100644 index 000000000..8a03abfcd --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/color.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace mbgl { + +// Stores a premultiplied color, with all four channels ranging from 0..1 +class Color { +public: + Color() = default; + Color(float r_, float g_, float b_, float a_) + : r(r_), g(g_), b(b_), a(a_) { + assert(r_ >= 0.0f); + assert(r_ <= 1.0f); + assert(g_ >= 0.0f); + assert(g_ <= 1.0f); + assert(b_ >= 0.0f); + assert(b_ <= 1.0f); + assert(a_ >= 0.0f); + assert(a_ <= 1.0f); + } + + float r = 0.0f; + float g = 0.0f; + float b = 0.0f; + float a = 0.0f; + + static Color black() { return { 0.0f, 0.0f, 0.0f, 1.0f }; }; + static Color white() { return { 1.0f, 1.0f, 1.0f, 1.0f }; }; + + static Color red() { return { 1.0f, 0.0f, 0.0f, 1.0f }; }; + static Color green() { return { 0.0f, 1.0f, 0.0f, 1.0f }; }; + static Color blue() { return { 0.0f, 0.0f, 1.0f, 1.0f }; }; + + static optional parse(const std::string&); + std::string stringify() const; + std::array toArray() const; + mbgl::Value toObject() const; + mbgl::Value serialize() const; +}; + +inline bool operator==(const Color& colorA, const Color& colorB) { + return colorA.r == colorB.r && colorA.g == colorB.g && colorA.b == colorB.b && colorA.a == colorB.a; +} + +inline bool operator!=(const Color& colorA, const Color& colorB) { + return !(colorA == colorB); +} + +inline Color operator*(const Color& color, float alpha) { + assert(alpha >= 0.0f); + assert(alpha <= 1.0f); + return { + color.r * alpha, + color.g * alpha, + color.b * alpha, + color.a * alpha + }; +} + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/compression.hpp b/mapbox-gl-native/include/mbgl/util/compression.hpp new file mode 100644 index 000000000..93a3ddb8b --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/compression.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace mbgl { +namespace util { + +std::string compress(const std::string& raw); +std::string decompress(const std::string& raw); + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/constants.hpp b/mapbox-gl-native/include/mbgl/util/constants.hpp new file mode 100644 index 000000000..bb026817c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/constants.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace mbgl { + +namespace util { + +constexpr double tileSize = 512; + +/* + * The maximum extent of a feature that can be safely stored in the buffer. + * In practice, all features are converted to this extent before being added. + * + * Positions are stored as signed 16bit integers. + * One bit is lost for signedness to support features extending past the left edge of the tile. + * One bit is lost because the line vertex buffer used to pack 1 bit of other data into the int. + * This is no longer the case but we're reserving this bit anyway. + * One bit is lost to support features extending past the extent on the right edge of the tile. + * This leaves us with 2^13 = 8192 + */ +constexpr int32_t EXTENT = 8192; + +constexpr double DEG2RAD = M_PI / 180.0; +constexpr double RAD2DEG = 180.0 / M_PI; +constexpr double M2PI = M_PI * 2; +constexpr double EARTH_RADIUS_M = 6378137; +constexpr double LATITUDE_MAX = 85.051128779806604; +constexpr double LONGITUDE_MAX = 180; +constexpr double DEGREES_MAX = 360; +constexpr double PITCH_MIN = 0.0; +constexpr double PITCH_MAX = M_PI / 3; +constexpr double MIN_ZOOM = 0.0; +constexpr double MAX_ZOOM = 25.5; +constexpr float MIN_ZOOM_F = MIN_ZOOM; +constexpr float MAX_ZOOM_F = MAX_ZOOM; +constexpr uint8_t DEFAULT_MAX_ZOOM = 22; + +// ONE_EM constant used to go between "em" units used in style spec and "points" used internally for layout. +constexpr float ONE_EM = 24.0f; + +constexpr uint8_t DEFAULT_PREFETCH_ZOOM_DELTA = 4; + +constexpr uint64_t DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024; + +// Default ImageManager's cache size for images added via onStyleImageMissing API. +// Average sprite size with 1.0 pixel ratio is ~2kB, 8kB for pixel ratio of 2.0. +constexpr std::size_t DEFAULT_ON_DEMAND_IMAGES_CACHE_SIZE = 100 * 8192; + +constexpr Duration DEFAULT_TRANSITION_DURATION = Milliseconds(300); +constexpr Seconds CLOCK_SKEW_RETRY_TIMEOUT { 30 }; + +constexpr UnitBezier DEFAULT_TRANSITION_EASE = { 0, 0, 0.25, 1 }; + +constexpr int DEFAULT_RATE_LIMIT_TIMEOUT = 5; + +constexpr const char* API_BASE_URL = "https://api.mapbox.com"; +constexpr const char* ASSET_PROTOCOL = "asset://"; +constexpr const char* FILE_PROTOCOL = "file://"; +constexpr uint32_t DEFAULT_MAXIMUM_CONCURRENT_REQUESTS = 20; + +constexpr uint8_t TERRAIN_RGB_MAXZOOM = 15; + +constexpr const char* LAST_RESORT_ALPHABETIC_FONT = "Open Sans Regular"; +constexpr const char* LAST_RESORT_PAN_UNICODE_FONT = "Arial Unicode MS Regular"; + +} // namespace util + +namespace debug { + +extern const bool tileParseWarnings; +extern const bool styleParseWarnings; +extern const bool spriteWarnings; +extern const bool renderWarnings; +extern const bool labelTextMissingWarning; +extern const bool missingFontStackWarning; +extern const bool missingFontFaceWarning; +extern const bool glyphWarning; +extern const bool shapingWarning; + +} // namespace debug + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/convert.hpp b/mapbox-gl-native/include/mbgl/util/convert.hpp new file mode 100644 index 000000000..02ec7feef --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/convert.hpp @@ -0,0 +1,19 @@ +#include + +#include +#include +#include + +namespace mbgl { +namespace util { + +template::value>> +MBGL_CONSTEXPR std::array convert(const std::array&from) { + std::array to {{}}; + std::copy(std::begin(from), std::end(from), std::begin(to)); + return to; +} + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/default_styles.hpp b/mapbox-gl-native/include/mbgl/util/default_styles.hpp new file mode 100644 index 000000000..e1c53178b --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/default_styles.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace util { +namespace default_styles { + +struct DefaultStyle { + const char* url; + const char* name; + const unsigned currentVersion; +}; + +// clang-format off +constexpr const DefaultStyle streets = { "mapbox://styles/mapbox/streets-v11", "Streets", 11 }; +constexpr const DefaultStyle outdoors = { "mapbox://styles/mapbox/outdoors-v11", "Outdoors", 11 }; +constexpr const DefaultStyle light = { "mapbox://styles/mapbox/light-v10", "Light", 10 }; +constexpr const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v10", "Dark", 10 }; +constexpr const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite", 9 }; +constexpr const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v11", "Satellite Streets", 11 }; +// clang-format on + +constexpr std::array orderedStyles = { + {streets, outdoors, light, dark, satellite, satelliteStreets}}; + +constexpr size_t numOrderedStyles = orderedStyles.size(); + +} // end namespace default_styles +} // end namespace util +} // end namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/enum.hpp b/mapbox-gl-native/include/mbgl/util/enum.hpp new file mode 100644 index 000000000..608befd3c --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/enum.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include +#include +#include + +namespace mbgl { + +template +class Enum { +public: + using Type = T; + static const char * toString(T); + static optional toEnum(const std::string&); +}; + +#define MBGL_DEFINE_ENUM(T, values...) \ + \ +static const constexpr std::pair T##_names[] = values; \ + \ +template <> \ +const char * Enum::toString(T t) { \ + auto it = std::find_if(std::begin(T##_names), std::end(T##_names), \ + [&] (const auto& v) { return t == v.first; }); \ + assert(it != std::end(T##_names)); return it->second; \ +} \ + \ +template <> \ +optional Enum::toEnum(const std::string& s) { \ + auto it = std::find_if(std::begin(T##_names), std::end(T##_names), \ + [&] (const auto& v) { return s == v.second; }); \ + return it == std::end(T##_names) ? optional() : it->first; \ +} + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/event.hpp b/mapbox-gl-native/include/mbgl/util/event.hpp new file mode 100644 index 000000000..15583e956 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/event.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include + +namespace mbgl { + +enum class EventSeverity : uint8_t { + Debug, + Info, + Warning, + Error, +}; + +enum class Event : uint8_t { + General, + Setup, + Shader, + ParseStyle, + ParseTile, + Render, + Style, + Database, + HttpRequest, + Sprite, + Image, + OpenGL, + JNI, + Android, + Crash, + Glyph, + Timing +}; + +struct EventPermutation { + const EventSeverity severity; + const Event event; + + constexpr bool operator==(const EventPermutation &rhs) const { + return severity == rhs.severity && event == rhs.event; + } +}; + +constexpr EventSeverity disabledEventSeverities[] = { +#ifndef NDEBUG + EventSeverity(-1) // Avoid zero size array +#else + EventSeverity::Debug +#endif +}; + +constexpr Event disabledEvents[] = { + Event(-1) // Avoid zero size array +}; + +constexpr EventPermutation disabledEventPermutations[] = { + { EventSeverity::Debug, Event::Shader } +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/exception.hpp b/mapbox-gl-native/include/mbgl/util/exception.hpp new file mode 100644 index 000000000..f0d7c64f4 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/exception.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace mbgl { +namespace util { + +struct Exception : std::runtime_error { + Exception(const char *msg) : std::runtime_error(msg) {} + Exception(const std::string &msg) : std::runtime_error(msg) {} +}; + +struct StyleImageException : Exception { + StyleImageException(const char *msg) : Exception(msg) {} + StyleImageException(const std::string &msg) : Exception(msg) {} +}; + +struct MisuseException : Exception { + MisuseException(const char *msg) : Exception(msg) {} + MisuseException(const std::string &msg) : Exception(msg) {} +}; + +struct StyleParseException : Exception { + StyleParseException(const char *msg) : Exception(msg) {} + StyleParseException(const std::string &msg) : Exception(msg) {} +}; + +struct StyleLoadException : Exception { + StyleLoadException(const char *msg) : Exception(msg) {} + StyleLoadException(const std::string &msg) : Exception(msg) {} +}; + +struct NotFoundException : Exception { + NotFoundException(const char *msg) : Exception(msg) {} + NotFoundException(const std::string &msg) : Exception(msg) {} +}; + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/expected.hpp b/mapbox-gl-native/include/mbgl/util/expected.hpp new file mode 100644 index 000000000..53c5b6ab4 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/expected.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace mbgl { + +template +using expected = nonstd::expected; + +template +using unexpected = nonstd::unexpected_type; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/feature.hpp b/mapbox-gl-native/include/mbgl/util/feature.hpp new file mode 100644 index 000000000..de9b03f60 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/feature.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { + +using Value = mapbox::base::Value; +using NullValue = mapbox::base::NullValue; +using PropertyMap = mapbox::base::ValueObject; +using FeatureIdentifier = mapbox::feature::identifier; +using GeoJSONFeature = mapbox::feature::feature; +using FeatureState = mapbox::base::ValueObject; +using FeatureStates = std::unordered_map; // +using LayerFeatureStates = std::unordered_map; // + +class Feature : public GeoJSONFeature { +public: + std::string source; + std::string sourceLayer; + PropertyMap state; + + using GeometryType = mapbox::geometry::geometry; + + Feature() = default; + Feature(const GeoJSONFeature& f) : GeoJSONFeature(f) {} + Feature(const GeometryType& geom_) : GeoJSONFeature(geom_) {} + Feature(GeometryType&& geom_) : GeoJSONFeature(std::move(geom_)) {} +}; + +template +optional numericValue(const Value& value) { + return value.match([](uint64_t t) { return optional(t); }, + [](int64_t t) { return optional(t); }, + [](double t) { return optional(t); }, + [](const auto&) { return optional(); }); +} + +inline optional featureIDtoString(const FeatureIdentifier& id) { + if (id.is()) { + return nullopt; + } + + return id.match( + [](const std::string& value_) { return value_; }, [](uint64_t value_) { return util::toString(value_); }, + [](int64_t value_) { return util::toString(value_); }, [](double value_) { return util::toString(value_); }, + [](const auto&) -> optional { return nullopt; }); +} + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/font_stack.hpp b/mapbox-gl-native/include/mbgl/util/font_stack.hpp new file mode 100644 index 000000000..ccc1a0606 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/font_stack.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace mbgl { + +// An array of font names +using FontStack = std::vector; +using FontStackHash = std::size_t; + +std::string fontStackToString(const FontStack&); + +struct FontStackHasher { + FontStackHash operator()(const FontStack&) const; +}; + +// Statically evaluate layer properties to determine what font stacks are used. +std::set fontStacks(const std::vector>&); + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/geo.hpp b/mapbox-gl-native/include/mbgl/util/geo.hpp new file mode 100644 index 000000000..ae6c1550f --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/geo.hpp @@ -0,0 +1,249 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace mbgl { + +class CanonicalTileID; +class UnwrappedTileID; + +using ScreenCoordinate = mapbox::geometry::point; +using ScreenLineString = mapbox::geometry::line_string; +using ScreenBox = mapbox::geometry::box; + +class LatLng { +private: + double lat; + double lon; + +public: + enum WrapMode : bool { Unwrapped, Wrapped }; + + LatLng(double lat_ = 0, double lon_ = 0, WrapMode mode = Unwrapped) + : lat(lat_), lon(lon_) { + if (std::isnan(lat)) { + throw std::domain_error("latitude must not be NaN"); + } + if (std::isnan(lon)) { + throw std::domain_error("longitude must not be NaN"); + } + if (std::abs(lat) > 90.0) { + throw std::domain_error("latitude must be between -90 and 90"); + } + if (!std::isfinite(lon)) { + throw std::domain_error("longitude must not be infinite"); + } + if (mode == Wrapped) { + wrap(); + } + } + + double latitude() const { return lat; } + double longitude() const { return lon; } + + LatLng wrapped() const { return { lat, lon, Wrapped }; } + + void wrap() { + lon = util::wrap(lon, -util::LONGITUDE_MAX, util::LONGITUDE_MAX); + } + + // If the distance from start to end longitudes is between half and full + // world, unwrap the start longitude to ensure the shortest path is taken. + void unwrapForShortestPath(const LatLng& end) { + const double delta = std::abs(end.lon - lon); + if (delta <= util::LONGITUDE_MAX || delta >= util::DEGREES_MAX) return; + if (lon > 0 && end.lon < 0) lon -= util::DEGREES_MAX; + else if (lon < 0 && end.lon > 0) lon += util::DEGREES_MAX; + } + + // Constructs a LatLng object with the top left position of the specified tile. + LatLng(const CanonicalTileID& id); + LatLng(const UnwrappedTileID& id); + + friend bool operator==(const LatLng& a, const LatLng& b) { + return a.lat == b.lat && a.lon == b.lon; + } + + friend bool operator!=(const LatLng& a, const LatLng& b) { + return !(a == b); + } +}; + +class LatLngBounds { +public: + // Return a bounds covering the entire (unwrapped) world. + static LatLngBounds world() { return {{-90, -180}, {90, 180}}; } + + // Return the bounds consisting of the single point. + static LatLngBounds singleton(const LatLng& a) { return {a, a}; } + + // Return the convex hull of two points; the smallest bounds that contains both. + static LatLngBounds hull(const LatLng& a, const LatLng& b) { + LatLngBounds bounds(a, a); + bounds.extend(b); + return bounds; + } + + // Return a bounds that may serve as the identity element for the extend operation. + static LatLngBounds empty() { + LatLngBounds bounds = world(); + std::swap(bounds.sw, bounds.ne); + return bounds; + } + + /// Construct an infinite bound, a bound for which the constrain method returns its + /// input unmodified. + /// + /// Note: this is different than LatLngBounds::world() since arbitrary unwrapped + /// coordinates are also inside the bounds. + LatLngBounds() : sw({-90, -180}), ne({90, 180}), bounded(false) {} + + // Constructs a LatLngBounds object with the tile's exact boundaries. + LatLngBounds(const CanonicalTileID&); + + bool valid() const { + return (sw.latitude() <= ne.latitude()) && (sw.longitude() <= ne.longitude()); + } + + double south() const { return sw.latitude(); } + double west() const { return sw.longitude(); } + double north() const { return ne.latitude(); } + double east() const { return ne.longitude(); } + + LatLng southwest() const { return sw; } + LatLng northeast() const { return ne; } + LatLng southeast() const { return {south(), east()}; } + LatLng northwest() const { return {north(), west()}; } + + LatLng center() const { return {(sw.latitude() + ne.latitude()) / 2, (sw.longitude() + ne.longitude()) / 2}; } + + LatLng constrain(const LatLng& p) const; + + void extend(const LatLng& point) { + sw = LatLng(std::min(point.latitude(), sw.latitude()), + std::min(point.longitude(), sw.longitude())); + ne = LatLng(std::max(point.latitude(), ne.latitude()), + std::max(point.longitude(), ne.longitude())); + } + + void extend(const LatLngBounds& bounds) { + extend(bounds.sw); + extend(bounds.ne); + } + + bool isEmpty() const { + return sw.latitude() > ne.latitude() || + sw.longitude() > ne.longitude(); + } + + bool crossesAntimeridian() const { + return (sw.wrapped().longitude() > ne.wrapped().longitude()); + } + + bool contains(const CanonicalTileID& tileID) const; + bool contains(const LatLng& point, LatLng::WrapMode wrap = LatLng::Unwrapped) const; + bool contains(const LatLngBounds& area, LatLng::WrapMode wrap = LatLng::Unwrapped) const; + + bool intersects(LatLngBounds area, LatLng::WrapMode wrap = LatLng::Unwrapped) const; + +private: + LatLng sw; + LatLng ne; + bool bounded = true; + + LatLngBounds(LatLng sw_, LatLng ne_) : sw(sw_), ne(ne_) {} + + bool containsLatitude(double latitude) const; + bool containsLongitude(double longitude, LatLng::WrapMode wrap) const; + + friend bool operator==(const LatLngBounds& a, const LatLngBounds& b) { + return (!a.bounded && !b.bounded) || (a.bounded && b.bounded && a.sw == b.sw && a.ne == b.ne); + } + + friend bool operator!=(const LatLngBounds& a, const LatLngBounds& b) { + return !(a == b); + } +}; + +// Determines the orientation of the map. +enum class NorthOrientation : uint8_t { + Upwards, // Default + Rightwards, + Downwards, + Leftwards, +}; + +/// The distance on each side between a rectangle and a rectangle within. +class EdgeInsets { +private: + double _top; // Number of pixels inset from the top edge. + double _left; // Number of pixels inset from the left edge. + double _bottom; // Number of pixels inset from the bottom edge. + double _right; // Number of pixels inset from the right edge. + +public: + EdgeInsets(double t_ = 0, double l_ = 0, double b_ = 0, double r_ = 0) + : _top(t_), _left(l_), _bottom(b_), _right(r_) { + if (std::isnan(_top)) { + throw std::domain_error("top must not be NaN"); + } + if (std::isnan(_left)) { + throw std::domain_error("left must not be NaN"); + } + if (std::isnan(_bottom)) { + throw std::domain_error("bottom must not be NaN"); + } + if (std::isnan(_right)) { + throw std::domain_error("right must not be NaN"); + } + } + + double top() const { return _top; } + double left() const { return _left; } + double bottom() const { return _bottom; } + double right() const { return _right; } + + bool isFlush() const { + return _top == 0 && _left == 0 && _bottom == 0 && _right == 0; + } + + void operator+=(const EdgeInsets& o) { + _top += o._top; + _left += o._left; + _bottom += o._bottom; + _right += o._right; + } + + EdgeInsets operator+(const EdgeInsets& o) const { + return { + _top + o._top, _left + o._left, _bottom + o._bottom, _right + o._right, + }; + } + + ScreenCoordinate getCenter(uint16_t width, uint16_t height) const; + + friend bool operator==(const EdgeInsets& a, const EdgeInsets& b) { + return a._top == b._top && a._left == b._left && a._bottom == b._bottom && a._right == b._right; + } + + friend bool operator!=(const EdgeInsets& a, const EdgeInsets& b) { + return !(a == b); + } +}; + +struct LatLngAltitude { + LatLng location = {0.0, 0.0}; + double altitude = 0.0; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/geojson.hpp b/mapbox-gl-native/include/mbgl/util/geojson.hpp new file mode 100644 index 000000000..2a6569be4 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/geojson.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace mbgl { + +using GeoJSON = mapbox::geojson::geojson; +using FeatureCollection = mapbox::geojson::feature_collection; +using FeatureExtensionValue = mapbox::util::variant; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/geometry.hpp b/mapbox-gl-native/include/mbgl/util/geometry.hpp new file mode 100644 index 000000000..17384f222 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/geometry.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { + +enum class FeatureType : uint8_t { + Unknown = 0, + Point = 1, + LineString = 2, + Polygon = 3 +}; + +using EmptyGeometry = mapbox::geometry::empty; + +template +using Point = mapbox::geometry::point; + +template +using LineString = mapbox::geometry::line_string; + +template +using Polygon = mapbox::geometry::polygon; + +template +using MultiPoint = mapbox::geometry::multi_point; + +template +using MultiLineString = mapbox::geometry::multi_line_string; + +template +using MultiPolygon = mapbox::geometry::multi_polygon; + +template +using LinearRing = mapbox::geometry::linear_ring; + +template +using Geometry = mapbox::geometry::geometry; + +template +Point convertPoint(const Point& p) { + return Point(p.x, p.y); +} + +struct ToFeatureType { + FeatureType operator()(const EmptyGeometry&) const { return FeatureType::Unknown; } + template + FeatureType operator()(const Point &) const { return FeatureType::Point; } + template + FeatureType operator()(const MultiPoint &) const { return FeatureType::Point; } + template + FeatureType operator()(const LineString &) const { return FeatureType::LineString; } + template + FeatureType operator()(const MultiLineString &) const { return FeatureType::LineString; } + template + FeatureType operator()(const Polygon &) const { return FeatureType::Polygon; } + template + FeatureType operator()(const MultiPolygon &) const { return FeatureType::Polygon; } + template + FeatureType operator()(const mapbox::geometry::geometry_collection &) const { return FeatureType::Unknown; } +}; + +template +auto forEachPoint(const Geometry& g, F f) { + mapbox::geometry::for_each_point(g, f); +} + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/ignore.hpp b/mapbox-gl-native/include/mbgl/util/ignore.hpp new file mode 100644 index 000000000..577bcf4d9 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/ignore.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace mbgl { +namespace util { + +// Accept any number of parameters of any types, and do nothing with them. +// Useful for providing a context for parameter pack expansion where a legal +// expansion context is not otherwise available. +// +// See https://github.com/mapbox/cpp/blob/master/C%2B%2B%20Structural%20Metaprogramming.md +// for more details. +// +template void ignore(Ts&&...) {} + +// std::initializer_list overload, for situations where you need sequenced +// modifications. +// +template void ignore(const std::initializer_list&) {} + +// Handle the zero-argument case. +inline void ignore(const std::initializer_list&) {} + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/image.hpp b/mapbox-gl-native/include/mbgl/util/image.hpp new file mode 100644 index 000000000..e0ee69443 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/image.hpp @@ -0,0 +1,166 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace mbgl { + +enum class ImageAlphaMode { + Unassociated, + Premultiplied, + Exclusive, // Alpha-channel only +}; + +template +class Image : private util::noncopyable { +public: + Image() = default; + + Image(Size size_) : size(size_), data(std::make_unique(bytes())) {} + + Image(Size size_, const uint8_t* srcData, std::size_t srcLength) : size(size_) { + if (srcLength != bytes()) { + throw std::invalid_argument("mismatched image size"); + } + data = std::make_unique(bytes()); + std::copy(srcData, srcData + srcLength, data.get()); + } + + Image(Size size_, std::unique_ptr data_) : size(size_), data(std::move(data_)) {} + + Image(Image&& o) noexcept : size(o.size), data(std::move(o.data)) { o.size.width = o.size.height = 0; } + + Image& operator=(Image&& o) noexcept { + size = o.size; + data = std::move(o.data); + o.size.width = o.size.height = 0; + return *this; + } + + friend bool operator==(const Image& lhs, const Image& rhs) { + return std::equal(lhs.data.get(), lhs.data.get() + lhs.bytes(), + rhs.data.get(), rhs.data.get() + rhs.bytes()); + } + + friend bool operator!=(const Image& lhs, const Image& rhs) { + return !(lhs == rhs); + } + + bool valid() const { return !size.isEmpty() && data != nullptr; } + + template + T clone() const { + T copy_(size); + std::copy(data.get(), data.get() + bytes(), copy_.data.get()); + return copy_; + } + + size_t stride() const { return channels * size.width; } + size_t bytes() const { return stride() * size.height; } + + void fill(uint8_t value) { + std::fill(data.get(), data.get() + bytes(), value); + } + + void resize(Size size_) { + if (size == size_) { + return; + } + Image newImage(size_); + newImage.fill(0); + copy(*this, newImage, {0, 0}, {0, 0}, { + std::min(size.width, size_.width), + std::min(size.height, size_.height) + }); + operator=(std::move(newImage)); + } + + // Clears the rect area specified by `pt` and `size` from `dstImage`. + static void clear(Image& dstImg, const Point& pt, const Size& size) { + if (size.isEmpty()) { + return; + } + + if (!dstImg.valid()) { + throw std::invalid_argument("invalid destination for image clear"); + } + + if (size.width > dstImg.size.width || + size.height > dstImg.size.height || + pt.x > dstImg.size.width - size.width || + pt.y > dstImg.size.height - size.height) { + throw std::out_of_range("out of range destination coordinates for image clear"); + } + + uint8_t* dstData = dstImg.data.get(); + + for (uint32_t y = 0; y < size.height; y++) { + const std::size_t dstOffset = (pt.y + y) * dstImg.stride() + pt.x * channels; + std::memset(dstData + dstOffset, 0, size.width * channels); + } + } + + // Copy image data within `rect` from `src` to the rectangle of the same size at `pt` + // in `dst`. If the specified bounds exceed the bounds of the source or destination, + // throw `std::out_of_range`. Must not be used to move data within a single Image. + static void copy(const Image& srcImg, Image& dstImg, const Point& srcPt, const Point& dstPt, const Size& size) { + if (size.isEmpty()) { + return; + } + + if (!srcImg.valid()) { + throw std::invalid_argument("invalid source for image copy"); + } + + if (!dstImg.valid()) { + throw std::invalid_argument("invalid destination for image copy"); + } + + if (size.width > srcImg.size.width || + size.height > srcImg.size.height || + srcPt.x > srcImg.size.width - size.width || + srcPt.y > srcImg.size.height - size.height) { + throw std::out_of_range("out of range source coordinates for image copy"); + } + + if (size.width > dstImg.size.width || + size.height > dstImg.size.height || + dstPt.x > dstImg.size.width - size.width || + dstPt.y > dstImg.size.height - size.height) { + throw std::out_of_range("out of range destination coordinates for image copy"); + } + + const uint8_t* srcData = srcImg.data.get(); + uint8_t* dstData = dstImg.data.get(); + + assert(srcData != dstData); + + for (uint32_t y = 0; y < size.height; y++) { + const std::size_t srcOffset = (srcPt.y + y) * srcImg.stride() + srcPt.x * channels; + const std::size_t dstOffset = (dstPt.y + y) * dstImg.stride() + dstPt.x * channels; + std::copy(srcData + srcOffset, + srcData + srcOffset + size.width * channels, + dstData + dstOffset); + } + } + + Size size; + static constexpr size_t channels = Mode == ImageAlphaMode::Exclusive ? 1 : 4; + std::unique_ptr data; +}; + +using UnassociatedImage = Image; +using PremultipliedImage = Image; +using AlphaImage = Image; + +// TODO: don't use std::string for binary data. +PremultipliedImage decodeImage(const std::string&); +std::string encodePNG(const PremultipliedImage&); + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/immutable.hpp b/mapbox-gl-native/include/mbgl/util/immutable.hpp new file mode 100644 index 000000000..fbd3ac9b3 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/immutable.hpp @@ -0,0 +1,133 @@ +#pragma once + +#include + +namespace mbgl { + +/** + * `Mutable` is a non-nullable uniquely owning reference to a `T`. It can be efficiently converted + * to `Immutable`. + * + * The lifecycle of `Mutable` and `Immutable` is as follows: + * + * 1. Create a `Mutable` using `makeMutable(...)` + * 2. Mutate it freely + * 3. When you're ready to freeze its state and enable safe cross-thread sharing, move assign or + * move construct it to `Immutable` + * + * The reason that `Mutable` exists, rather than simply using a `std::unique_ptr`, is to take advantage + * of the underlying single-allocation optimization provided by `std::make_shared`. + */ +template +class Mutable { +public: + Mutable(Mutable&&) noexcept = default; + Mutable& operator=(Mutable&&) noexcept = default; + + Mutable(const Mutable&) = delete; + Mutable& operator=(const Mutable&) = delete; + + T* get() { return ptr.get(); } + T* operator->() { return ptr.get(); } + T& operator*() { return *ptr; } + +private: + Mutable(std::shared_ptr&& s) + : ptr(std::move(s)) {} + + std::shared_ptr ptr; + + template friend class Immutable; + // NOLINTNEXTLINE(readability-redundant-declaration) + template friend Mutable makeMutable(Args&&...); + // NOLINTNEXTLINE(readability-redundant-declaration) + template friend Mutable staticMutableCast(const Mutable&); +}; + +template +Mutable makeMutable(Args&&... args) { + return Mutable(std::make_shared(std::forward(args)...)); +} + +template +Mutable staticMutableCast(const Mutable& u) { + return Mutable(std::static_pointer_cast(u.ptr)); +} + +/** + * `Immutable` is a non-nullable shared reference to a `const T`. Construction requires + * a transfer of unique ownership from a `Mutable`; once constructed it has the same behavior + * as `std::shared_ptr` but with better indication of intent. + * + * Normally one should not share state between threads because it's difficult to verify the + * absence of read/write data races. `Immutable` provides a guarantee that no writes are + * possible, and instances therefore can be freely transferred and shared between threads. + */ +template +class Immutable { +public: + template + Immutable(Mutable&& s) + : ptr(std::const_pointer_cast(std::move(s.ptr))) {} + + template + Immutable(Immutable s) + : ptr(std::move(s.ptr)) {} + + Immutable(Immutable&&) noexcept = default; + Immutable(const Immutable&) = default; + + template + Immutable& operator=(Mutable&& s) { + ptr = std::const_pointer_cast(std::move(s.ptr)); + return *this; + } + + Immutable& operator=(Immutable&&) noexcept = default; + Immutable& operator=(const Immutable&) = default; + + const T* get() const { return ptr.get(); } + const T* operator->() const { return ptr.get(); } + const T& operator*() const { return *ptr; } + + friend bool operator==(const Immutable& lhs, const Immutable& rhs) { + return lhs.ptr == rhs.ptr; + } + + friend bool operator!=(const Immutable& lhs, const Immutable& rhs) { + return lhs.ptr != rhs.ptr; + } + +private: + Immutable(std::shared_ptr&& s) + : ptr(std::move(s)) {} + + std::shared_ptr ptr; + + template friend class Immutable; + + // NOLINTNEXTLINE(readability-redundant-declaration) + template friend Immutable staticImmutableCast(const Immutable&); +}; + +template +Immutable staticImmutableCast(const Immutable& u) { + return Immutable(std::static_pointer_cast(u.ptr)); +} + +/** + * Constrained mutation of an immutable reference. Makes a temporarily-mutable copy of the + * input Immutable using the inner type's copy constructor, runs the given callable on the + * mutable copy, and then freezes the copy and reassigns it to the input reference. + * + * Note that other Immutables referring to the same inner instance are not affected; they + * continue to referencing the original immutable instance. + */ +template +void mutate(Immutable& immutable, Fn&& fn) { + Mutable mut = makeMutable(*immutable); + std::forward(fn)(*mut); + immutable = std::move(mut); +} + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/indexed_tuple.hpp b/mapbox-gl-native/include/mbgl/util/indexed_tuple.hpp new file mode 100644 index 000000000..99d73d1e1 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/indexed_tuple.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { + +template +struct TypeIndex; + +template +struct TypeIndex : std::integral_constant {}; + +template +struct TypeIndex : std::integral_constant::value> {}; + +template class IndexedTuple; + +// A tuple of Ts, where individual members can be accessed via `t.get()` for I ∈ Is. +// +// See https://github.com/mapbox/cpp/blob/master/C%2B%2B%20Structural%20Metaprogramming.md +// for motivation. +// +template +class IndexedTuple, TypeList> : public std::tuple { +public: + static_assert(sizeof...(Is) == sizeof...(Ts), "IndexedTuple size mismatch"); + + template + auto& get() { + return std::get::value, Ts...>(*this); + } + + template + const auto& get() const { + return std::get::value, Ts...>(*this); + } + + template + IndexedTuple(Us&&... other) : std::tuple(std::forward(other)...) {}; + + template + IndexedTuple, TypeList> + concat(const IndexedTuple, TypeList>& other) const { + return IndexedTuple, TypeList> { + get()..., + other.template get()... + }; + } + + // Help out MSVC++ + bool operator==(const IndexedTuple, TypeList>& other) const { + return static_cast&>(*this) == static_cast&>(other); + } + + bool operator!=(const IndexedTuple, TypeList>& other) const { + return !(*this == other); + } +}; + +template +using ExpandToType = T; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/interpolate.hpp b/mapbox-gl-native/include/mbgl/util/interpolate.hpp new file mode 100644 index 000000000..d1622dffb --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/interpolate.hpp @@ -0,0 +1,160 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace util { + +float interpolationFactor(float base, Range range, float z); + +template +struct Interpolator; + +template +T interpolate(const T& a, const T& b, const double t) { + return Interpolator()(a, b, t); +} + + +template +struct Interpolator { + T operator()(const T& a, const T& b, const double t) const { + return a * (1.0 - t) + b * t; + } +}; + +template +struct Interpolator> { +private: + using Array = std::array; + + template + Array operator()(const Array& a, const Array& b, const double t, std::index_sequence) { + return {{ interpolate(a[I], b[I], t)... }}; + } + +public: + Array operator()(const Array& a, const Array& b, const double t) { + return operator()(a, b, t, std::make_index_sequence()); + } +}; + + +// In order to accept Array as an output value for Curve +// expressions, we need to have an interpolatable std::vector type. +// However, style properties like line-dasharray are represented using +// std::vector, and should NOT be considered interpolatable. +// So, we use std::vector to represent expression array values, +// asserting that (a) the vectors are the same size, and (b) they contain +// only numeric values. (These invariants should be relatively safe, +// being enforced by the expression type system.) +template<> +struct Interpolator> { + std::vector operator()(const std::vector& a, + const std::vector& b, + const double t) const { + assert(a.size() == b.size()); + if (a.empty()) return {}; + std::vector result; + for (std::size_t i = 0; i < a.size(); i++) { + assert(a[i].template is()); + assert(b[i].template is()); + style::expression::Value item = interpolate( + a[i].template get(), + b[i].template get(), + t); + result.push_back(item); + } + return result; + } +}; + +template <> +struct Interpolator { +public: + style::Position operator()(const style::Position& a, const style::Position& b, const double t) { + auto pos = style::Position(); + auto interpolated = interpolate(a.getCartesian(), b.getCartesian(), t); + pos.setCartesian(interpolated); + return { pos }; + } +}; + +template <> +struct Interpolator { +public: + Color operator()(const Color& a, const Color& b, const double t) { + return { + interpolate(a.r, b.r, t), + interpolate(a.g, b.g, t), + interpolate(a.b, b.b, t), + interpolate(a.a, b.a, t) + }; + } +}; + +template <> +struct Interpolator { +public: + style::Rotation operator()(const style::Rotation& a, const style::Rotation& b, const double t) { + assert(a.period() == b.period()); + auto period = a.period(); + auto aAngle = std::fmod(a.getAngle(), period); + auto bAngle = std::fmod(b.getAngle(), period); + + if (aAngle - bAngle > period * 0.5) { + return {std::fmod(aAngle * (1.0 - t) + (bAngle + period) * t, period)}; + } + + if (aAngle - bAngle < period * -0.5) { + return {std::fmod((aAngle + period) * (1.0 - t) + bAngle * t, period)}; + } + + return {aAngle * (1.0 - t) + bAngle * t}; + } +}; + +struct Uninterpolated { + template + T operator()(const T& a, const T&, const double) const { + return a; + } +}; + +template <> +struct Interpolator + : Uninterpolated {}; + +template +struct Interpolator::value>> + : Uninterpolated {}; + +template <> +struct Interpolator + : Uninterpolated {}; + +template +struct Interpolator> + : Uninterpolated {}; + +template +struct Interpolatable + : std::conditional_t< + !std::is_base_of>::value, + std::true_type, + std::false_type> {}; + + + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/logging.hpp b/mapbox-gl-native/include/mbgl/util/logging.hpp new file mode 100644 index 000000000..1b72d7473 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/logging.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include + +#include +#include + +#include +#include + +namespace mbgl { + +class Log { +public: + class Observer : private util::noncopyable { + public: + virtual ~Observer() = default; + + // When an observer is set, this function will be called for every log + // message. Returning true will consume the message. + virtual bool onRecord(EventSeverity severity, Event event, int64_t code, const std::string &msg) = 0; + }; + + class NullObserver : public Observer { + bool onRecord(EventSeverity, Event, int64_t, const std::string&) override { + return true; + } + }; + + static void setObserver(std::unique_ptr Observer); + static std::unique_ptr removeObserver(); + +private: + template + constexpr static bool includes(const T e, const T (&l)[N], const size_t i = 0) { + return i < N && (l[i] == e || includes(e, l, i + 1)); + } + +public: + Log(); + ~Log(); + + static void useLogThread(bool enable); + + template + static void Debug(Event event, Args&& ...args) { + Record(EventSeverity::Debug, event, ::std::forward(args)...); + } + + template + static void Info(Event event, Args&& ...args) { + Record(EventSeverity::Info, event, ::std::forward(args)...); + } + + template + static void Warning(Event event, Args&& ...args) { + Record(EventSeverity::Warning, event, ::std::forward(args)...); + } + + template + static void Error(Event event, Args&& ...args) { + Record(EventSeverity::Error, event, ::std::forward(args)...); + } + + template + static void Record(EventSeverity severity, Event event, Args&& ...args) { + if (!includes(severity, disabledEventSeverities) && + !includes(event, disabledEvents) && + !includes({ severity, event }, disabledEventPermutations)) { + record(severity, event, ::std::forward(args)...); + } + } + +private: + static Log* get() noexcept; + + static void record(EventSeverity severity, Event event, const std::string &msg); + static void record(EventSeverity severity, Event event, const char* format = "", ...); + static void record(EventSeverity severity, Event event, int64_t code, const char* format = "", ...); + static void record(EventSeverity severity, + Event event, + int64_t code, + const std::string& msg, + const optional& threadName); + + // This method is the data sink that must be implemented by each platform we + // support. It should ideally output the error message in a human readable + // format to the developer. + static void platformRecord(EventSeverity severity, const std::string &msg); + class Impl; + const std::unique_ptr impl; +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/monotonic_timer.hpp b/mapbox-gl-native/include/mbgl/util/monotonic_timer.hpp new file mode 100644 index 000000000..bdb167214 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/monotonic_timer.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { +namespace util { + +class MonotonicTimer { +public: + static std::chrono::duration now(); + + template + inline static std::chrono::duration duration(F&& func, Args&&... args) { + auto start = now(); + func(std::forward(args)...); + return now() - start; + } +}; + +} // namespace util +} // namespace mbgl \ No newline at end of file diff --git a/mapbox-gl-native/include/mbgl/util/noncopyable.hpp b/mapbox-gl-native/include/mbgl/util/noncopyable.hpp new file mode 100644 index 000000000..8cb7e198d --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/noncopyable.hpp @@ -0,0 +1,23 @@ +#pragma once + +namespace mbgl { namespace util { + +namespace non_copyable_ +{ + +class noncopyable +{ +public: + noncopyable( noncopyable const& ) = delete; + noncopyable& operator=(noncopyable const& ) = delete; + +protected: + constexpr noncopyable() = default; + ~noncopyable() = default; +}; +} // namespace non_copyable_ + +using noncopyable = non_copyable_::noncopyable; + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/optional.hpp b/mapbox-gl-native/include/mbgl/util/optional.hpp new file mode 100644 index 000000000..e0a3786a7 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/optional.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace mbgl { + +template +using optional = std::experimental::optional; + +using nullopt_t = std::experimental::nullopt_t; +constexpr nullopt_t nullopt = std::experimental::nullopt; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/pass_types.hpp b/mapbox-gl-native/include/mbgl/util/pass_types.hpp new file mode 100644 index 000000000..227a81778 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/pass_types.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace mbgl { + +// Using this type as a return type enforces the client to retain the returned object. +template +class Pass { +public: + Pass(T obj_) : obj(std::move(obj_)) {} + Pass(Pass&&) noexcept = default; + Pass(const Pass&) = delete; + Pass& operator=(const Pass&) = delete; + operator T() && { return std::move(obj); } + +private: + T obj; +}; + +template +using PassRefPtr = Pass>; + +} // namespace mbgl \ No newline at end of file diff --git a/mapbox-gl-native/include/mbgl/util/platform.hpp b/mapbox-gl-native/include/mbgl/util/platform.hpp new file mode 100644 index 000000000..917b98483 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/platform.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace mbgl { +namespace platform { + +class Request; + +// Uppercase a string, potentially using platform-specific routines. +std::string uppercase(const std::string &string); + +// Lowercase a string, potentially using platform-specific routines. +std::string lowercase(const std::string &string); + +// Gets the name of the current thread. +std::string getCurrentThreadName(); + +// Set the name of the current thread, truncated at 15. +void setCurrentThreadName(const std::string& name); + +// Makes the current thread low priority. +void makeThreadLowPriority(); + +// Sets priority of a current thread. Platform implementation +// must validate provided value. +void setCurrentThreadPriority(double priority); + +} // namespace platform +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/premultiply.hpp b/mapbox-gl-native/include/mbgl/util/premultiply.hpp new file mode 100644 index 000000000..532c7cb11 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/premultiply.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace mbgl { +namespace util { + +PremultipliedImage premultiply(UnassociatedImage&&); +UnassociatedImage unpremultiply(PremultipliedImage&&); + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/projection.hpp b/mapbox-gl-native/include/mbgl/util/projection.hpp new file mode 100644 index 000000000..f8563719d --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/projection.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { + +class ProjectedMeters { +private: + double _northing; // Distance measured northwards. + double _easting; // Distance measured eastwards. + +public: + ProjectedMeters(double n_ = 0, double e_ = 0) + : _northing(n_), _easting(e_) { + if (std::isnan(_northing)) { + throw std::domain_error("northing must not be NaN"); + } + if (std::isnan(_easting)) { + throw std::domain_error("easting must not be NaN"); + } + } + + double northing() const { return _northing; } + double easting() const { return _easting; } + + friend bool operator==(const ProjectedMeters& a, const ProjectedMeters& b) { + return a._northing == b._northing && a._easting == b._easting; + } + + friend bool operator!=(const ProjectedMeters& a, const ProjectedMeters& b) { + return !(a == b); + } +}; + +// Spherical Mercator projection +// http://docs.openlayers.org/library/spherical_mercator.html +class Projection { +public: + // Map pixel width at given scale. + static double worldSize(double scale) { + return scale * util::tileSize; + } + + static double getMetersPerPixelAtLatitude(double lat, double zoom) { + const double constrainedZoom = util::clamp(zoom, util::MIN_ZOOM, util::MAX_ZOOM); + const double constrainedScale = std::pow(2.0, constrainedZoom); + const double constrainedLatitude = util::clamp(lat, -util::LATITUDE_MAX, util::LATITUDE_MAX); + return std::cos(constrainedLatitude * util::DEG2RAD) * util::M2PI * util::EARTH_RADIUS_M / worldSize(constrainedScale); + } + + static ProjectedMeters projectedMetersForLatLng(const LatLng& latLng) { + const double constrainedLatitude = util::clamp(latLng.latitude(), -util::LATITUDE_MAX, util::LATITUDE_MAX); + const double constrainedLongitude = util::clamp(latLng.longitude(), -util::LONGITUDE_MAX, util::LONGITUDE_MAX); + + const double m = 1 - 1e-15; + const double f = util::clamp(std::sin(util::DEG2RAD * constrainedLatitude), -m, m); + + const double easting = util::EARTH_RADIUS_M * constrainedLongitude * util::DEG2RAD; + const double northing = 0.5 * util::EARTH_RADIUS_M * std::log((1 + f) / (1 - f)); + + return {northing, easting}; + } + + static LatLng latLngForProjectedMeters(const ProjectedMeters& projectedMeters) { + double latitude = (2 * std::atan(std::exp(projectedMeters.northing() / util::EARTH_RADIUS_M)) - (M_PI / 2.0)) * util::RAD2DEG; + double longitude = projectedMeters.easting() * util::RAD2DEG / util::EARTH_RADIUS_M; + + latitude = util::clamp(latitude, -util::LATITUDE_MAX, util::LATITUDE_MAX); + longitude = util::clamp(longitude, -util::LONGITUDE_MAX, util::LONGITUDE_MAX); + + return {latitude, longitude}; + } + + static Point project(const LatLng& latLng, double scale) { + return project_(latLng, worldSize(scale)); + } + + //Returns point on tile + static Point project(const LatLng& latLng, int32_t zoom) { + return project_(latLng, 1 << zoom); + } + + static LatLng unproject(const Point& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) { + auto p2 = p * util::DEGREES_MAX / worldSize(scale); + return LatLng { + util::DEGREES_MAX / M_PI * std::atan(std::exp((util::LONGITUDE_MAX - p2.y) * util::DEG2RAD)) - 90.0, + p2.x - util::LONGITUDE_MAX, + wrapMode + }; + } + +private: + static Point project_(const LatLng& latLng, double worldSize) { + const double latitude = util::clamp(latLng.latitude(), -util::LATITUDE_MAX, util::LATITUDE_MAX); + return Point { + util::LONGITUDE_MAX + latLng.longitude(), + util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latitude * M_PI / util::DEGREES_MAX)) + } * (worldSize / util::DEGREES_MAX); + } +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/range.hpp b/mapbox-gl-native/include/mbgl/util/range.hpp new file mode 100644 index 000000000..5591a22a1 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/range.hpp @@ -0,0 +1,27 @@ +#include + +#pragma once + +namespace mbgl { + +template +class Range { +public: + constexpr Range(T min_, T max_) + : min(std::move(min_)), max(std::move(max_)) {} + + T min; + T max; +}; + +template +bool operator==(const Range& a, const Range& b) { + return a.min == b.min && a.max == b.max; +} + +template +bool operator!=(const Range& a, const Range& b) { + return !(a == b); +} + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/run_loop.hpp b/mapbox-gl-native/include/mbgl/util/run_loop.hpp new file mode 100644 index 000000000..4bf614c55 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/run_loop.hpp @@ -0,0 +1,144 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace util { + +using LOOP_HANDLE = void *; + +class RunLoop : public Scheduler, + private util::noncopyable { +public: + enum class Type : uint8_t { + Default, + New, + }; + + enum class Priority : bool { + Default = false, + High = true, + }; + + enum class Event : uint8_t { + None = 0, + Read = 1, + Write = 2, + ReadWrite = Read | Write, + }; + + RunLoop(Type type = Type::Default); + ~RunLoop() override; + + static RunLoop* Get(); + static LOOP_HANDLE getLoopHandle(); + + void run(); + void runOnce(); + void stop(); + + // Platform integration callback for platforms that do not have full + // run loop integration or don't want to block at the Mapbox GL Native + // loop. It will be called from any thread and is up to the platform + // to, after receiving the callback, call RunLoop::runOnce() from the + // same thread as the Map object lives. + void setPlatformCallback(std::function callback) { platformCallback = std::move(callback); } + + // So far only needed by the libcurl backend. + void addWatch(int fd, Event, std::function&& callback); + void removeWatch(int fd); + + // Invoke fn(args...) on this RunLoop. + template + void invoke(Priority priority, Fn&& fn, Args&&... args) { + push(priority, WorkTask::make(std::forward(fn), std::forward(args)...)); + } + + // Invoke fn(args...) on this RunLoop. + template + void invoke(Fn&& fn, Args&&... args) { + invoke(Priority::Default, std::forward(fn), std::forward(args)...); + } + + // Post the cancellable work fn(args...) to this RunLoop. + template + std::unique_ptr + invokeCancellable(Fn&& fn, Args&&... args) { + std::shared_ptr task = WorkTask::make(std::forward(fn), std::forward(args)...); + push(Priority::Default, task); + return std::make_unique(task); + } + + void schedule(std::function fn) override { invoke(std::move(fn)); } + ::mapbox::base::WeakPtr makeWeakPtr() override { return weakFactory.makeWeakPtr(); } + + class Impl; + +private: + MBGL_STORE_THREAD(tid) + + using Queue = std::queue>; + + // Wakes up the RunLoop so that it starts processing items in the queue. + void wake(); + + // Adds a WorkTask to the queue, and wakes it up. + void push(Priority priority, std::shared_ptr task) { + std::lock_guard lock(mutex); + if (priority == Priority::High) { + highPriorityQueue.emplace(std::move(task)); + } else { + defaultQueue.emplace(std::move(task)); + } + wake(); + + if (platformCallback) { + platformCallback(); + } + } + + void process() { + std::shared_ptr task; + std::unique_lock lock(mutex); + while (true) { + if (!highPriorityQueue.empty()) { + task = std::move(highPriorityQueue.front()); + highPriorityQueue.pop(); + } else if (!defaultQueue.empty()) { + task = std::move(defaultQueue.front()); + defaultQueue.pop(); + } else { + break; + } + lock.unlock(); + (*task)(); + task.reset(); + lock.lock(); + } + } + + std::function platformCallback; + + Queue defaultQueue; + Queue highPriorityQueue; + std::mutex mutex; + + std::unique_ptr impl; + ::mapbox::base::WeakPtrFactory weakFactory{this}; +}; + +} // namespace util +} // namespace mbgl + +#include diff --git a/mapbox-gl-native/include/mbgl/util/size.hpp b/mapbox-gl-native/include/mbgl/util/size.hpp new file mode 100644 index 000000000..12c0ad056 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/size.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +namespace mbgl { + +class Size { +public: + constexpr Size() = default; + + constexpr Size(const uint32_t width_, const uint32_t height_) : width(width_), height(height_) { + } + + constexpr uint32_t area() const { + return width * height; + } + + constexpr float aspectRatio() const { + return static_cast(width) / static_cast(height); + } + + constexpr bool isEmpty() const { + return width == 0 || height == 0; + } + + uint32_t width = 0; + uint32_t height = 0; +}; + +constexpr inline bool operator==(const Size& a, const Size& b) { + return a.width == b.width && a.height == b.height; +} + +constexpr inline bool operator!=(const Size& a, const Size& b) { + return !(a == b); +} + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/string.hpp b/mapbox-gl-native/include/mbgl/util/string.hpp new file mode 100644 index 000000000..27c3943c5 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/string.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include + +// Polyfill needed by Qt when building for Android with GCC +#if defined(__ANDROID__) && defined(__GLIBCXX__) + +namespace std { + +inline int stoi(const std::string &str) +{ + return atoi(str.c_str()); +} + +inline float stof(const std::string &str) { + return static_cast(atof(str.c_str())); +} + +} // namespace std + +#endif + +namespace mbgl { +namespace util { + +std::string toString(int64_t); +std::string toString(uint64_t); +std::string toString(int32_t); +std::string toString(uint32_t); +std::string toString(double, bool decimal = false); + +inline std::string toString(int16_t t) { + return toString(static_cast(t)); +} + +inline std::string toString(uint16_t t) { + return toString(static_cast(t)); +} + +inline std::string toString(int8_t t) { + return toString(static_cast(t)); +} + +inline std::string toString(uint8_t t) { + return toString(static_cast(t)); +} + +template ::value>> +inline std::string toString(unsigned long t) { + return toString(static_cast(t)); +} + +template ::value>> +inline std::string toString(unsigned long long t) { + return toString(static_cast(t)); +} + +inline std::string toString(float t, bool decimal = false) { + return toString(static_cast(t), decimal); +} + +inline std::string toString(long double t, bool decimal = false) { + return toString(static_cast(t), decimal); +} + +std::string toString(const std::exception_ptr &); + +template +std::string toString(T) = delete; + +std::string toHex(uint32_t); +std::string toHex(uint64_t); + +inline float stof(const std::string& str) { + return std::stof(str); +} + +} // namespace util +} // namespace mbgl + +// Android's libstdc++ doesn't have std::to_string() +#if defined(__ANDROID__) && defined(__GLIBCXX__) + +namespace std { + +template +inline std::string to_string(T value) { + return mbgl::util::toString(value); +} + +} // namespace std + +#endif diff --git a/mapbox-gl-native/include/mbgl/util/thread.hpp b/mapbox-gl-native/include/mbgl/util/thread.hpp new file mode 100644 index 000000000..7a9b74170 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/thread.hpp @@ -0,0 +1,170 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace util { + +// Manages a thread with `Object`. + +// Upon creation of this object, it launches a thread and creates an object of type `Object` +// in that thread. When the `Thread<>` object is destructed, the destructor waits +// for thread termination. The `Thread<>` constructor blocks until the thread and +// the `Object` are fully created, so after the object creation, it's safe to obtain the +// `Object` stored in this thread. The thread created will always have low priority on +// the platforms that support setting thread priority. +// +// The following properties make this class different from `ThreadPool`: +// +// - Only one thread is created. +// - `Object` will live in a single thread, providing thread affinity. +// - It is safe to use `ThreadLocal` in an `Object` managed by `Thread<>` +// - A `RunLoop` is created for the `Object` thread. +// - `Object` can use `Timer` and do asynchronous I/O, like wait for sockets events. +// +template +class Thread { +public: + template + Thread(std::function prioritySetter_, const std::string& name, TupleArgs&& args) { + std::promise running_; + running = running_.get_future(); + thread = std::thread([this, + name, + capturedArgs = std::forward(args), + runningPromise = std::move(running_), + prioritySetter = std::move(prioritySetter_)]() mutable { + platform::setCurrentThreadName(name); + if (prioritySetter) prioritySetter(); + platform::attachThread(); + + // narrowing the scope to release the Object before we detach the thread + { + util::RunLoop loop_(util::RunLoop::Type::New); + loop = &loop_; + EstablishedActor establishedActor(loop_, object, std::move(capturedArgs)); + + runningPromise.set_value(); + + loop->run(); + + (void) establishedActor; + + loop = nullptr; + } + + platform::detachThread(); + }); + } + + template + Thread(const std::string& name, Args&&... args) + : Thread([] { platform::makeThreadLowPriority(); }, name, std::make_tuple(std::forward(args)...)) {} + + template + Thread(const std::function& prioritySetter, const std::string& name, Args&&... args) + : Thread(prioritySetter, name, std::make_tuple(std::forward(args)...)) {} + + ~Thread() { + if (paused) { + resume(); + } + + std::promise stoppable; + + running.wait(); + + // Invoke a noop task on the run loop to ensure that we're executing + // run() before we call stop() + loop->invoke([&] { + stoppable.set_value(); + }); + + stoppable.get_future().get(); + + loop->stop(); + thread.join(); + } + + // Returns a non-owning reference to `Object` that + // can be used to send messages to `Object`. It is safe + // to the non-owning reference to outlive this object + // and be used after the `Thread<>` gets destroyed. + ActorRef> actor() { + return object.self(); + } + + // Pauses the `Object` thread. It will prevent the object to wake + // up from events such as timers and file descriptor I/O. Messages + // sent to a paused `Object` will be queued and only processed after + // `resume()` is called. + void pause() { + MBGL_VERIFY_THREAD(tid); + + assert(!paused); + + paused = std::make_unique>(); + resumed = std::make_unique>(); + + auto pausing = paused->get_future(); + + running.wait(); + + loop->invoke(RunLoop::Priority::High, [this] { + auto resuming = resumed->get_future(); + paused->set_value(); + resuming.get(); + }); + + pausing.get(); + } + + // Resumes the `Object` thread previously paused by `pause()`. + void resume() { + MBGL_VERIFY_THREAD(tid); + + assert(paused); + + resumed->set_value(); + + resumed.reset(); + paused.reset(); + } + +private: + MBGL_STORE_THREAD(tid); + + AspiringActor object; + + std::thread thread; + + std::future running; + + std::unique_ptr> paused; + std::unique_ptr> resumed; + + util::RunLoop* loop = nullptr; +}; + +// Returns function, that once invoked, will set a thread priority for +// a thread `threadType` based on a setting provided by corresponding +// Settings' platform::EXPERIMENTAL_THREAD_PRIORITY_* value. +std::function makeThreadPrioritySetter(std::string threadType); + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/tileset.hpp b/mapbox-gl-native/include/mbgl/util/tileset.hpp new file mode 100644 index 000000000..c6e41fc4a --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/tileset.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +class Tileset { +public: + enum class Scheme : bool { XYZ, TMS }; + enum class DEMEncoding : bool { Mapbox, Terrarium }; + + std::vector tiles; + Range zoomRange; + std::string attribution; + Scheme scheme; + // DEMEncoding is not supported by the TileJSON spec + DEMEncoding encoding; + optional bounds; + + Tileset(std::vector tiles_ = std::vector(), + Range zoomRange_ = {0, util::DEFAULT_MAX_ZOOM}, + std::string attribution_ = {}, + Scheme scheme_ = Scheme::XYZ, + DEMEncoding encoding_ = DEMEncoding::Mapbox) + : tiles(std::move(tiles_)), + zoomRange(zoomRange_), + attribution(std::move(attribution_)), + scheme(scheme_), + encoding(encoding_){}; + + // TileJSON also includes center and zoom but they are not used by mbgl. + + friend bool operator==(const Tileset& lhs, const Tileset& rhs) { + return std::tie(lhs.tiles, lhs.zoomRange, lhs.attribution, lhs.scheme, lhs.bounds) + == std::tie(rhs.tiles, rhs.zoomRange, rhs.attribution, rhs.scheme, rhs.bounds); + } + + friend bool operator!=(const Tileset& lhs, const Tileset& rhs) { + return !(lhs == rhs); + } +}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/timer.hpp b/mapbox-gl-native/include/mbgl/util/timer.hpp new file mode 100644 index 000000000..7d76e3b79 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/timer.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include +#include + +namespace mbgl { +namespace util { + +class Timer : private util::noncopyable { +public: + Timer(); + ~Timer(); + + void start(Duration timeout, Duration repeat, std::function&&); + void stop(); + +private: + class Impl; + std::unique_ptr impl; +}; + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/traits.hpp b/mapbox-gl-native/include/mbgl/util/traits.hpp new file mode 100644 index 000000000..e37144e60 --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/traits.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include + +namespace mbgl { + +template +constexpr auto underlying_type(T t) -> typename std::underlying_type_t { + return typename std::underlying_type_t(t); +} + +template struct is_utf16char_like: std::false_type {}; +template struct is_utf16char_like: is_utf16char_like {}; +template <> struct is_utf16char_like: std::true_type {}; +template <> struct is_utf16char_like: std::true_type {}; +template <> struct is_utf16char_like: std::true_type {}; + +template using is_utf16char_like_pointer = std::integral_constant::value + && is_utf16char_like::type>::value>; + +template +typename std::enable_if::value && is_utf16char_like_pointer::value, OutPointer>::type utf16char_cast(InChar *in) +{ + return reinterpret_cast(in); +} + +template +struct is_linear_container : std::false_type {}; + +template +struct is_linear_container> : std::true_type {}; +template +struct is_linear_container> : std::true_type {}; + +} // namespace mbgl diff --git a/mapbox-gl-native/include/mbgl/util/type_list.hpp b/mapbox-gl-native/include/mbgl/util/type_list.hpp new file mode 100644 index 000000000..1b594bf7f --- /dev/null +++ b/mapbox-gl-native/include/mbgl/util/type_list.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +namespace mbgl { + +template +struct TypeList { + template