diff options
139 files changed, 9591 insertions, 4587 deletions
diff --git a/.travis.yml b/.travis.yml index ec877eb68b..eb4e700b1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ before_install: install: - make setup +- npm install git+https://github.com/mapbox/mapbox-gl-test-suite.git before_script: - rm -rf mapnik-packaging/out/packages @@ -30,10 +31,7 @@ script: - make linux -j4 BUILDTYPE=${BUILDTYPE} - make test -j4 BUILDTYPE=${BUILDTYPE} - ./scripts/run_tests.sh -- ./scripts/compare_images.js - -after_script: -- ./scripts/deploy_results.sh +- (cd ./node_modules/mapbox-gl-test-suite/ && (./bin/compare_images.js; ./bin/deploy_results.sh)) notifications: hipchat: @@ -78,8 +78,10 @@ xproj: config.gypi macosx/mapboxgl-app.gyp clear_xcode_cache node open ./build/macosx/mapboxgl-app.xcodeproj # build iOS project for Xcode -iproj: config.gypi ios/mapbox-gl-cocoa/app/mapboxgl-app.gyp clear_xcode_cache node +iproj-cli: config.gypi ios/mapbox-gl-cocoa/app/mapboxgl-app.gyp clear_xcode_cache node deps/run_gyp ios/mapbox-gl-cocoa/app/mapboxgl-app.gyp --depth=. --generator-output=./build -f xcode + +iproj: iproj-cli open ./build/ios/mapbox-gl-cocoa/app/mapboxgl-app.xcodeproj # build Linux project for Xcode (Runs on Mac OS X too, but without platform-specific code) @@ -5,14 +5,15 @@ implemented in C++11, currently targeting iOS, OS X, and Ubuntu Linux. # Depends - - Modern C++ compiler that supports `-std=c++11` (On OSX clang++, on Linux g++-4.8 or g++-4.9) + - Modern C++ compiler that supports `-std=c++11` (On OS X clang++, on Linux g++-4.8 or g++-4.9) - Boost headers - `libpng` - `libuv` - `glfw3` - `libcurl` (depends on OpenSSL; Linux only) - `libboost_regex` (Linux only) - - Homebrew (for build on OS X) + - Apple Command Line Tools (for build on OS X; available at [Apple Developer](https://developer.apple.com/downloads)) + - [Homebrew](http://brew.sh) (for build on OS X) - Python 2.x (for build only) - Node.js (for build only) @@ -33,6 +34,8 @@ The demo applications use Mapbox vector tiles, which **require a Mapbox account For iOS and OS X use of the demo apps in Xcode, setup the access token by editing the scheme for the application target, then adding an environment variable with the name `MAPBOX_ACCESS_TOKEN`. +![edit scheme](https://cloud.githubusercontent.com/assets/98601/3647749/30f74f26-1102-11e4-84af-f1be853b4e38.png) + ![setting access token in Xcode scheme](https://cloud.githubusercontent.com/assets/52399/3543326/9e7cfbb8-0860-11e4-8def-3684a9028b61.png) ## OS X diff --git a/bin/build-style.js b/bin/build-style.js deleted file mode 100755 index 06d4cc0808..0000000000 --- a/bin/build-style.js +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -var mkdirp = require('mkdirp'); -var path = require('path'); -var fs = require('fs'); - -var data = JSON.stringify(require(path.join(process.cwd(), process.argv[2]))); -var out = path.join(process.argv[3], 'style.min.js'); - -mkdirp.sync(path.dirname(out)); -fs.writeFileSync(out, data); diff --git a/bin/default.style.json b/bin/default.style.json deleted file mode 100755 index aea2a1a3ac..0000000000 --- a/bin/default.style.json +++ /dev/null @@ -1,2244 +0,0 @@ -{ - "version": 3, - "sprite": "https://www.mapbox.com/mapbox-gl-styles/sprites/outdoors", - "glyphs": "https://mapbox.s3.amazonaws.com/gl-glyphs-256/{fontstack}/{range}.pbf", - "sources": { - "mapbox": { - "type": "vector", - "url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5", - "maxZoom": 14 - } - }, - "constants": { - "@land": "rgb(244,239,225)", - "@water": "#cdd", - "@water_dark": "#185869", - "@crop": "#eeeed4", - "@grass": "#e6e6cc", - "@scrub": "#dfe5c8", - "@wood": "#cee2bd", - "@snow": "#f4f8ff", - "@rock": "#ddd", - "@sand": "#ffd", - "@cemetery": "#edf4ed", - "@pitch": "#fff", - "@park": "#d4e4bc", - "@piste": "blue", - "@school": "#e8dfe0", - "@hospital": "#f8eee0", - "@builtup": "#f6faff", - "@case": "#fff", - "@motorway": "#cda0a0", - "@main": "#ddc0b9", - "@street": "#fff", - "@text": "#666", - "@text_stroke": "rgba(255,255,255,0.8)", - "@country_text": "#222", - "@marine_text": "#a0bdc0", - "@water_text": "#185869", - "@land_night": "#017293", - "@water_night": "#103", - "@water_dark_night": "#003366", - "@crop_night": "#178d96", - "@grass_night": "#23948a", - "@scrub_night": "#31a186", - "@wood_night": "#45b581", - "@park_night": "#51bd8b", - "@snow_night": "#5ad9fe", - "@rock_night": "#999", - "@sand_night": "#437162", - "@cemetery_night": "#218c96", - "@pitch_night": "rgba(255,255,255,0.2)", - "@school_night": "#01536a", - "@hospital_night": "#015e7a", - "@builtup_night": "#014b60", - "@admin_night": "#ffb680", - "@text_night": "#fff", - "@text_water_night": "#2a5b8a", - "@text_stroke_night": "#103", - "@text2_stroke_night": "rgba(1,69,89,0.8)", - "@case_night": "#015e7a", - "@street_case_night": "#015b76", - "@motorway_night": "#bbdde7", - "@main_night": "#64b2c9", - "@street_night": "#0186ac", - "@contour_night": "#ffff80", - "@river_canal_width": { - "stops": [[10, 0.5], [11, 1], [13, 2], [15, 3]] - }, - "@stream_width": { - "stops": [[12, 0.25], [13, 0.5], [15, 1.5], [17, 2]] - }, - "@motorway_width": { - "stops": [[4, 0], [5, 0.5], [7, 0.8], [9, 1], [10, 1.2], [11, 2], [12, 3], [13, 4], [14, 6], [15, 9], [16, 12], [17, 14]] - }, - "@motorway_casing_width": { - "stops": [[6.5, 0.6], [7, 0.8], [9, 2.8], [10, 3], [11, 4], [12, 5], [13, 6.5], [14, 9], [15, 12], [16, 15], [17, 17]] - }, - "@motorway_link_width": { - "stops": [[11, 1.2], [13, 2], [15, 3], [17, 4]] - }, - "@motorway_link_casing_width": { - "stops": [[11, 2.8], [13, 3.5], [15, 5], [17, 6]] - }, - "@main_width": { - "stops": [[4, 1], [11, 1], [12, 1.5], [13, 2], [14, 3], [15, 6], [16, 10], [17, 12]] - }, - "@main_casing_width": { - "stops": [[8, 2.9], [11, 2.9], [12, 3.5], [13, 4], [14, 5.5], [15, 9], [16, 12], [17, 14]] - }, - "@street_width": { - "stops": [[13.5, 0], [14, 1.5], [15, 3], [16, 8]] - }, - "@street_casing_width": { - "stops": [[12, 0.4], [13, 1], [14, 2.5], [15, 4], [16, 10]] - }, - "@street_casing_opacity": { - "stops": [[13, 0], [13.5, 1]] - }, - "@service_casing_width": { - "stops": [[13, 0.5], [14, 3], [15, 3.5], [16, 4], [17, 5], [18, 6]] - }, - "@runway_width": { - "stops": [[9, 1], [10, 2], [11, 3], [12, 5], [13, 7], [14, 11], [15, 15], [16, 19], [17, 23]] - }, - "@taxiway_width": { - "stops": [[9, 0.2], [11, 0.2], [12, 1], [13, 1.5], [14, 2], [15, 3], [16, 4], [17, 5]] - }, - "@aerialway_width": { - "stops": [[12.5, 0.8], [13, 1.4], [14, 1.6], [15, 2], [16, 2.4], [17, 3]] - }, - "@aerialway_casing_width": { - "stops": [[12.5, 2], [13, 2.5], [14, 3], [15, 3.5], [16, 4], [21, 5]] - }, - "@path_width": { - "stops": [[13, 1.2], [14, 1.5], [15, 1.8]] - }, - "@admin_l2_width": { - "stops": [[1, 0.5], [2, 0.7], [3, 0.7], [4, 0.8], [5, 1], [7, 2], [9, 3]] - }, - "@admin_l3_width": { - "stops": [[5, 0.6], [7, 1], [11, 2]] - }, - "@road_label_1_size": { - "stops": [[12, 11], [13, 12], [14, 13], [15, 14], [16, 16], [17, 18]] - }, - "@road_label_2_size": { - "stops": [[12, 11], [13, 12], [15, 14], [17, 16]] - }, - "@road_label_3_size": { - "stops": [[14, 10], [15, 12], [17, 14]] - }, - "@fence_width": { - "stops": [[16, 0.6], [18, 1]] - }, - "@hedge_width": { - "stops": [[15, 0.6], [16, 1.2], [18, 1.6]] - }, - "@barrier_line_land_width": { - "stops": [[13, 0.4], [14, 0.75], [15, 1.5], [16, 3], [17, 6], [18, 12], [19, 24], [20, 48]] - }, - "@country_label_size": { - "stops": [[0, 14], [11, 24]] - }, - "@poi_label_1-2_size": { - "stops": [[14, 10], [15, 11], [16, 12]] - }, - "@poi_label_3_size": { - "stops": [[15, 10], [16, 11]] - }, - "@hillshade_rasterize": { - "enabled": { - "stops": [[10, false], [11, true]] - }, - "size": { - "stops": [[10, 1024], [11, 512], [12, 256]] - }, - "blur": 1 - } - }, - "layers": [{ - "id": "background", - "type": "background", - "style": { - "background-color": "@land" - }, - "style.night": { - "background-color": "@land_night" - } - }, { - "id": "landcover_snow", - "source": "mapbox", - "source-layer": "landcover", - "filter": { "class": "snow" }, - "type": "fill", - "style": { - "fill-color": "@snow" - }, - "style.night": { - "fill-color": "@snow_night" - } - }, { - "id": "landcover_crop", - "source": "mapbox", - "source-layer": "landcover", - "filter": { "class": "crop" }, - "type": "fill", - "style": { - "fill-color": "@crop" - }, - "style.night": { - "fill-color": "@crop_night" - } - }, { - "id": "landcover_grass", - "source": "mapbox", - "source-layer": "landcover", - "filter": { "class": "grass" }, - "type": "fill", - "style": { - "fill-color": "@grass", - "fill-opacity": { - "stops": [[12, 1], [13, 0.8], [16, 0.2]] - } - }, - "style.night": { - "fill-color": "@grass_night", - "fill-opacity": { - "stops": [[12, 1], [13, 0.8], [16, 0.2]] - } - } - }, { - "id": "landcover_scrub", - "source": "mapbox", - "source-layer": "landcover", - "filter": { "class": "scrub" }, - "type": "fill", - "style": { - "fill-color": "@scrub", - "fill-opacity": { - "stops": [[12, 1], [13, 0.8], [16, 0.2]] - } - }, - "style.night": { - "fill-color": "@scrub_night", - "fill-opacity": { - "stops": [[12, 1], [13, 0.8], [16, 0.2]] - } - } - }, { - "id": "landcover_wood", - "source": "mapbox", - "source-layer": "landcover", - "filter": { "class": "wood" }, - "type": "fill", - "style": { - "fill-color": "@wood", - "fill-opacity": { - "stops": [[12, 1], [13, 0.8], [16, 0.2]] - } - }, - "style.night": { - "fill-color": "@wood_night", - "fill-opacity": { - "stops": [[12, 1], [13, 0.8], [16, 0.2]] - } - } - }, { - "id": "landuse_wood", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "wood" }, - "type": "fill", - "style": { - "fill-color": "@wood" - }, - "style.night": { - "fill-color": "@wood_night", - "fill-opacity": 0.8 - } - }, { - "id": "landuse_school", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "school" }, - "type": "fill", - "style": { - "fill-color": "@school" - }, - "style.night": { - "fill-color": "@school_night" - } - }, { - "id": "landuse_sand", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "sand" }, - "type": "fill", - "style": { - "fill-color": "@sand" - }, - "style.night": { - "fill-color": "@sand_night", - "fill-opacity": 0.8 - } - }, { - "id": "landuse_pitch", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "pitch" }, - "type": "fill", - "style": { - "fill-color": "rgba(255,255,255,0.5)", - "fill-outline-color": "@pitch" - }, - "style.night": { - "fill-color": "@pitch_night", - "fill-outline-color": "@pitch" - } - }, { - "id": "landuse_park", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "park" }, - "type": "fill", - "style": { - "fill-color": "@park" - }, - "style.night": { - "fill-color": "@park_night" - } - }, { - "id": "landuse_industrial", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "industrial" }, - "type": "fill", - "style": { - "fill-color": "rgba(246,250,255,0.5)" - }, - "style.night": { - "fill-color": "@builtup_night" - } - }, { - "id": "landuse_scrub", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "scrub" }, - "type": "fill", - "style": { - "fill-color": "@scrub" - }, - "style.night": { - "fill-color": "@scrub_night", - "fill-opacity": 0.8 - } - }, { - "id": "landuse_grass", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "grass" }, - "type": "fill", - "style": { - "fill-color": "@grass" - }, - "style.night": { - "fill-color": "@grass_night", - "fill-opacity": 0.8 - } - }, { - "id": "landuse_crop", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "crop" }, - "type": "fill", - "style": { - "fill-color": "@crop" - }, - "style.night": { - "fill-color": "@crop_night", - "fill-opacity": 0.8 - } - }, { - "id": "landuse_rock", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "rock" }, - "type": "fill", - "style": { - "fill-color": "@rock" - }, - "style.night": { - "fill-color": "@rock_night", - "fill-opacity": 0.8 - } - }, { - "id": "landuse_snow", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "snow" }, - "type": "fill", - "style": { - "fill-color": "@snow" - }, - "style.night": { - "fill-color": "@snow_night", - "fill-opacity": 0.8 - } - }, { - "id": "landuse_hospital", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "hospital" }, - "type": "fill", - "style": { - "fill-color": "@hospital" - }, - "style.night": { - "fill-color": "@hospital_night" - } - }, { - "id": "landuse_cemetery", - "source": "mapbox", - "source-layer": "landuse", - "filter": { "class": "cemetery" }, - "type": "fill", - "style": { - "fill-color": "@cemetery" - }, - "style.night": { - "fill-color": "@cemetery_night" - } - }, { - "id": "overlay_wetland", - "source": "mapbox", - "source-layer": "landuse_overlay", - "filter": { "class": ["wetland", "wetland_noveg"] }, - "type": "fill", - "style": { - "fill-color": "rgba(210,225,225,0.2)", - "fill-image": "wetland_noveg_64" - }, - "style.night": { - "fill-color": "rgba(210,225,225,0.2)", - "fill-image": "wetland_noveg_64" - } - }, { - "id": "overlay_breakwater_pier", - "source": "mapbox", - "source-layer": "landuse_overlay", - "filter": { "class": ["breakwater", "pier"] }, - "type": "fill", - "style": { - "fill-color": "@land" - }, - "style.night": { - "fill-color": "@land_night" - } - }, { - "id": "waterway_river_canal", - "source": "mapbox", - "source-layer": "waterway", - "filter": { "type": ["river", "canal"] }, - "type": "line", - "render": { - "line-cap": "round" - }, - "style": { - "line-color": "#87abaf", - "line-width": "@river_canal_width" - }, - "style.night": { - "line-color": "rgb(10,20,71)", - "line-width": "@river_canal_width" - } - }, { - "id": "waterway_stream", - "source": "mapbox", - "source-layer": "waterway", - "filter": { "type": "stream" }, - "type": "line", - "render": { - "line-cap": "round" - }, - "style": { - "line-color": "#87abaf", - "line-width": "@stream_width" - }, - "style.night": { - "line-color": "rgb(10,20,71)", - "line-width": "@stream_width" - } - }, { - "id": "building_shadow", - "source": "mapbox", - "source-layer": "building", - "type": "fill", - "style": { - "fill-color": "#d5d1c6", - "fill-translate": [1, 1], - "fill-opacity": { - "stops": [[15.5, 0], [16, 1]] - }, - "fill-outline-color": "#d5d1c6" - }, - "style.night": { - "fill-color": "#026688", - "fill-translate": [1, 1], - "fill-opacity": { - "stops": [[15.5, 0], [16, 1]] - }, - "fill-outline-color": "#026688" - } - }, { - "id": "building", - "ref": "building_shadow", - "style": { - "fill-color": "#ebe7db" - }, - "style.night": { - "fill-color": "#027797" - } - }, { - "id": "building_wall", - "ref": "building_shadow", - "style": { - "fill-color": "#ebe7db", - "fill-opacity": { - "stops": [[15.5, 0], [16, 0.7]] - }, - "fill-outline-color": "#d5d1c6" - }, - "style.night": { - "fill-color": "#027797", - "fill-opacity": { - "stops": [[15.5, 0], [16, 0.7]] - }, - "fill-outline-color": "#026688" - } - }, { - "id": "hillshade_full_highlight", - "source": "mapbox", - "source-layer": "hillshade", - "filter": { "class": "full_highlight" }, - "type": "fill", - "rasterize": "@hillshade_rasterize", - "style": { - "fill-color": "#fffff3", - "fill-antialias": false, - "fill-opacity": { - "stops": [[14, 0.3], [15, 0.3], [16, 0.2], [17, 0.2], [18, 0.1]] - } - }, - "style.night": { - "fill-color": "#fdfdad", - "fill-antialias": false, - "fill-opacity": { - "stops": [[13, 0.4], [14, 0.3], [16, 0.2], [17, 0.1], [18, 0.05]] - } - } - }, { - "id": "hillshade_medium_highlight", - "source": "mapbox", - "source-layer": "hillshade", - "filter": { "class": "medium_highlight" }, - "type": "fill", - "rasterize": "@hillshade_rasterize", - "style": { - "fill-color": "#ffd", - "fill-antialias": false, - "fill-opacity": { - "stops": [[14, 0.3], [15, 0.3], [16, 0.2], [17, 0.2], [18, 0.1]] - } - }, - "style.night": { - "fill-color": "#ffe1b7", - "fill-antialias": false, - "fill-opacity": { - "stops": [[14, 0.3], [16, 0.2], [17, 0.15], [18, 0.05]] - } - } - }, { - "id": "hillshade_medium_shadow", - "source": "mapbox", - "source-layer": "hillshade", - "filter": { "class": "medium_shadow" }, - "type": "fill", - "rasterize": "@hillshade_rasterize", - "style": { - "fill-color": "#206", - "fill-antialias": false, - "fill-opacity": { - "stops": [[14, 0.08], [15, 0.075], [16, 0.05], [17, 0.05], [18, 0.025]] - } - }, - "style.night": { - "fill-color": "#206", - "fill-antialias": false, - "fill-opacity": { - "stops": [[15, 0.3], [16, 0.2], [17, 0.1], [18, 0.05]] - } - } - }, { - "id": "hillshade_full_shadow", - "source": "mapbox", - "source-layer": "hillshade", - "filter": { "class": "full_shadow" }, - "type": "fill", - "rasterize": "@hillshade_rasterize", - "style": { - "fill-color": "#103", - "fill-antialias": false, - "fill-opacity": { - "stops": [[14, 0.08], [15, 0.075], [16, 0.05], [17, 0.05], [18, 0.025]] - } - }, - "style.night": { - "fill-color": "#103", - "fill-antialias": false, - "fill-opacity": { - "stops": [[15, 0.3], [16, 0.2], [17, 0.1], [18, 0.05]] - } - } - }, { - "id": "contour_line_loud", - "source": "mapbox", - "source-layer": "contour", - "filter": { "index": 5 }, - "type": "line", - "render": { - "line-join": "round" - }, - "style": { - "line-color": "#008", - "line-width": 0.9, - "line-opacity": { - "stops": [[11, 0.05], [12, 0.11]] - } - }, - "style.night": { - "line-color": "@contour_night", - "line-width": 0.9, - "line-opacity": { - "stops": [[11, 0.1], [12, 0.2]] - } - } - }, { - "id": "contour_line_regular", - "source": "mapbox", - "source-layer": "contour", - "type": "line", - "render": { - "line-join": "round" - }, - "style": { - "line-color": "#008", - "line-width": 0.5, - "line-opacity": { - "stops": [[11, 0.05], [12, 0.11]] - } - }, - "style.night": { - "line-color": "@contour_night", - "line-width": 0.5, - "line-opacity": { - "stops": [[11, 0.1], [12, 0.4]] - } - } - }, { - "id": "barrier_line_gate", - "source": "mapbox", - "source-layer": "barrier_line", - "filter": { "class": "gate" }, - "type": "line", - "style": { - "line-width": 2.5, - "line-color": "#aab" - }, - "style.night": { - "line-width": 2.5, - "line-color": "#59596f" - } - }, { - "id": "barrier_line_fence", - "source": "mapbox", - "source-layer": "barrier_line", - "filter": { "class": "fence" }, - "type": "line", - "style": { - "line-color": "#aeada3", - "line-width": "@fence_width" - }, - "style.night": { - "line-color": "#014b61", - "line-width": "@fence_width" - } - }, { - "id": "barrier_line_hedge", - "source": "mapbox", - "source-layer": "barrier_line", - "filter": { "class": "hedge" }, - "type": "line", - "style": { - "line-color": "#8de99b", - "line-width": "@hedge_width" - }, - "style.night": { - "line-color": "#2e7a57", - "line-width": "@hedge_width" - } - }, { - "id": "barrier_line_land", - "source": "mapbox", - "source-layer": "barrier_line", - "filter": { "class": "land" }, - "type": "line", - "style": { - "line-color": "@land", - "line-width": "@barrier_line_land_width" - }, - "style.night": { - "line-color": "@land_night", - "line-width": "@barrier_line_land_width" - } - }, { - "id": "barrier_line_land_fill", - "source": "mapbox", - "source-layer": "barrier_line", - "filter": { "class": "land" }, - "type": "fill", - "style": { - "fill-color": "@land" - }, - "style.night": { - "fill-color": "@land_night" - } - }, { - "id": "barrier_line_cliff", - "source": "mapbox", - "source-layer": "barrier_line", - "filter": { "class": "cliff" }, - "type": "line", - "style": { - "line-color": "#987", - "line-width": 4 - }, - "style.night": { - "line-color": "#63574b", - "line-width": 4 - } - }, { - "id": "water", - "source": "mapbox", - "source-layer": "water", - "type": "fill", - "style": { - "fill-color": "@water", - "fill-outline-color": "#a2bdc0" - }, - "style.night": { - "fill-color": "@water_night", - "fill-outline-color": "@water_dark_night" - } - }, { - "id": "aeroway_fill", - "source": "mapbox", - "source-layer": "aeroway", - "type": "fill", - "style": { - "fill-color": "#ddd" - }, - "style.night": { - "fill-color": "#367" - } - }, { - "id": "aeroway_runway", - "source": "mapbox", - "source-layer": "aeroway", - "filter": { "type": "runway" }, - "type": "line", - "style": { - "line-color": "#ddd", - "line-width": "@runway_width" - }, - "style.night": { - "line-color": "#367", - "line-width": "@runway_width" - } - }, { - "id": "aeroway_taxiway", - "source": "mapbox", - "source-layer": "aeroway", - "filter": { "type": "taxiway" }, - "type": "line", - "style": { - "line-color": "#ddd", - "line-width": "@taxiway_width" - }, - "style.night": { - "line-color": "#367", - "line-width": "@taxiway_width" - } - }, { - "id": "tunnel_motorway_link_casing", - "source": "mapbox", - "source-layer": "tunnel", - "filter": { "class": "motorway_link" }, - "type": "line", - "style": { - "line-color": "@case", - "line-dasharray": [6, 6], - "line-width": "@motorway_link_casing_width" - }, - "style.night": { - "line-color": "@case_night", - "line-dasharray": [6, 6], - "line-width": "@motorway_link_casing_width" - } - }, { - "id": "tunnel_service_casing", - "source": "mapbox", - "source-layer": "tunnel", - "filter": { "class": "service" }, - "type": "line", - "style": { - "line-color": "#000", - "line-opacity": 0.04, - "line-dasharray": [6, 6], - "line-width": "@service_casing_width" - }, - "style.night": { - "line-color": "#000", - "line-opacity": 0.04, - "line-dasharray": [6, 6], - "line-width": "@service_casing_width" - } - }, { - "id": "tunnel_main_casing", - "source": "mapbox", - "source-layer": "tunnel", - "filter": { "class": "main" }, - "type": "line", - "style": { - "line-color": "@case", - "line-dasharray": [6, 6], - "line-width": "@main_casing_width", - "line-opacity": { - "stops": [[8, 0], [9, 1]] - } - }, - "style.night": { - "line-color": "@case_night", - "line-dasharray": [6, 6], - "line-width": "@main_casing_width", - "line-opacity": { - "stops": [[8, 0], [9, 1]] - } - } - }, { - "id": "tunnel_street_casing", - "source": "mapbox", - "source-layer": "tunnel", - "filter": { "class": ["street", "street_limited"] }, - "type": "line", - "style": { - "line-color": "#d9d5c6", - "line-width": "@street_casing_width", - "line-opacity": "@street_casing_opacity" - }, - "style.night": { - "line-color": "@street_case_night", - "line-width": "@street_casing_width", - "line-opacity": "@street_casing_opacity" - } - }, { - "id": "tunnel_motorway_link", - "ref": "tunnel_motorway_link_casing", - "style": { - "line-color": "#e6cec7", - "line-width": "@motorway_link_width" - }, - "style.night": { - "line-color": "#78b0c1", - "line-width": "@motorway_link_width" - } - }, { - "id": "tunnel_service", - "ref": "tunnel_service_casing", - "style": { - "line-color": "#e6cec7", - "line-width": 2 - }, - "style.night": { - "line-color": "#017ca0", - "line-width": 2 - } - }, { - "id": "tunnel_street", - "ref": "tunnel_street_casing", - "style": { - "line-color": "#d9d5c6", - "line-width": "@street_width" - }, - "style.night": { - "line-color": "@street_night", - "line-width": "@street_width" - } - }, { - "id": "tunnel_main", - "ref": "tunnel_main_casing", - "style": { - "line-color": "#e6cec7", - "line-width": "@main_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - }, - "style.night": { - "line-color": "#78b0c1", - "line-width": "@main_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - } - }, { - "id": "tunnel_motorway_casing", - "source": "mapbox", - "source-layer": "tunnel", - "filter": { "class": "motorway" }, - "type": "line", - "style": { - "line-color": "@case", - "line-dasharray": [6, 6], - "line-width": "@motorway_casing_width", - "line-opacity": { - "stops": [[8.5, 0], [9, 1]] - } - }, - "style.night": { - "line-color": "@case_night", - "line-dasharray": [6, 6], - "line-width": "@motorway_casing_width", - "line-opacity": { - "stops": [[8.5, 0], [9, 1]] - } - } - }, { - "id": "tunnel_motorway", - "ref": "tunnel_motorway_casing", - "style": { - "line-color": "#e6cec7", - "line-width": "@motorway_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - }, - "style.night": { - "line-color": "#78b0c1", - "line-width": "@motorway_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - } - }, { - "id": "road_path_case", - "source": "mapbox", - "source-layer": "road", - "filter": { "class": "path" }, - "type": "line", - "style": { - "line-color": "#ffd", - "line-opacity": 0.4, - "line-width": { - "stops": [[14, 3], [15, 4]] - } - }, - "style.night": { - "line-color": "@land_night", - "line-opacity": 0.2 - } - }, { - "id": "road_path_footway", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "type": "footway" }, - "type": "line", - "style": { - "line-color": "#bba", - "line-dasharray": [10, 4], - "line-width": "@path_width" - }, - "style.night": { - "line-color": "#fff", - "line-dasharray": [10, 4], - "line-width": "@path_width" - } - }, { - "id": "road_path_path", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "type": "path" }, - "type": "line", - "style": { - "line-color": "#987", - "line-dasharray": [10, 4], - "line-opacity": 0.8, - "line-width": { - "stops": [[13, 0.8], [14, 0.9], [15, 1.2]] - } - }, - "style.night": { - "line-color": "#fff", - "line-dasharray": [10, 4], - "line-opacity": 0.8, - "line-width": { - "stops": [[13, 0.8], [14, 0.9], [15, 1.2]] - } - } - }, { - "id": "road_path_cycleway", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "type": "cycleway" }, - "type": "line", - "style": { - "line-color": "#488", - "line-dasharray": [10, 4], - "line-width": "@path_width" - }, - "style.night": { - "line-color": "#94e6ff", - "line-dasharray": [10, 4], - "line-width": "@path_width" - } - }, { - "id": "road_path_mtb", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "type": "mtb" }, - "type": "line", - "style": { - "line-color": "#488", - "line-dasharray": [12, 4], - "line-width": "@path_width" - }, - "style.night": { - "line-color": "#94e6ff", - "line-dasharray": [12, 4], - "line-width": "@path_width" - } - }, { - "id": "road_path_piste", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "type": "piste" }, - "type": "line", - "style": { - "line-color": "#87b", - "line-dasharray": [8, 4], - "line-width": "@path_width" - }, - "style.night": { - "line-color": "#715dae", - "line-dasharray": [8, 4], - "line-width": "@path_width" - } - }, { - "id": "road_path_steps", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "type": "steps" }, - "type": "line", - "style": { - "line-color": "#bba", - "line-dasharray": [10, 4], - "line-width": 4 - }, - "style.night": { - "line-color": "#016684", - "line-dasharray": [10, 4], - "line-opacity": 0.3, - "line-width": 6 - } - }, { - "id": "road_major_rail", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "class": "major_rail" }, - "type": "line", - "style": { - "line-color": "#c8c4c0", - "line-width": 0.8 - }, - "style.night": { - "line-color": "#c8c4c0", - "line-width": 0.8 - } - }, { - "id": "road_motorway_link_casing", - "source": "mapbox", - "source-layer": "road", - "filter": { "class": "motorway_link" }, - "type": "line", - "render": { - "line-join": "round", - "line-cap": "round" - }, - "style": { - "line-color": "@case", - "line-width": "@motorway_link_casing_width" - }, - "style.night": { - "line-color": "@case_night", - "line-width": "@motorway_link_casing_width" - } - }, { - "id": "road_service_casing", - "source": "mapbox", - "source-layer": "road", - "filter": { "class": "service" }, - "type": "line", - "render": { - "line-join": "round", - "line-cap": "round" - }, - "style": { - "line-color": "#000", - "line-opacity": 0.04, - "line-width": "@service_casing_width" - }, - "style.night": { - "line-color": "#000", - "line-opacity": 0.04, - "line-width": "@service_casing_width" - } - }, { - "id": "road_main_casing", - "source": "mapbox", - "source-layer": "road", - "filter": { "class": "main" }, - "type": "line", - "render": { - "line-join": "round", - "line-cap": "round" - }, - "style": { - "line-color": "@case", - "line-width": "@main_casing_width", - "line-opacity": { - "stops": [[8, 0], [9, 1]] - } - }, - "style.night": { - "line-color": "@case_night", - "line-width": "@main_casing_width", - "line-opacity": { - "stops": [[8, 0], [9, 1]] - } - } - }, { - "id": "road_street_casing", - "source": "mapbox", - "source-layer": "road", - "filter": { "class": ["street", "street_limited"] }, - "type": "line", - "render": { - "line-join": "round", - "line-cap": "round" - }, - "style": { - "line-color": "#d9d5c6", - "line-width": "@street_casing_width", - "line-opacity": "@street_casing_opacity" - }, - "style.night": { - "line-color": "@street_case_night", - "line-width": "@street_casing_width", - "line-opacity": "@street_casing_opacity" - } - }, { - "id": "road_motorway_link", - "ref": "road_motorway_link_casing", - "style": { - "line-color": "@motorway", - "line-width": "@motorway_link_width" - }, - "style.night": { - "line-color": "@motorway_night", - "line-width": "@motorway_link_width" - } - }, { - "id": "road_service", - "ref": "road_service_casing", - "style": { - "line-color": "@street", - "line-width": 2 - }, - "style.night": { - "line-color": "@street_night", - "line-width": 2 - } - }, { - "id": "road_street", - "ref": "road_street_casing", - "style": { - "line-color": "@street", - "line-width": "@street_width" - }, - "style.night": { - "line-color": "@street_night", - "line-width": "@street_width" - } - }, { - "id": "road_main", - "ref": "road_main_casing", - "style": { - "line-color": "@main", - "line-width": "@main_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - }, - "style.night": { - "line-color": "@main_night", - "line-width": "@main_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - } - }, { - "id": "road_motorway_casing", - "source": "mapbox", - "source-layer": "road", - "filter": { "class": "motorway" }, - "type": "line", - "render": { - "line-join": "round", - "line-cap": "round" - }, - "style": { - "line-color": "@case", - "line-width": "@motorway_casing_width", - "line-opacity": { - "stops": [[8.5, 0], [9, 1]] - } - }, - "style.night": { - "line-color": "@case_night", - "line-width": "@motorway_casing_width", - "line-opacity": { - "stops": [[8.5, 0], [9, 1]] - } - } - }, { - "id": "road_motorway", - "ref": "road_motorway_casing", - "style": { - "line-color": "@motorway", - "line-width": "@motorway_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - }, - "style.night": { - "line-color": "@motorway_night", - "line-width": "@motorway_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - } - }, { - "id": "road_major_rail_hatching", - "ref": "road_major_rail", - "style": { - "line-color": "#c8c4c0", - "line-dasharray": [2, 31], - "line-width": 5 - }, - "style.night": { - "line-color": "#c8c4c0", - "line-dasharray": [2, 31], - "line-width": 5 - } - }, { - "id": "bridge_motorway_link_casing", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "class": "motorway_link" }, - "type": "line", - "style": { - "line-color": "@case", - "line-width": "@motorway_link_casing_width" - }, - "style.night": { - "line-color": "@case_night", - "line-width": "@motorway_link_casing_width" - } - }, { - "id": "bridge_service_casing", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "class": "service" }, - "type": "line", - "style": { - "line-color": "#000", - "line-opacity": 0.04, - "line-width": "@service_casing_width" - }, - "style.night": { - "line-color": "#000", - "line-opacity": 0.04, - "line-width": "@service_casing_width" - } - }, { - "id": "bridge_main_casing", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "class": "main" }, - "type": "line", - "style": { - "line-color": "@case", - "line-width": "@main_casing_width", - "line-opacity": { - "stops": [[8, 0], [9, 1]] - } - }, - "style.night": { - "line-color": "@case_night", - "line-width": "@main_casing_width", - "line-opacity": { - "stops": [[8, 0], [9, 1]] - } - } - }, { - "id": "bridge_street_casing", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "class": ["street", "street_limited"] }, - "type": "line", - "style": { - "line-color": "#d9d5c6", - "line-width": "@street_casing_width", - "line-opacity": "@street_casing_opacity" - }, - "style.night": { - "line-color": "@street_case_night", - "line-width": "@street_casing_width", - "line-opacity": "@street_casing_opacity" - } - }, { - "id": "bridge_motorway_link", - "ref": "bridge_motorway_link_casing", - "style": { - "line-color": "@motorway", - "line-width": "@motorway_link_width" - }, - "style.night": { - "line-color": "@motorway_night", - "line-width": "@motorway_link_width" - } - }, { - "id": "bridge_service", - "ref": "bridge_service_casing", - "style": { - "line-color": "@street", - "line-width": 2 - }, - "style.night": { - "line-color": "@street_night", - "line-width": 2 - } - }, { - "id": "bridge_street", - "ref": "bridge_street_casing", - "style": { - "line-color": "@street", - "line-width": "@street_width" - }, - "style.night": { - "line-color": "@street_night", - "line-width": "@street_width" - } - }, { - "id": "bridge_main", - "ref": "bridge_main_casing", - "style": { - "line-color": "@main", - "line-width": "@main_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - }, - "style.night": { - "line-color": "@main_night", - "line-width": "@main_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - } - }, { - "id": "bridge_motorway_casing", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "class": "motorway" }, - "type": "line", - "style": { - "line-color": "@case", - "line-width": "@motorway_casing_width", - "line-opacity": { - "stops": [[8.5, 0], [9, 1]] - } - }, - "style.night": { - "line-color": "@case_night", - "line-width": "@motorway_casing_width", - "line-opacity": { - "stops": [[8.5, 0], [9, 1]] - } - } - }, { - "id": "bridge_motorway", - "ref": "bridge_motorway_casing", - "style": { - "line-color": "@motorway", - "line-width": "@motorway_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - }, - "style.night": { - "line-color": "@motorway_night", - "line-width": "@motorway_width", - "line-opacity": { - "stops": [[5.5, 0], [6, 1]] - } - } - }, { - "id": "bridge_aerialway_casing", - "source": "mapbox", - "source-layer": "bridge", - "filter": { "class": "aerialway" }, - "type": "line", - "style": { - "line-color": "white", - "line-opacity": 0.5, - "line-width": "@aerialway_casing_width" - }, - "style.night": { - "line-color": "white", - "line-opacity": 0.5, - "line-width": "@aerialway_casing_width" - } - }, { - "id": "bridge_aerialway", - "ref": "bridge_aerialway_casing", - "style": { - "line-color": "#876", - "line-opacity": 0.5, - "line-width": "@aerialway_width" - }, - "style.night": { - "line-color": "#876", - "line-opacity": 0.5, - "line-width": "@aerialway_width" - } - }, { - "id": "admin_l3", - "source": "mapbox", - "source-layer": "admin", - "filter": { "admin_level": [3, 4, 5] }, - "type": "line", - "render": { - "line-join": "round" - }, - "style": { - "line-color": "#88a", - "line-dasharray": [60, 20], - "line-opacity": { - "stops": [[3, 0], [5, 1]] - }, - "line-width": "@admin_l3_width" - }, - "style.night": { - "line-color": "@admin_night", - "line-dasharray": [60, 20], - "line-opacity": { - "stops": [[3, 0], [5, 1]] - }, - "line-width": "@admin_l3_width" - } - }, { - "id": "admin_l2", - "source": "mapbox", - "source-layer": "admin", - "filter": { "admin_level": 2 }, - "type": "line", - "render": { - "line-join": "round", - "line-cap": "round" - }, - "style": { - "line-color": "#88a", - "line-width": "@admin_l2_width" - }, - "style.night": { - "line-color": "@admin_night", - "line-width": "@admin_l2_width" - } - }, { - "id": "admin_maritime_cover", - "source": "mapbox", - "source-layer": "admin", - "filter": { "maritime": 1 }, - "type": "line", - "render": { - "line-join": "round", - "line-cap": "round" - }, - "style": { - "line-color": "@water", - "line-width": 5 - }, - "style.night": { - "line-color": "@water_night", - "line-width": 5 - } - }, { - "id": "admin_maritime", - "ref": "admin_maritime_cover", - "style": { - "line-color": "#c0d6d6", - "line-width": { - "stops": [[5, 1], [7, 2], [11, 3]] - } - }, - "style.night": { - "line-color": "#0a1347", - "line-width": { - "stops": [[5, 1], [7, 2], [11, 3]] - } - } - }, { - "id": "country_label_line", - "source": "mapbox", - "source-layer": "country_label_line", - "type": "line", - "render": { - "text-max-width": 5 - }, - "style": { - "line-color": "@country_text", - "line-width": 0.5, - "line-opacity": 0.5 - }, - "style.night": { - "line-color": "@text_night", - "line-width": 0.5, - "line-opacity": 0.5 - } - }, { - "id": "country_label", - "source": "mapbox", - "source-layer": "country_label", - "filter": { "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold, Arial Unicode MS Bold", - "text-max-size": 24, - "text-max-width": 5 - }, - "style": { - "text-color": "@country_text", - "text-halo-color": "rgba(255,255,255,0.5)", - "text-halo-width": 0.5, - "text-size": "@country_label_size" - }, - "style.night": { - "text-color": "@text_night", - "text-halo-color": "@text2_stroke_night", - "text-halo-width": 0.4, - "text-size": "@country_label_size" - } - }, { - "id": "marine_label_line_1", - "source": "mapbox", - "source-layer": "marine_label", - "filter": { "$type": "LineString", "labelrank": 1 }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "curve", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 30, - "text-max-angle": 0.5, - "text-letter-spacing": 0.4 - }, - "style": { - "text-color": "@marine_text", - "text-size": { - "stops": [[2, 20], [3, 25], [4, 30], [21, 30]] - }, - "text-halo-color": "@water" - }, - "style.night": { - "text-color": "@water_dark_night", - "text-size": { - "stops": [[2, 20], [3, 25], [4, 30], [21, 30]] - }, - "text-halo-color": "@water_night" - } - }, { - "id": "marine_label_line_2", - "source": "mapbox", - "source-layer": "marine_label", - "filter": { "$type": "LineString", "labelrank": 2 }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "curve", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 24, - "text-max-angle": 0.5 - }, - "style": { - "text-color": "@marine_text", - "text-size": { - "stops": [[2, 13], [3, 14], [4, 20], [5, 24], [21, 24]] - }, - "text-halo-color": "@water" - }, - "style.night": { - "text-color": "@water_dark_night", - "text-size": { - "stops": [[2, 13], [3, 14], [4, 20], [5, 24], [21, 24]] - }, - "text-halo-color": "@water_night" - } - }, { - "id": "marine_label_line_3", - "source": "mapbox", - "source-layer": "marine_label", - "filter": { "$type": "LineString", "labelrank": 3 }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "curve", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 18, - "text-max-angle": 0.5 - }, - "style": { - "text-color": "@marine_text", - "text-size": { - "stops": [[2, 12], [3, 13], [4, 15], [5, 18], [21, 18]] - }, - "text-halo-color": "@water" - }, - "style.night": { - "text-color": "@water_dark_night", - "text-size": { - "stops": [[2, 12], [3, 13], [4, 15], [5, 18], [21, 18]] - }, - "text-halo-color": "@water_night" - } - }, { - "id": "marine_label_line_other", - "source": "mapbox", - "source-layer": "marine_label", - "filter": { "$type": "LineString", "labelrank": [4, 5, 6] }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "curve", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 16, - "text-max-angle": 0.5 - }, - "style": { - "text-color": "@marine_text", - "text-size": { - "stops": [[3, 12], [4, 14], [5, 16], [21, 16]] - }, - "text-halo-color": "@water" - }, - "style.night": { - "text-color": "@water_dark_night", - "text-size": { - "stops": [[3, 12], [4, 14], [5, 16], [21, 16]] - }, - "text-halo-color": "@water_night" - } - }, { - "id": "marine_label_point_1", - "source": "mapbox", - "source-layer": "marine_label", - "filter": { "$type": "Point", "labelrank": 1 }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 30, - "text-max-width": 8, - "text-letter-spacing": 0.4, - "text-line-height": 2 - }, - "style": { - "text-color": "@marine_text", - "text-size": { - "stops": [[2, 20], [3, 25], [4, 30], [21, 30]] - }, - "text-halo-color": "@water" - }, - "style.night": { - "text-color": "@water_dark_night", - "text-size": { - "stops": [[2, 20], [3, 25], [4, 30], [21, 30]] - }, - "text-halo-color": "@water_night" - } - }, { - "id": "marine_label_point_2", - "source": "mapbox", - "source-layer": "marine_label", - "filter": { "$type": "Point", "labelrank": 2 }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 24, - "text-max-width": 8, - "text-letter-spacing": 0.2, - "text-line-height": 1.5 - }, - "style": { - "text-color": "@marine_text", - "text-size": { - "stops": [[2, 13], [3, 14], [4, 20], [5, 24], [21, 24]] - }, - "text-halo-color": "@water" - }, - "style.night": { - "text-color": "@water_dark_night", - "text-size": { - "stops": [[2, 13], [3, 14], [4, 20], [5, 24], [21, 24]] - }, - "text-halo-color": "@water_night" - } - }, { - "id": "marine_label_point_3", - "source": "mapbox", - "source-layer": "marine_label", - "filter": { "$type": "Point", "labelrank": 3 }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 18, - "text-max-width": 8, - "text-letter-spacing": 0.1, - "text-line-height": 1.3 - }, - "style": { - "text-color": "@marine_text", - "text-size": { - "stops": [[2, 12], [3, 13], [4, 15], [5, 18], [21, 18]] - }, - "text-halo-color": "@water" - }, - "style.night": { - "text-color": "@water_dark_night", - "text-size": { - "stops": [[2, 12], [3, 13], [4, 15], [5, 18], [21, 18]] - }, - "text-halo-color": "@water_night" - } - }, { - "id": "marine_label_point_other", - "source": "mapbox", - "source-layer": "marine_label", - "filter": { "$type": "Point", "labelrank": [4, 5, 6] }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 16, - "text-max-width": 8, - "text-letter-spacing": 0.1, - "text-line-height": 1.2 - }, - "style": { - "text-color": "@marine_text", - "text-size": { - "stops": [[3, 12], [4, 14], [5, 16], [21, 16]] - }, - "text-halo-color": "@water" - }, - "style.night": { - "text-color": "@water_dark_night", - "text-size": { - "stops": [[3, 12], [4, 14], [5, 16], [21, 16]] - }, - "text-halo-color": "@water_night" - } - }, { - "id": "state_label", - "source": "mapbox", - "source-layer": "state_label", - "filter": { "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Regular, Arial Unicode MS Regular", - "text-max-size": 16, - "text-max-width": 8 - }, - "style": { - "text-color": "#333", - "text-halo-width": 0.4, - "text-halo-color": "rgba(244,239,225,0.8)", - "text-size": { - "stops": [[2.99, 0], [3, 10], [8.99, 16], [9, 0]] - } - }, - "style.night": { - "text-color": "#fff", - "text-halo-width": 0.4, - "text-halo-color": "@land_night", - "text-size": { - "stops": [[2.99, 0], [3, 10], [8.99, 16], [9, 0]] - } - } - }, { - "id": "place_label_city", - "source": "mapbox", - "source-layer": "place_label", - "filter": { "type": "city", "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold, Arial Unicode MS Bold", - "text-max-size": 20, - "text-max-width": 8 - }, - "style": { - "text-color": "#444", - "text-halo-width": 0.4, - "text-halo-color": "@text_stroke", - "text-size": { - "stops": [[2.99, 0], [3, 10], [6, 14], [13.99, 20], [14, 0]] - } - }, - "style.night": { - "text-color": "#fff", - "text-halo-width": 0.4, - "text-halo-color": "@text2_stroke_night", - "text-size": { - "stops": [[2.99, 0], [3, 10], [6, 14], [13.99, 20], [14, 0]] - } - } - }, { - "id": "place_label_town", - "source": "mapbox", - "source-layer": "place_label", - "filter": { "type": "town", "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold, Arial Unicode MS Bold", - "text-max-size": 24, - "text-max-width": 8 - }, - "style": { - "text-color": "#716656", - "text-halo-width": 0.3, - "text-halo-color": "@text_stroke", - "text-size": { - "stops": [[8, 10], [11, 13], [13, 17], [15, 22]] - } - }, - "style.night": { - "text-color": "@text_night", - "text-halo-width": 0.3, - "text-halo-color": "@text2_stroke_night", - "text-size": { - "stops": [[8, 10], [11, 13], [13, 17], [15, 22]] - } - } - }, { - "id": "place_label_village", - "source": "mapbox", - "source-layer": "place_label", - "filter": { "type": "village", "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold, Arial Unicode MS Bold", - "text-max-size": 22, - "text-max-width": 8 - }, - "style": { - "text-color": "#635644", - "text-halo-width": 0.3, - "text-halo-color": "@text_stroke", - "text-size": { - "stops": [[8, 8], [11, 10], [13, 14], [15, 16], [16, 20]] - } - }, - "style.night": { - "text-color": "@text_night", - "text-halo-width": 0.3, - "text-halo-color": "@text2_stroke_night", - "text-size": { - "stops": [[8, 8], [11, 10], [13, 14], [15, 16], [16, 20]] - } - } - }, { - "id": "place_label_other", - "source": "mapbox", - "source-layer": "place_label", - "filter": { "type": ["hamlet", "suburb", "neighbourhood"], "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold, Arial Unicode MS Bold", - "text-max-size": 18, - "text-max-width": 6 - }, - "style": { - "text-color": "#7d6c55", - "text-halo-color": "@text_stroke", - "text-size": { - "stops": [[12, 11], [13, 12], [15, 14], [17, 18]] - } - }, - "style.night": { - "text-color": "@text_night", - "text-halo-color": "@text2_stroke_night", - "text-halo-width": 0.3, - "text-size": { - "stops": [[12, 11], [13, 12], [15, 14], [17, 18]] - } - } - }, { - "id": "road_label_1", - "source": "mapbox", - "source-layer": "road_label", - "filter": { "class": ["motorway", "main"], "$type": "LineString" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "curve", - "text-padding": 2, - "text-font": "Open Sans Regular, Arial Unicode MS Regular", - "text-max-size": 18, - "text-max-angle": 0.5 - }, - "style": { - "text-color": "#585042", - "text-halo-color": "@land", - "text-halo-width": 0.6, - "text-size": "@road_label_1_size" - }, - "style.night": { - "text-color": "@text_night", - "text-halo-color": "@text2_stroke_night", - "text-halo-width": 0.5, - "text-size": "@road_label_1_size" - } - }, { - "id": "road_label_2", - "source": "mapbox", - "source-layer": "road_label", - "filter": { "class": ["street", "street_limited"], "$type": "LineString" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "curve", - "text-padding": 2, - "text-font": "Open Sans Regular, Arial Unicode MS Regular", - "text-max-size": 16, - "text-max-angle": 0.5 - }, - "style": { - "text-color": "#585042", - "text-halo-color": "@land", - "text-halo-width": 0.6, - "text-size": "@road_label_2_size" - }, - "style.night": { - "text-color": "@text_night", - "text-halo-color": "@text2_stroke_night", - "text-halo-width": 0.5, - "text-size": "@road_label_2_size" - } - }, { - "id": "road_label_3", - "source": "mapbox", - "source-layer": "road_label", - "filter": { "class": ["service", "driveway", "path"], "$type": "LineString" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "curve", - "text-padding": 2, - "text-font": "Open Sans Regular, Arial Unicode MS Regular", - "text-max-size": 14, - "text-max-angle": 0.5 - }, - "style": { - "text-color": "#585042", - "text-halo-color": "@land", - "text-halo-width": 0.6, - "text-size": "@road_label_3_size" - }, - "style.night": { - "text-color": "@text_night", - "text-halo-color": "@text2_stroke_night", - "text-halo-width": 0.5, - "text-size": "@road_label_3_size" - } - }, { - "id": "contour_label", - "source": "mapbox", - "source-layer": "contour", - "filter": { "index": [5, 10], "$type": "LineString" }, - "type": "text", - "render": { - "text-path": "curve", - "text-field": "{ele} m", - "text-font": "Open Sans Regular, Arial Unicode MS Regular", - "text-max-size": 10, - "text-max-angle": 0.5 - }, - "style": { - "text-color": "@text", - "text-halo-color": "@land", - "text-halo-width": 0.3, - "text-size": 10 - }, - "style.night": { - "text-color": "@contour_night", - "text-halo-color": "@land_night", - "text-halo-width": 0.3, - "text-size": 10 - } - }, { - "id": "water_label", - "source": "mapbox", - "source-layer": "water_label", - "filter": { "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 12, - "text-max-width": 8 - }, - "style": { - "text-color": "@water_dark", - "text-halo-color": "rgba(255,255,255,0.75)" - }, - "style.night": { - "text-color": "@text_water_night", - "text-halo-color": "@water_night" - } - }, { - "id": "waterway_label", - "source": "mapbox", - "source-layer": "waterway_label", - "filter": { "$type": "LineString" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "curve", - "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", - "text-max-size": 12, - "text-max-angle": 0.5 - }, - "style": { - "text-color": "@water_dark", - "text-halo-width": 0.4, - "text-halo-color": "@text_stroke" - }, - "style.night": { - "text-color": "@text_water_night", - "text-halo-color": "@water_night" - } - }, { - "id": "poi", - "source": "mapbox", - "source-layer": "poi_label", - "filter": { "scalerank": [1, 2] }, - "type": "icon", - "render": { - "icon-image": "{maki}-12", - "icon-size": 12 - }, - "style.night": {} - }, { - "id": "poi_label_1-2", - "source": "mapbox", - "source-layer": "poi_label", - "filter": { "scalerank": [1, 2], "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-padding": 2, - "text-font": "Open Sans Semibold, Arial Unicode MS Bold", - "text-max-size": 12, - "text-max-width": 10, - "text-alignment": "center" - }, - "style": { - "text-color": "#444", - "text-size": "@poi_label_1-2_size", - "text-halo-color": "@land", - "text-halo-width": 0.3 - }, - "style.night": { - "text-color": "#fff", - "text-size": "@poi_label_1-2_size", - "text-halo-color": "@text2_stroke_night", - "text-halo-width": 0.3 - } - }, { - "id": "poi_3", - "source": "mapbox", - "source-layer": "poi_label", - "filter": { "scalerank": 3 }, - "type": "icon", - "render": { - "icon-image": "{maki}-12", - "icon-size": 12 - }, - "style": { - "icon-opacity": { - "stops": [[15.5, 0], [15.75, 1]] - } - }, - "style.night": { - "icon-opacity": { - "stops": [[15.5, 0], [15.75, 1]] - } - } - }, { - "id": "poi_label_3", - "source": "mapbox", - "source-layer": "poi_label", - "filter": { "scalerank": 3, "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-padding": 2, - "text-font": "Open Sans Semibold, Arial Unicode MS Bold", - "text-max-size": 11, - "text-max-width": 10, - "text-alignment": "center" - }, - "style": { - "text-color": "#444", - "text-size": "@poi_label_3_size", - "text-halo-color": "@land", - "text-halo-width": 0.3, - "text-opacity": { - "stops": [[15.5, 0], [15.75, 1]] - } - }, - "style.night": { - "text-color": "#fff", - "text-size": "@poi_label_3_size", - "text-halo-color": "@text2_stroke_night", - "text-halo-width": 0.3, - "text-opacity": { - "stops": [[15.5, 0], [15.75, 1]] - } - } - }, { - "id": "poi_4", - "source": "mapbox", - "source-layer": "poi_label", - "filter": { "scalerank": 4 }, - "type": "icon", - "render": { - "icon-image": "{maki}-12", - "icon-size": 12 - }, - "style": { - "icon-opacity": { - "stops": [[17.5, 0], [17.75, 1]] - } - }, - "style.night": { - "icon-opacity": { - "stops": [[17.5, 0], [17.75, 1]] - } - } - }, { - "id": "poi_label_4", - "source": "mapbox", - "source-layer": "poi_label", - "filter": { "scalerank": 4, "$type": "Point" }, - "type": "text", - "render": { - "text-field": "{name_en}", - "text-path": "horizontal", - "text-padding": 2, - "text-font": "Open Sans Semibold, Arial Unicode MS Bold", - "text-max-size": 10, - "text-max-width": 10, - "text-alignment": "center" - }, - "style": { - "text-color": "#444", - "text-size": 10, - "text-opacity": { - "stops": [[17.5, 0], [17.75, 1]] - }, - "text-halo-color": "@land", - "text-halo-width": 0.3 - }, - "style.night": { - "text-color": "#fff", - "text-size": 10, - "text-opacity": { - "stops": [[17.5, 0], [17.75, 1]] - }, - "text-halo-color": "@text2_stroke_night", - "text-halo-width": 0.3 - } - }, { - "id": "poi_aerodrome", - "source": "mapbox", - "source-layer": "poi_label", - "filter": { "maki": "airport" }, - "type": "icon", - "render": { - "icon-image": "{maki}-12", - "icon-size": 12 - }, - "style": { - "icon-opacity": { - "stops": [[12, 0], [12.25, 1]] - } - }, - "style.night": { - "icon-opacity": { - "stops": [[12, 0], [12.25, 1]] - } - } - }] -} diff --git a/common.gypi b/common.gypi index a38a279ef1..513d8688f0 100644 --- a/common.gypi +++ b/common.gypi @@ -7,7 +7,7 @@ 'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0', 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', 'GCC_ENABLE_CPP_RTTI':'YES', - 'OTHER_CPLUSPLUSFLAGS': [ '-Wall', '-Wextra' ], + 'OTHER_CPLUSPLUSFLAGS': [ '-Wall', '-Wextra', '-Wno-variadic-macros' ], 'GCC_WARN_PEDANTIC': 'YES', 'GCC_WARN_UNINITIALIZED_AUTOS': 'YES_AGGRESSIVE' }, diff --git a/common/glfw_view.cpp b/common/glfw_view.cpp index 60d95c8b81..f53090a000 100644 --- a/common/glfw_view.cpp +++ b/common/glfw_view.cpp @@ -1,5 +1,7 @@ #include "glfw_view.hpp" +#include <mbgl/util/string.hpp> + GLFWView::GLFWView(bool fullscreen) : fullscreen(fullscreen) { #ifdef NVIDIA glDiscardFramebufferEXT = (PFNGLDISCARDFRAMEBUFFEREXTPROC)glfwGetProcAddress("glDiscardFramebufferEXT"); @@ -192,9 +194,15 @@ void GLFWView::make_active() { void GLFWView::swap() { glfwPostEmptyEvent(); + + double lon, lat, zoom; + map->getLonLatZoom(lon, lat, zoom); + const double bearing = map->getBearing(); + const std::string title = mbgl::util::sprintf<128>("Mapbox GL – %.2f/%.6f/%.6f/%.1f", zoom, lat, lon, bearing); + glfwSetWindowTitle(window, title.c_str()); } -void GLFWView::notify_map_change(mbgl::MapChange change, mbgl::timestamp delay) { +void GLFWView::notify_map_change(mbgl::MapChange /*change*/, mbgl::timestamp /*delay*/) { // no-op } diff --git a/common/settings_json.cpp b/common/settings_json.cpp index bceab50da6..f2f1248aa6 100644 --- a/common/settings_json.cpp +++ b/common/settings_json.cpp @@ -18,7 +18,7 @@ void Settings_JSON::load() { longitude = document[rapidjson::SizeType(0)].GetDouble(); latitude = document[1].GetDouble(); zoom = document[2].GetDouble(); - angle = document[3].GetDouble(); + bearing = document[3].GetDouble(); debug = document[4].GetBool(); } } @@ -32,7 +32,7 @@ void Settings_JSON::save() { writer.Double(longitude); writer.Double(latitude); writer.Double(zoom); - writer.Double(angle); + writer.Double(bearing); writer.Bool(debug); writer.EndArray(); } @@ -41,6 +41,6 @@ void Settings_JSON::clear() { longitude = 0; latitude = 0; zoom = 0; - angle = 0; + bearing = 0; debug = false; } diff --git a/common/settings_json.hpp b/common/settings_json.hpp index 09f7841c91..25c2179ba0 100644 --- a/common/settings_json.hpp +++ b/common/settings_json.hpp @@ -14,7 +14,7 @@ public: double longitude = 0; double latitude = 0; double zoom = 0; - double angle = 0; + double bearing = 0; bool debug = false; }; diff --git a/common/settings_nsuserdefaults.hpp b/common/settings_nsuserdefaults.hpp index dcc6ab46b9..3533e3da35 100644 --- a/common/settings_nsuserdefaults.hpp +++ b/common/settings_nsuserdefaults.hpp @@ -14,7 +14,7 @@ public: double longitude = 0; double latitude = 0; double zoom = 0; - double angle = 0; + double bearing = 0; bool debug = false; }; diff --git a/common/settings_nsuserdefaults.mm b/common/settings_nsuserdefaults.mm index 24571677f4..dfc601bddb 100644 --- a/common/settings_nsuserdefaults.mm +++ b/common/settings_nsuserdefaults.mm @@ -9,7 +9,7 @@ Settings_NSUserDefaults::Settings_NSUserDefaults() [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"longitude" : @(longitude), @"latitude" : @(latitude), @"zoom" : @(zoom), - @"angle" : @(angle), + @"bearing" : @(bearing), @"debug" : @(debug) }]; load(); } @@ -21,7 +21,7 @@ void Settings_NSUserDefaults::load() longitude = [settings[@"longitude"] doubleValue]; latitude = [settings[@"latitude"] doubleValue]; zoom = [settings[@"zoom"] doubleValue]; - angle = [settings[@"angle"] doubleValue]; + bearing = [settings[@"bearing"] doubleValue]; debug = [settings[@"debug"] boolValue]; } @@ -29,8 +29,8 @@ void Settings_NSUserDefaults::save() { [[NSUserDefaults standardUserDefaults] setValuesForKeysWithDictionary:@{ @"longitude" : @(longitude), @"latitude" : @(latitude), - @"zoom" : @(zoom), - @"angle" : @(angle), + @"zoom" : @(zoom), + @"bearing" : @(bearing), @"debug" : @(debug) }]; [[NSUserDefaults standardUserDefaults] synchronize]; } diff --git a/include/mbgl/geometry/icon_buffer.hpp b/include/mbgl/geometry/icon_buffer.hpp index 346b3b5f06..e7e5f40355 100644 --- a/include/mbgl/geometry/icon_buffer.hpp +++ b/include/mbgl/geometry/icon_buffer.hpp @@ -3,16 +3,18 @@ #include "buffer.hpp" +#include <array> + namespace mbgl { class IconVertexBuffer : public Buffer< - 4 + // int16 x/y coordinates per vertex (== 4 bytes) - 4 // uint16 x/y coordinates of icon in sprite (== 4 bytes) + 20 > { public: - typedef int16_t vertex_type; + static const double angleFactor; + + size_t add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float angle, float minzoom, std::array<float, 2> range, float maxzoom, float labelminzoom); - void add(vertex_type x, vertex_type y, uint16_t tx, uint16_t ty); }; } diff --git a/include/mbgl/geometry/interpolate.hpp b/include/mbgl/geometry/interpolate.hpp index cebbcc7028..998cba31cd 100644 --- a/include/mbgl/geometry/interpolate.hpp +++ b/include/mbgl/geometry/interpolate.hpp @@ -7,7 +7,7 @@ namespace mbgl { Anchors interpolate(const std::vector<Coordinate> &vertices, float spacing, - float minScale = 0.0f, int start = 0); + float minScale, float maxScale, float tilePixelRatio, int start = 0); } #endif diff --git a/include/mbgl/geometry/sprite_atlas.hpp b/include/mbgl/geometry/sprite_atlas.hpp index 4b55540cc8..dc2378c2fb 100644 --- a/include/mbgl/geometry/sprite_atlas.hpp +++ b/include/mbgl/geometry/sprite_atlas.hpp @@ -26,18 +26,21 @@ public: // Changes the pixel ratio. bool resize(float newRatio); - // Update uninitialized sprites in this atlas from the given sprite. + // Update uninitialized (= outdated) sprites in this atlas from the given sprite. void update(const Sprite &sprite); - // Returns the coordinates of a square icon. The getter also *creates* new square icons in the - // atlas if they don't exist, but they'll be default-initialized with a a black circle. - Rect<dimension> getIcon(int size, const std::string &name); - // Returns the coordinates of an image that is sourced from the sprite image. - // This getter does not create images, as the dimension of the texture us unknown if the - // sprite is not yet loaded. Instead, it returns a 0/0/0/0 rect. + // This getter attempts to read the image from the sprite if it is already loaded. + // In that case, it copies it into the sprite atlas and returns the dimensions. + // Otherwise, it returns a 0/0/0/0 rect. Rect<dimension> getImage(const std::string &name, const Sprite &sprite); + // Returns the coordinates of an image that is sourced from the sprite image. + // This getter waits until the sprite image was loaded, copies it into the sprite + // image and returns the dimensions. + // NEVER CALL THIS FUNCTION FROM THE RENDER THREAD! it is blocking. + Rect<dimension> waitForImage(const std::string &name, const Sprite &sprite); + // Binds the image buffer of this sprite atlas to the GPU, and uploads data if it is out // of date. void bind(bool linear = false); diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 774e5e1292..be01b456cc 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -1,26 +1,34 @@ - #ifndef MBGL_MAP_MAP #define MBGL_MAP_MAP -#include <uv.h> - -#include <mbgl/map/view.hpp> #include <mbgl/map/transform.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/geometry/glyph_atlas.hpp> -#include <mbgl/text/glyph_store.hpp> #include <mbgl/renderer/painter.hpp> + #include <mbgl/util/noncopyable.hpp> -#include <mbgl/util/texturepool.hpp> +#include <mbgl/util/time.hpp> +#include <mbgl/util/uv.hpp> #include <cstdint> -#include <string> -#include <map> +#include <atomic> +#include <iosfwd> +#include <memory> +#include <set> +#include <vector> namespace mbgl { -class Source; +class GlyphAtlas; +class GlyphStore; +class LayerDescription; class SpriteAtlas; +class Sprite; +class Style; +class StyleLayer; +class StyleLayerGroup; +class StyleSource; +class Texturepool; +class FileSource; +class View; class Map : private util::noncopyable { public: @@ -57,7 +65,8 @@ public: void toggleClass(const std::string &name); const std::vector<std::string> &getAppliedClasses() const; void setDefaultTransitionDuration(uint64_t duration_milliseconds = 0); - void setStyleJSON(std::string newStyleJSON); + void setStyleURL(const std::string &url); + void setStyleJSON(std::string newStyleJSON, const std::string &base = ""); std::string getStyleJSON() const; void setAccessToken(std::string access_token); std::string getAccessToken() const; @@ -89,9 +98,9 @@ public: // Rotation void rotateBy(double sx, double sy, double ex, double ey, double duration = 0); - void setAngle(double angle, double duration = 0); - void setAngle(double angle, double cx, double cy); - double getAngle() const; + void setBearing(double degrees, double duration = 0); + void setBearing(double degrees, double cx, double cy); + double getBearing() const; void resetNorth(); void startRotating(); void stopRotating(); @@ -104,10 +113,12 @@ public: public: inline const TransformState &getState() const { return state; } - inline std::shared_ptr<const Style> getStyle() const { return style; } + inline std::shared_ptr<FileSource> getFileSource() const { return fileSource; } + inline std::shared_ptr<Style> getStyle() const { return style; } inline std::shared_ptr<GlyphAtlas> getGlyphAtlas() { return glyphAtlas; } inline std::shared_ptr<GlyphStore> getGlyphStore() { return glyphStore; } inline std::shared_ptr<SpriteAtlas> getSpriteAtlas() { return spriteAtlas; } + std::shared_ptr<Sprite> getSprite(); inline std::shared_ptr<Texturepool> getTexturepool() { return texturepool; } inline std::shared_ptr<uv::loop> getLoop() { return loop; } inline timestamp getAnimationTime() const { return animationTime; } @@ -143,6 +154,14 @@ private: void renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass); private: + bool async = false; + std::shared_ptr<uv::loop> loop; + uv_thread_t thread; + uv_async_t *async_terminate = nullptr; + uv_async_t *async_render = nullptr; + uv_async_t *async_cleanup = nullptr; + +private: // If cleared, the next time the render thread attempts to render the map, it will *actually* // render the map. std::atomic_flag is_clean = ATOMIC_FLAG_INIT; @@ -162,10 +181,13 @@ private: Transform transform; TransformState state; + std::shared_ptr<FileSource> fileSource; + std::shared_ptr<Style> style; std::shared_ptr<GlyphAtlas> glyphAtlas; std::shared_ptr<GlyphStore> glyphStore; std::shared_ptr<SpriteAtlas> spriteAtlas; + std::shared_ptr<Sprite> sprite; std::shared_ptr<Texturepool> texturepool; Painter painter; @@ -180,13 +202,6 @@ private: std::set<std::shared_ptr<StyleSource>> activeSources; -private: - bool async = false; - std::shared_ptr<uv::loop> loop; - uv_thread_t thread; - uv_async_t *async_terminate = nullptr; - uv_async_t *async_render = nullptr; - uv_async_t *async_cleanup = nullptr; }; } diff --git a/include/mbgl/map/raster_tile_data.hpp b/include/mbgl/map/raster_tile_data.hpp index 976faa91bc..bf8691454f 100644 --- a/include/mbgl/map/raster_tile_data.hpp +++ b/include/mbgl/map/raster_tile_data.hpp @@ -1,18 +1,24 @@ #ifndef MBGL_MAP_RASTER_TILE_DATA #define MBGL_MAP_RASTER_TILE_DATA +#include <mbgl/map/tile.hpp> #include <mbgl/map/tile_data.hpp> - #include <mbgl/renderer/raster_bucket.hpp> +#include <memory> namespace mbgl { +class Map; +class Painter; +class SourceInfo; +class StyleLayer; + class RasterTileData : public TileData { friend class TileParser; public: - RasterTileData(Tile::ID id, Map &map, const std::string url); + RasterTileData(Tile::ID id, Map &map, const SourceInfo &source); ~RasterTileData(); virtual void parse(); diff --git a/include/mbgl/map/source.hpp b/include/mbgl/map/source.hpp index be5fad3af8..c3f2d4fe8b 100644 --- a/include/mbgl/map/source.hpp +++ b/include/mbgl/map/source.hpp @@ -3,30 +3,29 @@ #include <mbgl/map/tile.hpp> #include <mbgl/map/tile_data.hpp> +#include <mbgl/style/style_source.hpp> + #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/time.hpp> -#include <mbgl/style/style_source.hpp> -#include <mbgl/style/types.hpp> +#include <mbgl/util/mat4.hpp> -#include <list> +#include <cstdint> #include <forward_list> -#include <memory> -#include <vector> -#include <string> +#include <iosfwd> #include <map> -#include <set> +#include <memory> namespace mbgl { +class Map; +class Painter; +class StyleLayer; class TransformState; -class Texturepool; +struct box; class Source : public std::enable_shared_from_this<Source>, private util::noncopyable { public: - Source(StyleSource style_source, const std::string &access_token = ""); - Source(SourceType type = SourceType::Vector, const std::string &url = "", - uint32_t tile_size = 512, uint32_t min_zoom = 0, uint32_t max_zoom = 22, - const std::string &access_token = ""); + Source(SourceInfo info, const std::string &access_token = ""); bool update(Map &map); void updateMatrices(const mat4 &projMatrix, const TransformState &transform); @@ -41,11 +40,7 @@ public: static std::string normalizeSourceURL(const std::string &url, const std::string &access_token); public: - const SourceType type; - const std::string url; - const uint32_t tile_size; - const int32_t min_zoom; - const int32_t max_zoom; + const SourceInfo info; private: bool findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain); diff --git a/include/mbgl/map/sprite.hpp b/include/mbgl/map/sprite.hpp index 22126eb6a1..967f1d6614 100644 --- a/include/mbgl/map/sprite.hpp +++ b/include/mbgl/map/sprite.hpp @@ -1,18 +1,20 @@ #ifndef MBGL_STYLE_SPRITE #define MBGL_STYLE_SPRITE -#include <mbgl/util/raster.hpp> -#include <mbgl/util/vec.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/noncopyable.hpp> -#include <string> -#include <mutex> -#include <memory> +#include <cstdint> #include <atomic> +#include <iosfwd> +#include <memory> +#include <string> #include <unordered_map> +#include <future> namespace mbgl { -class Map; +class FileSource; class SpritePosition { public: @@ -28,36 +30,47 @@ public: uint8_t pixelRatio = 1; }; -class Sprite : public std::enable_shared_from_this<Sprite> { -public: - Sprite(Map &map, float pixelRatio = 1); +class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable { +private: + struct Key {}; + void load(const std::shared_ptr<FileSource> &fileSource); - void load(const std::string& base_url); +public: + Sprite(const Key &, const std::string& base_url, float pixelRatio); + static std::shared_ptr<Sprite> Create(const std::string& base_url, float pixelRatio, const std::shared_ptr<FileSource> &fileSource); const SpritePosition &getSpritePosition(const std::string& name) const; + void waitUntilLoaded() const; bool isLoaded() const; + operator bool() const; + +private: + const bool valid; + public: const float pixelRatio; + const std::string spriteURL; + const std::string jsonURL; std::unique_ptr<util::Image> raster; private: - void asyncParseJSON(); - void asyncParseImage(); - - static void parseJSON(std::shared_ptr<Sprite> &sprite); - static void parseImage(std::shared_ptr<Sprite> &sprite); - static void complete(std::shared_ptr<Sprite> &sprite); + void parseJSON(); + void parseImage(); + void complete(); private: - Map ↦ - std::string url; std::string body; std::string image; - std::atomic<bool> loaded; + std::atomic<bool> loadedImage; + std::atomic<bool> loadedJSON; std::unordered_map<std::string, SpritePosition> pos; const SpritePosition empty; + + std::promise<void> promise; + std::future<void> future; + }; } diff --git a/include/mbgl/map/tile.hpp b/include/mbgl/map/tile.hpp index 7e868afad9..9cf5ff5341 100644 --- a/include/mbgl/map/tile.hpp +++ b/include/mbgl/map/tile.hpp @@ -1,15 +1,17 @@ #ifndef MBGL_MAP_TILE #define MBGL_MAP_TILE -#include <mbgl/util/vec.hpp> #include <mbgl/util/mat4.hpp> #include <mbgl/util/noncopyable.hpp> #include <cstdint> -#include <forward_list> -#include <string> #include <bitset> +#include <cmath> +#include <cstdint> +#include <forward_list> +#include <iosfwd> #include <memory> +#include <string> namespace mbgl { diff --git a/include/mbgl/map/tile_data.hpp b/include/mbgl/map/tile_data.hpp index a4b73c339f..c349ea2bae 100644 --- a/include/mbgl/map/tile_data.hpp +++ b/include/mbgl/map/tile_data.hpp @@ -2,22 +2,26 @@ #define MBGL_MAP_TILE_DATA #include <mbgl/map/tile.hpp> -#include <mbgl/util/noncopyable.hpp> -#include <mbgl/platform/platform.hpp> -#include <mbgl/geometry/vao.hpp> #include <mbgl/renderer/debug_bucket.hpp> +#include <mbgl/geometry/debug_font_buffer.hpp> + +#include <mbgl/util/noncopyable.hpp> -#include <cstdint> -#include <string> -#include <memory> #include <atomic> +#include <exception> +#include <iosfwd> +#include <memory> +#include <string> namespace mbgl { class Map; class Painter; +class SourceInfo; class StyleLayer; +namespace platform { class Request; } + class TileData : public std::enable_shared_from_this<TileData>, private util::noncopyable { public: @@ -37,7 +41,7 @@ public: }; public: - TileData(Tile::ID id, Map &map, const std::string url); + TileData(Tile::ID id, Map &map, const SourceInfo &source); ~TileData(); void request(); @@ -60,8 +64,13 @@ public: protected: Map ↦ +public: + const SourceInfo &source; + // Request-related information. const std::string url; + +protected: std::weak_ptr<platform::Request> req; std::string data; diff --git a/include/mbgl/map/tile_parser.hpp b/include/mbgl/map/tile_parser.hpp index 14e00946b8..9aa0f4dc66 100644 --- a/include/mbgl/map/tile_parser.hpp +++ b/include/mbgl/map/tile_parser.hpp @@ -2,27 +2,30 @@ #define MBGL_MAP_TILE_PARSER #include <mbgl/map/vector_tile.hpp> -#include <mbgl/text/placement.hpp> -#include <mbgl/text/glyph_store.hpp> -#include <mbgl/text/glyph.hpp> -#include <mbgl/util/utf.hpp> #include <mbgl/style/filter_expression.hpp> +#include <mbgl/text/glyph.hpp> +#include <mbgl/text/collision.hpp> + +#include <cstdint> +#include <iosfwd> +#include <memory> +#include <string> namespace mbgl { -class Style; +class Bucket; +class FontStack; class GlyphAtlas; class GlyphStore; class SpriteAtlas; -class VectorTileData; -class StyleLayer; -class StyleLayerGroup; +class Sprite; +class Style; class StyleBucket; class StyleBucketFill; class StyleBucketLine; -class StyleBucketIcon; - -class Bucket; +class StyleBucketSymbol; +class StyleLayerGroup; +class VectorTileData; class TileParser { public: @@ -30,25 +33,22 @@ public: const std::shared_ptr<const Style> &style, const std::shared_ptr<GlyphAtlas> &glyphAtlas, const std::shared_ptr<GlyphStore> &glyphStore, - const std::shared_ptr<SpriteAtlas> &spriteAtlas); + const std::shared_ptr<SpriteAtlas> &spriteAtlas, + const std::shared_ptr<Sprite> &sprite); public: void parse(); private: bool obsolete() const; - void parseGlyphs(); void parseStyleLayers(std::shared_ptr<StyleLayerGroup> group); - void addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face); std::unique_ptr<Bucket> createBucket(std::shared_ptr<StyleBucket> bucket_desc); std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill); std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line); - std::unique_ptr<Bucket> createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon); - std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketText &text); + std::unique_ptr<Bucket> createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol); - template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter); - template <class Bucket, typename ...Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter, Args&& ...args); + template <class Bucket> void addBucketGeometries(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter); private: const VectorTile vector_data; @@ -59,8 +59,9 @@ private: std::shared_ptr<GlyphAtlas> glyphAtlas; std::shared_ptr<GlyphStore> glyphStore; std::shared_ptr<SpriteAtlas> spriteAtlas; + std::shared_ptr<Sprite> sprite; - Placement placement; + Collision collision; }; } diff --git a/include/mbgl/map/transform.hpp b/include/mbgl/map/transform.hpp index 61bef76563..df7839ea96 100644 --- a/include/mbgl/map/transform.hpp +++ b/include/mbgl/map/transform.hpp @@ -1,20 +1,23 @@ #ifndef MBGL_MAP_TRANSFORM #define MBGL_MAP_TRANSFORM -#include <mbgl/util/transition.hpp> +#include <mbgl/util/time.hpp> +#include <mbgl/map/transform_state.hpp> + #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/uv.hpp> -#include "view.hpp" -#include "transform_state.hpp" - +#include <cstdint> +#include <cmath> #include <forward_list> +#include <memory> namespace mbgl { -struct box; +class View; +namespace util { class transition; } - class Transform : private util::noncopyable { +class Transform : private util::noncopyable { public: Transform(View &view); @@ -93,7 +96,7 @@ private: const double max_scale = std::pow(2, 18); // cache values for spherical mercator math - double zc, Bc, Cc; + double Bc, Cc; std::forward_list<std::shared_ptr<util::transition>> transitions; std::shared_ptr<util::transition> scale_timeout; diff --git a/include/mbgl/map/transform_state.hpp b/include/mbgl/map/transform_state.hpp index 85dd2eb87d..5b3b7d3755 100644 --- a/include/mbgl/map/transform_state.hpp +++ b/include/mbgl/map/transform_state.hpp @@ -1,15 +1,17 @@ #ifndef MBGL_MAP_TRANSFORM_STATE #define MBGL_MAP_TRANSFORM_STATE -#include <mbgl/util/mat4.hpp> #include <mbgl/map/tile.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/util/vec.hpp> + #include <cstdint> +#include <array> +#include <limits> namespace mbgl { -class Transform; - class TransformState { friend class Transform; diff --git a/include/mbgl/map/vector_tile.hpp b/include/mbgl/map/vector_tile.hpp index 77e379865f..1bbe645307 100644 --- a/include/mbgl/map/vector_tile.hpp +++ b/include/mbgl/map/vector_tile.hpp @@ -1,24 +1,22 @@ #ifndef MBGL_MAP_VECTOR_TILE #define MBGL_MAP_VECTOR_TILE -#include <mbgl/util/pbf.hpp> -#include <mbgl/util/vec.hpp> +#include <mbgl/style/filter_expression.hpp> #include <mbgl/style/value.hpp> #include <mbgl/text/glyph.hpp> -#include <mbgl/style/filter_expression.hpp> -#include <vector> +#include <mbgl/util/pbf.hpp> + +#include <cstdint> +#include <iosfwd> #include <map> +#include <string> #include <unordered_map> -#include <set> -#include <limits> +#include <vector> namespace mbgl { -class BucketDescription; class VectorTileLayer; -struct pbf; - enum class FeatureType { Unknown = 0, Point = 1, @@ -88,7 +86,7 @@ private: const FilterExpression& filterExpression; }; -std::ostream& operator<<(std::ostream&, const GlyphPlacement& placement); +std::ostream& operator<<(std::ostream&, const PositionedGlyph& placement); class VectorTileLayer { public: diff --git a/include/mbgl/map/vector_tile_data.hpp b/include/mbgl/map/vector_tile_data.hpp index dd55e8dae1..edad228999 100644 --- a/include/mbgl/map/vector_tile_data.hpp +++ b/include/mbgl/map/vector_tile_data.hpp @@ -1,29 +1,32 @@ #ifndef MBGL_MAP_VECTOR_TILE_DATA #define MBGL_MAP_VECTOR_TILE_DATA +#include <mbgl/map/tile.hpp> #include <mbgl/map/tile_data.hpp> - - -#include <mbgl/renderer/bucket.hpp> - -#include <mbgl/geometry/vertex_buffer.hpp> #include <mbgl/geometry/elements_buffer.hpp> #include <mbgl/geometry/fill_buffer.hpp> -#include <mbgl/geometry/line_buffer.hpp> #include <mbgl/geometry/icon_buffer.hpp> +#include <mbgl/geometry/line_buffer.hpp> #include <mbgl/geometry/text_buffer.hpp> -#include <mbgl/map/tile_parser.hpp> +#include <iosfwd> +#include <memory> #include <unordered_map> namespace mbgl { +class Bucket; +class Map; +class Painter; +class SourceInfo; +class StyleLayer; +class TileParser; class VectorTileData : public TileData { friend class TileParser; public: - VectorTileData(Tile::ID id, Map &map, const std::string url); + VectorTileData(Tile::ID id, Map &map, const SourceInfo &source); ~VectorTileData(); virtual void beforeParse(); @@ -48,6 +51,9 @@ protected: std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets; std::unique_ptr<TileParser> parser; + +public: + const float depth; }; } diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp index b55c721c99..01a23aa255 100644 --- a/include/mbgl/platform/event.hpp +++ b/include/mbgl/platform/event.hpp @@ -68,6 +68,7 @@ constexpr EventSeverity disabledEventSeverities[] = { constexpr Event disabledEvents[] = { + Event(-1) // Avoid zero size array }; constexpr EventPermutation disabledEventPermutations[] = { diff --git a/include/mbgl/renderer/icon_bucket.hpp b/include/mbgl/renderer/icon_bucket.hpp deleted file mode 100644 index e3b311332d..0000000000 --- a/include/mbgl/renderer/icon_bucket.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef MBGL_RENDERER_ICONBUCKET -#define MBGL_RENDERER_ICONBUCKET - -#include <mbgl/renderer/bucket.hpp> -#include <mbgl/geometry/elements_buffer.hpp> -#include <mbgl/geometry/icon_buffer.hpp> -#include <mbgl/style/style_bucket.hpp> - -#include <vector> -#include <memory> - -#ifndef BUFFER_OFFSET -#define BUFFER_OFFSET(i) ((char *)nullptr + (i)) -#endif - -namespace mbgl { - -class Style; -class IconVertexBuffer; -class BucketDescription; -class IconShader; -class DotShader; -class SpriteAtlas; -class VectorTileFeature; - -class IconBucket : public Bucket { -public: - IconBucket(IconVertexBuffer& vertexBuffer, - const StyleBucketIcon& properties); - - virtual void render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id); - virtual bool hasData() const; - - void addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas); - - void drawIcons(IconShader& shader); - void drawIcons(DotShader& shader); - -public: - const StyleBucketIcon &properties; - -private: - - IconVertexBuffer& vertexBuffer; - VertexArrayObject array; - - const size_t vertex_start; - size_t vertex_end = 0; -}; - -} - -#endif diff --git a/include/mbgl/renderer/painter.hpp b/include/mbgl/renderer/painter.hpp index 9868b5d9da..b4023de167 100644 --- a/include/mbgl/renderer/painter.hpp +++ b/include/mbgl/renderer/painter.hpp @@ -38,8 +38,7 @@ class StyleSource; class FillBucket; class LineBucket; -class IconBucket; -class TextBucket; +class SymbolBucket; class RasterBucket; struct FillProperties; @@ -62,6 +61,8 @@ public: void cleanup(); + // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any + // tiles whatsoever. void clear(); // Updates the default matrices to the current viewport dimensions. @@ -73,10 +74,6 @@ public: // Renders debug information for a tile. void renderTileDebug(const Tile& tile); - // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any - // tiles whatsoever. - void renderMatte(); - // Renders the red debug frame around a tile, visualizing its perimeter. void renderDebugFrame(); @@ -85,8 +82,7 @@ public: void renderFill(FillBucket& bucket, const FillProperties& properties, const Tile::ID& id, const mat4 &mat); void renderFill(FillBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id); void renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id); - void renderIcon(IconBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id); - void renderText(TextBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id); + void renderSymbol(SymbolBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id); void renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id); void preparePrerender(PrerenderedTexture &texture); @@ -121,7 +117,7 @@ public: bool needsAnimation() const; private: void setupShaders(); - const mat4 &translatedMatrix(const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchorType anchor = TranslateAnchorType::Default); + const mat4 &translatedMatrix(const std::array<float, 2> &translation, const Tile::ID &id, TranslateAnchorType anchor = TranslateAnchorType::Map); void prepareTile(const Tile& tile); diff --git a/include/mbgl/renderer/symbol_bucket.hpp b/include/mbgl/renderer/symbol_bucket.hpp new file mode 100644 index 0000000000..c003230cae --- /dev/null +++ b/include/mbgl/renderer/symbol_bucket.hpp @@ -0,0 +1,109 @@ +#ifndef MBGL_RENDERER_SYMBOLBUCKET +#define MBGL_RENDERER_SYMBOLBUCKET + +#include "bucket.hpp" +#include <mbgl/geometry/vao.hpp> +#include <mbgl/geometry/elements_buffer.hpp> +#include <mbgl/geometry/text_buffer.hpp> +#include <mbgl/geometry/icon_buffer.hpp> +#include <mbgl/map/vector_tile.hpp> +#include <mbgl/text/types.hpp> +#include <mbgl/text/glyph.hpp> +#include <mbgl/style/style_bucket.hpp> + +#include <memory> +#include <map> +#include <vector> + +namespace mbgl { + +class Style; +class TextShader; +class IconShader; +class DotShader; +class Collision; +class SpriteAtlas; +class Sprite; +class GlyphAtlas; +class GlyphStore; +class FontStack; + +class SymbolFeature { +public: + pbf geometry; + std::u32string label; + std::string sprite; +}; + + +class Symbol { +public: + vec2<float> tl, tr, bl, br; + Rect<uint16_t> tex; + float angle; + float minScale = 0.0f; + float maxScale = std::numeric_limits<float>::infinity(); + CollisionAnchor anchor; +}; + +typedef std::vector<Symbol> Symbols; + + +class SymbolBucket : public Bucket { +public: + SymbolBucket(const StyleBucketSymbol &properties, Collision &collision); + + virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id); + virtual bool hasData() const; + virtual bool hasTextData() const; + virtual bool hasIconData() const; + + void addFeatures(const VectorTileLayer &layer, const FilterExpression &filter, + const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite, + GlyphAtlas &glyphAtlas, GlyphStore &glyphStore); + + void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, PlacementRange placementRange, + float zoom); + + void drawGlyphs(TextShader &shader); + void drawIcons(IconShader& shader); + +private: + + std::vector<SymbolFeature> processFeatures(const VectorTileLayer &layer, const FilterExpression &filter, GlyphStore &glyphStore, const Sprite &sprite); + + + void addFeature(const pbf &geom_pbf, const Shaping &shaping, const GlyphPositions &face, const Rect<uint16_t> &image); + void addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const GlyphPositions &face, const Rect<uint16_t> &image); + + + // Adds placed items to the buffer. + template <typename Buffer> + void addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale, PlacementRange placementRange); + + // Adds glyphs to the glyph atlas so that they have a left/top/width/height coordinates associated to them that we can use for writing to a buffer. + void addGlyphsToAtlas(uint64_t tileid, const std::string stackname, const std::u32string &string, + const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face); + +public: + const StyleBucketSymbol &properties; + +private: + Collision &collision; + + struct { + TextVertexBuffer vertices; + TriangleElementsBuffer triangles; + std::vector<ElementGroup> groups; + } text; + + struct { + IconVertexBuffer vertices; + TriangleElementsBuffer triangles; + std::vector<ElementGroup> groups; + } icon; + +}; +} + +#endif diff --git a/include/mbgl/renderer/text_bucket.hpp b/include/mbgl/renderer/text_bucket.hpp deleted file mode 100644 index cb4b8f2cda..0000000000 --- a/include/mbgl/renderer/text_bucket.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef MBGL_RENDERER_TEXTBUCKET -#define MBGL_RENDERER_TEXTBUCKET - -#include "bucket.hpp" -#include <mbgl/geometry/vao.hpp> -#include <mbgl/geometry/elements_buffer.hpp> -#include <mbgl/map/vector_tile.hpp> -#include <mbgl/text/types.hpp> -#include <mbgl/text/glyph.hpp> -#include <mbgl/style/style_bucket.hpp> - -#include <memory> -#include <map> -#include <vector> - -namespace mbgl { - -class Style; -class TextVertexBuffer; -class TriangleElementsBuffer; -class TextShader; -class Placement; -struct pbf; - -class TextBucket : public Bucket { - typedef ElementGroup triangle_group_type; - -public: - TextBucket( - TextVertexBuffer &vertexBuffer, - TriangleElementsBuffer &triangleElementsBuffer, - const StyleBucketText &properties, Placement &placement); - - virtual void render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID &id); - virtual bool hasData() const; - - void addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, - PlacementRange placementRange, float zoom); - - void addFeature(const pbf &geometry, - const GlyphPositions &face, - const Shaping &shaping); - - void drawGlyphs(TextShader &shader); - -public: - const StyleBucketText &properties; - -private: - TextVertexBuffer& vertexBuffer; - TriangleElementsBuffer& triangleElementsBuffer; - Placement &placement; - - const size_t vertex_start; - const size_t triangle_elements_start; - - std::vector<triangle_group_type> triangleGroups; -}; -} - -#endif diff --git a/include/mbgl/shader/icon_shader.hpp b/include/mbgl/shader/icon_shader.hpp index b97b3c2be3..54869fe48d 100644 --- a/include/mbgl/shader/icon_shader.hpp +++ b/include/mbgl/shader/icon_shader.hpp @@ -11,30 +11,57 @@ public: void bind(char *offset); - void setImage(int32_t image); - void setColor(const std::array<float, 4>& color); - void setDimension(const std::array<float, 2>& dimension); - void setSize(float size); - void setRatio(float ratio); + void setExtrudeMatrix(const std::array<float, 16>& exmatrix); + void setAngle(float angle); + void setZoom(float zoom); + void setFlip(float flip); + void setFadeDist(float fadedist); + void setMinFadeZoom(float minfadezoom); + void setMaxFadeZoom(float maxfadezoom); + void setFadeZoom(float fadezoom); + void setOpacity(float opacity); + void setTextureSize(const std::array<float, 2> &texsize); private: int32_t a_pos = -1; + int32_t a_offset = -1; int32_t a_tex = -1; + int32_t a_angle = -1; + int32_t a_minzoom = -1; + int32_t a_maxzoom = -1; + int32_t a_rangeend = -1; + int32_t a_rangestart = -1; + int32_t a_labelminzoom = -1; - int32_t image = -1; - int32_t u_image = -1; + std::array<float, 16> exmatrix = {{}}; + int32_t u_exmatrix = -1; - std::array<float, 4> color = {{}}; - int32_t u_color = -1; + float angle = 0; + int32_t u_angle = -1; - std::array<float, 2> dimension = {{}}; - int32_t u_dimension = -1; + float zoom = 0; + int32_t u_zoom = -1; - float size = 0; - int32_t u_size = -1; + float flip = 0; + int32_t u_flip = -1; - float ratio = 0; - int32_t u_ratio = -1; + float fadedist = 0; + int32_t u_fadedist = -1; + + float minfadezoom = 0; + int32_t u_minfadezoom = -1; + + float maxfadezoom = 0; + int32_t u_maxfadezoom = -1; + + float fadezoom = 0; + int32_t u_fadezoom = -1; + + float opacity = 0; + int32_t u_opacity = -1; + + std::array<float, 2> texsize = {{}}; + int32_t u_texsize = -1; }; } diff --git a/include/mbgl/shader/line_shader.hpp b/include/mbgl/shader/line_shader.hpp index 38efe0b71f..c476b5c4c6 100644 --- a/include/mbgl/shader/line_shader.hpp +++ b/include/mbgl/shader/line_shader.hpp @@ -16,6 +16,7 @@ public: void setLineWidth(const std::array<float, 2>& linewidth); void setRatio(float ratio); void setDashArray(const std::array<float, 2>& dasharray); + void setBlur(float blur); void setDebug(float debug); private: @@ -37,6 +38,9 @@ private: std::array<float, 2> dasharray = {{}}; int32_t u_dasharray = -1; + + float blur = 0.0f; + int32_t u_blur = -1; }; diff --git a/include/mbgl/style/property_key.hpp b/include/mbgl/style/property_key.hpp index d9005f2f4a..68dc709597 100644 --- a/include/mbgl/style/property_key.hpp +++ b/include/mbgl/style/property_key.hpp @@ -30,7 +30,15 @@ enum class PropertyKey { IconOpacity, IconRotate, - IconRotateAnchor, + IconSize, + IconColor, + IconHaloColor, + IconHaloWidth, + IconHaloBlur, + IconTranslate, // for transitions only + IconTranslateX, + IconTranslateY, + IconTranslateAnchor, TextOpacity, TextSize, @@ -38,6 +46,10 @@ enum class PropertyKey { TextHaloColor, TextHaloWidth, TextHaloBlur, + TextTranslate, // for transitions only + TextTranslateX, + TextTranslateY, + TextTranslateAnchor, CompositeOpacity, diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp index 6aab71a4c6..3df58ab42b 100644 --- a/include/mbgl/style/style.hpp +++ b/include/mbgl/style/style.hpp @@ -48,13 +48,15 @@ public: const BackgroundProperties &getBackgroundProperties() const; + const std::string &getSpriteURL() const; + public: - std::shared_ptr<Sprite> sprite; std::shared_ptr<StyleLayerGroup> layers; std::vector<std::string> appliedClasses; - std::string sprite_url; std::string glyph_url; +private: + std::string sprite_url; private: PropertyTransition defaultTransition; diff --git a/include/mbgl/style/style_bucket.hpp b/include/mbgl/style/style_bucket.hpp index 62f072d9cd..530e0c1259 100644 --- a/include/mbgl/style/style_bucket.hpp +++ b/include/mbgl/style/style_bucket.hpp @@ -4,8 +4,10 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/filter_expression.hpp> #include <mbgl/style/style_source.hpp> + #include <mbgl/util/vec.hpp> #include <mbgl/util/variant.hpp> +#include <mbgl/util/noncopyable.hpp> #include <memory> #include <forward_list> @@ -16,56 +18,74 @@ class Source; class StyleBucketFill { public: - WindingType winding = WindingType::Default; + WindingType winding = WindingType::NonZero; }; class StyleBucketLine { public: - CapType cap = CapType::Default; - JoinType join = JoinType::Default; + CapType cap = CapType::Butt; + JoinType join = JoinType::Miter; float miter_limit = 2.0f; float round_limit = 1.0f; }; -class StyleBucketIcon { +class StyleBucketSymbol { public: - uint16_t size = 16; - vec2<float> translate {0, 0}; - TranslateAnchorType translate_anchor = TranslateAnchorType::Default; - std::string icon; - float spacing = 0.0f; - float padding = 2.0f; -}; - -class StyleBucketText { -public: - std::string field; - TextPathType path = TextPathType::Default; - TextTransformType transform = TextTransformType::Default; - std::string font; - float max_size = 16.0f; - float max_width = 15.0f * 24; - float line_height = 1.2f * 24; - float letter_spacing = 0.0f; - float alignment = 0.5f; - float vertical_alignment = 0.5; - vec2<float> translate {0, 0}; - TranslateAnchorType translate_anchor = TranslateAnchorType::Default; - float max_angle_delta = M_PI; + // Make movable only. + inline StyleBucketSymbol() = default; + inline StyleBucketSymbol(StyleBucketSymbol &&) = default; + inline StyleBucketSymbol& operator=(StyleBucketSymbol &&) = default; + inline StyleBucketSymbol(const StyleBucketSymbol &) = delete; + inline StyleBucketSymbol& operator=(const StyleBucketSymbol &) = delete; + + PlacementType placement = PlacementType::Point; float min_distance = 250.0f; - float rotate = 0.0f; // what is this? - float padding = 2.0f; - float slant = 0.0f; - bool always_visible = false; + + struct { + bool allow_overlap = false; + bool ignore_placement = false; + bool optional = false; + RotationAlignmentType rotation_alignment = RotationAlignmentType::Viewport; + float max_size = 1.0f; + std::string image; + float rotate = 0.0f; + float padding = 2.0f; + bool keep_upright = false; + vec2<float> offset = {0, 0}; + TranslateAnchorType translate_anchor = TranslateAnchorType::Map; + } icon; + + struct { + RotationAlignmentType rotation_alignment = RotationAlignmentType::Viewport; + std::string field; + std::string font; + float max_size = 16.0f; + float max_width = 15.0f * 24 /* em */; + float line_height = 1.2f * 24 /* em */; + float letter_spacing = 0.0f * 24 /* em */; + TextJustifyType justify = TextJustifyType::Center; + TextHorizontalAlignType horizontal_align = TextHorizontalAlignType::Center; + TextVerticalAlignType vertical_align = TextVerticalAlignType::Center; + float max_angle = 45.0f /* degrees */; + float rotate = 0.0f; + float slant = 0.0f; + float padding = 2.0f; + bool keep_upright = true; + TextTransformType transform = TextTransformType::None; + vec2<float> offset = {0, 0}; + TranslateAnchorType translate_anchor = TranslateAnchorType::Map; + bool allow_overlap = false; + bool ignore_placement = false; + bool optional = false; + } text; }; class StyleBucketRaster { public: }; -typedef util::variant<StyleBucketFill, StyleBucketLine, StyleBucketIcon, - StyleBucketText, StyleBucketRaster, - std::false_type> StyleBucketRender; +typedef util::variant<StyleBucketFill, StyleBucketLine, StyleBucketSymbol, + StyleBucketRaster, std::false_type> StyleBucketRender; class StyleBucket { @@ -79,6 +99,8 @@ public: std::string source_layer; FilterExpression filter; StyleBucketRender render = std::false_type(); + float min_zoom = -std::numeric_limits<float>::infinity(); + float max_zoom = std::numeric_limits<float>::infinity(); }; diff --git a/include/mbgl/style/style_parser.hpp b/include/mbgl/style/style_parser.hpp index 431e3af8db..8b8dc74f21 100644 --- a/include/mbgl/style/style_parser.hpp +++ b/include/mbgl/style/style_parser.hpp @@ -62,8 +62,8 @@ private: // Parses optional properties into a render bucket. template<typename T> bool parseRenderProperty(JSVal value, T &target, const char *name); - template <typename T, typename Parser> - bool parseRenderProperty(JSVal value, T &target, const char *name, Parser &parser); + template <typename Parser, typename T> + bool parseRenderProperty(JSVal value, T &target, const char *name); // Parses optional properties into style class properties. template <typename T> diff --git a/include/mbgl/style/style_properties.hpp b/include/mbgl/style/style_properties.hpp index c7606be254..35ea97781b 100644 --- a/include/mbgl/style/style_properties.hpp +++ b/include/mbgl/style/style_properties.hpp @@ -19,7 +19,7 @@ struct FillProperties { Color fill_color = {{ 0, 0, 0, 1 }}; Color stroke_color = {{ 0, 0, 0, -1 }}; std::array<float, 2> translate = {{ 0, 0 }}; - TranslateAnchorType translateAnchor = TranslateAnchorType::Default; + TranslateAnchorType translateAnchor = TranslateAnchorType::Map; std::string image; inline bool isVisible() const { @@ -32,8 +32,8 @@ struct LineProperties { float opacity = 1.0f; Color color = {{ 0, 0, 0, 1 }}; std::array<float, 2> translate = {{ 0, 0 }}; - TranslateAnchorType translateAnchor = TranslateAnchorType::Default; - float width = 0; + TranslateAnchorType translateAnchor = TranslateAnchorType::Map; + float width = 1; float offset = 0; float blur = 0; std::array<float, 2> dash_array = {{ 1, -1 }}; @@ -44,30 +44,38 @@ struct LineProperties { } }; -struct IconProperties { - inline IconProperties() {} - float opacity = 1.0f; - float rotate = 0.0f; - RotateAnchorType rotate_anchor = RotateAnchorType::Default; +struct SymbolProperties { + inline SymbolProperties() {} + + struct { + float opacity = 1.0f; + float rotate = 0.0f; + float size = 1.0f; + Color color = {{ 0, 0, 0, 1 }}; + Color halo_color = {{ 0, 0, 0, 0 }}; + float halo_width = 0.0f; + float halo_blur = 0.0f; + std::array<float, 2> translate = {{ 0, 0 }}; + TranslateAnchorType translate_anchor = TranslateAnchorType::Map; + } icon; + + struct { + float opacity = 1.0f; + float size = 12.0f; + Color color = {{ 0, 0, 0, 1 }}; + Color halo_color = {{ 0, 0, 0, 0 }}; + float halo_width = 0.0f; + float halo_blur = 0.0f; + std::array<float, 2> translate = {{ 0, 0 }}; + TranslateAnchorType translate_anchor = TranslateAnchorType::Map; + } text; inline bool isVisible() const { - return opacity > 0; + return (icon.opacity > 0 && (icon.color[3] > 0 || icon.halo_color[3] > 0) && icon.size > 0) || + (text.opacity > 0 && (text.color[3] > 0 || text.halo_color[3] > 0) && text.size > 0); } }; -struct TextProperties { - inline TextProperties() {} - float opacity = 1.0f; - float size = 12.0f; - Color color = {{ 0, 0, 0, 1 }}; - Color halo_color = {{ 1, 1, 1, 0.75 }}; - float halo_width = 0.25f; - float halo_blur = 1.0f; - - inline bool isVisible() const { - return opacity > 0 && (color[3] > 0 || halo_color[3] > 0) && size > 0; - } -}; struct CompositeProperties { inline CompositeProperties() {} @@ -94,14 +102,13 @@ struct RasterProperties { struct BackgroundProperties { inline BackgroundProperties() {} - Color color = {{ 1, 1, 1, 1 }}; + Color color = {{ 0, 0, 0, 1 }}; }; typedef util::variant< FillProperties, LineProperties, - IconProperties, - TextProperties, + SymbolProperties, CompositeProperties, RasterProperties, BackgroundProperties, diff --git a/include/mbgl/style/style_source.hpp b/include/mbgl/style/style_source.hpp index 6b86c30907..fa5c8dfb34 100644 --- a/include/mbgl/style/style_source.hpp +++ b/include/mbgl/style/style_source.hpp @@ -9,7 +9,7 @@ namespace mbgl { class Source; -class StyleSource { +class SourceInfo { public: const SourceType type; const std::string url; @@ -17,13 +17,21 @@ public: const int32_t min_zoom; const int32_t max_zoom; + SourceInfo(SourceType type = SourceType::Vector, const std::string &url = "", + uint32_t tile_size = 512, int32_t min_zoom = 0, int32_t max_zoom = 22) + : type(type), url(url), tile_size(tile_size), min_zoom(min_zoom), max_zoom(max_zoom) {} +}; + + +class StyleSource { +public: + const SourceInfo info; + bool enabled = false; std::shared_ptr<Source> source; - StyleSource(SourceType type = SourceType::Vector, const std::string &url = "", - uint32_t tile_size = 512, uint32_t min_zoom = 0, uint32_t max_zoom = 22) - : type(type), url(url), tile_size(tile_size), min_zoom(min_zoom), max_zoom(max_zoom) {} -}; + StyleSource(const SourceInfo &info) : info(info) {} }; +} #endif diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp index ca2061fa91..55b7685fd3 100644 --- a/include/mbgl/style/types.hpp +++ b/include/mbgl/style/types.hpp @@ -11,12 +11,13 @@ namespace mbgl { // Stores a premultiplied color, with all four channels ranging from 0..1 typedef std::array<float, 4> Color; +// ------------------------------------------------------------------------------------------------- + enum class StyleLayerType : uint8_t { Unknown, Fill, Line, - Icon, - Text, + Symbol, Raster, Composite, Background @@ -26,133 +27,172 @@ MBGL_DEFINE_ENUM_CLASS(StyleLayerTypeClass, StyleLayerType, { { StyleLayerType::Unknown, "unknown" }, { StyleLayerType::Fill, "fill" }, { StyleLayerType::Line, "line" }, - { StyleLayerType::Icon, "icon" }, - { StyleLayerType::Text, "text" }, + { StyleLayerType::Symbol, "symbol" }, { StyleLayerType::Raster, "raster" }, { StyleLayerType::Composite, "composite" }, { StyleLayerType::Background, "background" }, { StyleLayerType(-1), "unknown" }, }); +// ------------------------------------------------------------------------------------------------- + +enum class SourceType : uint8_t { + Vector, + Raster, + GeoJSON, + Video +}; + +MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, { + { SourceType::Vector, "vector" }, + { SourceType::Raster, "raster" }, + { SourceType::GeoJSON, "geojson" }, + { SourceType::Video, "video" }, +}); -enum class WindingType : uint8_t { +// ------------------------------------------------------------------------------------------------- + +enum class WindingType : bool { EvenOdd, NonZero, - Default = NonZero }; +MBGL_DEFINE_ENUM_CLASS(WindingTypeClass, WindingType, { + { WindingType::EvenOdd, "even-odd" }, + { WindingType::NonZero, "non-zero" }, +}); + +// ------------------------------------------------------------------------------------------------- + enum class CapType : uint8_t { - None, Round, Butt, Square, - Default = None }; +MBGL_DEFINE_ENUM_CLASS(CapTypeClass, CapType, { + { CapType::Round, "round" }, + { CapType::Butt, "butt" }, + { CapType::Square, "square" }, +}); + +// ------------------------------------------------------------------------------------------------- + enum class JoinType : uint8_t { - None, Miter, Bevel, - Round, - Default = None + Round }; -enum class TextPathType : uint8_t { - Horizontal, - Curve, - Default = Horizontal -}; +MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, { + { JoinType::Miter, "miter" }, + { JoinType::Bevel, "bevel" }, + { JoinType::Round, "round" }, +}); -enum class TextTransformType : uint8_t { - None, - Uppercase, - Lowercase, - Default = None +// ------------------------------------------------------------------------------------------------- + +enum class TranslateAnchorType : bool { + Map, + Viewport }; -enum class TranslateAnchorType : uint8_t { +MBGL_DEFINE_ENUM_CLASS(TranslateAnchorTypeClass, TranslateAnchorType, { + { TranslateAnchorType::Map, "map" }, + { TranslateAnchorType::Viewport, "viewport" }, +}); + +// ------------------------------------------------------------------------------------------------- + +enum class RotateAnchorType : bool { Map, Viewport, - Default = Map }; -enum class RotateAnchorType : uint8_t { +MBGL_DEFINE_ENUM_CLASS(RotateAnchorTypeClass, RotateAnchorType, { + { RotateAnchorType::Map, "map" }, + { RotateAnchorType::Viewport, "viewport" }, +}); + +// ------------------------------------------------------------------------------------------------- + +enum class PlacementType : bool { + Point, + Line, +}; + +MBGL_DEFINE_ENUM_CLASS(PlacementTypeClass, PlacementType, { + { PlacementType::Point, "point" }, + { PlacementType::Line, "line" }, +}); + +// ------------------------------------------------------------------------------------------------- + +enum class RotationAlignmentType : bool { Map, Viewport, - Default = Viewport }; -enum class SourceType : uint8_t { - Vector, - Raster, - GeoJSON, - Video, - Default = Vector -}; +MBGL_DEFINE_ENUM_CLASS(RotationAlignmentTypeClass, RotationAlignmentType, { + { RotationAlignmentType::Map, "map" }, + { RotationAlignmentType::Viewport, "viewport" }, +}); -inline WindingType parseWindingType(const std::string &type) { - if (type == "even-odd") return WindingType::EvenOdd; - if (type == "non-zero") return WindingType::NonZero; - return WindingType::Default; -} +// ------------------------------------------------------------------------------------------------- -inline CapType parseCapType(const std::string &cap) { - if (cap == "round") return CapType::Round; - if (cap == "butt") return CapType::Butt; - if (cap == "square") return CapType::Square; - return CapType::None; -} +enum class TextJustifyType : uint8_t { + Center, + Left, + Right +}; -inline JoinType parseJoinType(const std::string &join) { - if (join == "miter") return JoinType::Miter; - if (join == "bevel") return JoinType::Bevel; - if (join == "round") return JoinType::Round; - return JoinType::None; -} +MBGL_DEFINE_ENUM_CLASS(TextJustifyTypeClass, TextJustifyType, { + { TextJustifyType::Center, "center" }, + { TextJustifyType::Left, "left" }, + { TextJustifyType::Right, "right" }, +}); -inline TextPathType parseTextPathType(const std::string &path) { - if (path == "horizontal") return TextPathType::Horizontal; - if (path == "curve") return TextPathType::Curve; - return TextPathType::Default; -} +// ------------------------------------------------------------------------------------------------- -inline TextTransformType parseTextTransformType(const std::string& transform) { - if (transform == "uppercase") return TextTransformType::Uppercase; - if (transform == "lowercase") return TextTransformType::Lowercase; - return TextTransformType::Default; +enum class TextHorizontalAlignType : uint8_t { + Left, + Center, + Right, }; -inline TranslateAnchorType parseTranslateAnchorType(const std::string &anchor) { - if (anchor == "map") return TranslateAnchorType::Map; - if (anchor == "viewport") return TranslateAnchorType::Viewport; - return TranslateAnchorType::Default; -} +MBGL_DEFINE_ENUM_CLASS(TextHorizontalAlignTypeClass, TextHorizontalAlignType, { + { TextHorizontalAlignType::Left, "left" }, + { TextHorizontalAlignType::Center, "center" }, + { TextHorizontalAlignType::Right, "right" }, +}); -inline RotateAnchorType parseRotateAnchorType(const std::string &anchor) { - if (anchor == "map") return RotateAnchorType::Map; - if (anchor == "viewport") return RotateAnchorType::Viewport; - return RotateAnchorType::Default; -} +// ------------------------------------------------------------------------------------------------- -inline float parseAlignmentType(const std::string &alignment) { - if (alignment == "right") return 1.0f; - if (alignment == "left") return 0.0f; - return 0.5f; -} +enum class TextVerticalAlignType : uint8_t { + Top, + Center, + Bottom, +}; -inline float parseVerticalAlignmentType(const std::string &alignment) { - if (alignment == "bottom") return 1.0f; - if (alignment == "top") return 0.0f; - return 0.5f; -} +MBGL_DEFINE_ENUM_CLASS(TextVerticalAlignTypeClass, TextVerticalAlignType, { + { TextVerticalAlignType::Top, "top" }, + { TextVerticalAlignType::Center, "center" }, + { TextVerticalAlignType::Bottom, "bottom" }, +}); -inline SourceType parseSourceType(const std::string &source) { - if (source == "vector") return SourceType::Vector; - if (source == "raster") return SourceType::Raster; - if (source == "geojson") return SourceType::GeoJSON; - if (source == "video") return SourceType::Video; - return SourceType::Default; -} +// ------------------------------------------------------------------------------------------------- + +enum class TextTransformType : uint8_t { + None, + Uppercase, + Lowercase, +}; + +MBGL_DEFINE_ENUM_CLASS(TextTransformTypeClass, TextTransformType, { + { TextTransformType::None, "none" }, + { TextTransformType::Uppercase, "uppercase" }, + { TextTransformType::Lowercase, "lowercase" }, +}); } diff --git a/include/mbgl/text/collision.hpp b/include/mbgl/text/collision.hpp index 31103a7439..87ebdb279e 100644 --- a/include/mbgl/text/collision.hpp +++ b/include/mbgl/text/collision.hpp @@ -8,24 +8,23 @@ namespace mbgl { class Collision { public: - Collision(); + Collision(float zoom, float tileExtent, float tileSize, float placementDepth = 1); ~Collision(); - PlacementProperty place(const GlyphBoxes &boxes, - const CollisionAnchor &anchor, - float minPlacementScale, float maxPlacementScale, - float padding, bool horizontal, bool alwaysVisible); - float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale, - float maxPlacementScale, float pad); - PlacementRange getPlacementRange(const GlyphBoxes &glyphs, - float placementScale, bool horizontal); - void insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor, - float placementScale, const PlacementRange &placementRange, - bool horizontal, float padding); + float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale); + PlacementRange getPlacementRange(const GlyphBoxes &glyphs, float placementScale, + bool horizontal); + void insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor, float placementScale, + const PlacementRange &placementRange, bool horizontal); private: - void *cTree; void *hTree; + void *cTree; + +public: + const float tilePixelRatio; + const float zoom; + const float maxPlacementScale; }; } diff --git a/include/mbgl/text/glyph.hpp b/include/mbgl/text/glyph.hpp index 899c8fffee..38f9e116ab 100644 --- a/include/mbgl/text/glyph.hpp +++ b/include/mbgl/text/glyph.hpp @@ -16,7 +16,7 @@ GlyphRange getGlyphRange(char32_t glyph); struct GlyphMetrics { operator bool() const { - return width == 0 && height == 0 && advance == 0; + return !(width == 0 && height == 0 && advance == 0); } // Glyph metrics. @@ -35,7 +35,7 @@ struct Glyph { : rect(rect), metrics(metrics) {} operator bool() const { - return !metrics && !rect; + return metrics || rect; } const Rect<uint16_t> rect; @@ -44,18 +44,17 @@ struct Glyph { typedef std::map<uint32_t, Glyph> GlyphPositions; -class GlyphPlacement { +class PositionedGlyph { public: - inline explicit GlyphPlacement(uint32_t face, uint32_t glyph, uint32_t x, uint32_t y) - : face(face), glyph(glyph), x(x), y(y) {} + inline explicit PositionedGlyph(uint32_t glyph, uint32_t x, uint32_t y) + : glyph(glyph), x(x), y(y) {} - uint32_t face = 0; uint32_t glyph = 0; int32_t x = 0; int32_t y = 0; }; -typedef std::vector<GlyphPlacement> Shaping; +typedef std::vector<PositionedGlyph> Shaping; } #endif diff --git a/include/mbgl/text/glyph_store.hpp b/include/mbgl/text/glyph_store.hpp index 9c874b31b0..e0c0391c73 100644 --- a/include/mbgl/text/glyph_store.hpp +++ b/include/mbgl/text/glyph_store.hpp @@ -3,6 +3,7 @@ #include <mbgl/text/glyph.hpp> #include <mbgl/util/pbf.hpp> +#include <mbgl/util/vec.hpp> #include <cstdint> #include <vector> @@ -13,6 +14,7 @@ namespace mbgl { +class FileSource; class SDFGlyph { public: @@ -30,17 +32,11 @@ public: void insert(uint32_t id, const SDFGlyph &glyph); const std::map<uint32_t, GlyphMetrics> &getMetrics() const; const std::map<uint32_t, SDFGlyph> &getSDFs() const; - const Shaping getShaping(const std::u32string &string, - const float &maxWidth, - const float &lineHeight, - const float &alignment, - const float &verticalAlignment, - const float &letterSpacing) const; - void lineWrap(Shaping &shaping, - const float &lineHeight, - const float &maxWidth, - const float &alignment, - const float &verticalAlignment) const; + const Shaping getShaping(const std::u32string &string, float maxWidth, float lineHeight, + float horizontalAlign, float verticalAlign, float justify, + float spacing, const vec2<float> &translate) const; + void lineWrap(Shaping &shaping, float lineHeight, float maxWidth, float horizontalAlign, + float verticalAlign, float justify) const; private: std::map<uint32_t, std::string> bitmaps; @@ -51,7 +47,7 @@ private: class GlyphPBF { public: - GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange); + GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const std::shared_ptr<FileSource> &fileSource); void parse(FontStack &stack); @@ -67,13 +63,15 @@ private: // Manages Glyphrange PBF loading. class GlyphStore { public: - GlyphStore(const std::string &glyphURL); + GlyphStore(const std::shared_ptr<FileSource> &fileSource); // Block until all specified GlyphRanges of the specified font stack are loaded. void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges); FontStack &getFontStack(const std::string &fontStack); + void setURL(const std::string &url); + private: // Loads an individual glyph range from the font stack and adds it to rangeSets std::shared_future<GlyphPBF &> loadGlyphRange(const std::string &fontStack, std::map<GlyphRange, std::unique_ptr<GlyphPBF>> &rangeSets, GlyphRange range); @@ -81,9 +79,10 @@ private: FontStack &createFontStack(const std::string &fontStack); public: - const std::string glyphURL; + std::string glyphURL; private: + const std::shared_ptr<FileSource> fileSource; std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges; std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks; std::mutex mtx; diff --git a/include/mbgl/text/placement.hpp b/include/mbgl/text/placement.hpp index 69a1a10b72..28eb8d5317 100644 --- a/include/mbgl/text/placement.hpp +++ b/include/mbgl/text/placement.hpp @@ -1,36 +1,30 @@ #ifndef MBGL_TEXT_PLACEMENT #define MBGL_TEXT_PLACEMENT -#include <mbgl/text/collision.hpp> -#include <mbgl/geometry/geometry.hpp> -#include <mbgl/util/vec.hpp> +#include <mbgl/text/types.hpp> #include <mbgl/text/glyph.hpp> -#include <vector> + +#include <mbgl/util/vec.hpp> namespace mbgl { -class TextBucket; -class StyleBucketText; +struct Anchor; +class StyleBucketSymbol; class Placement { public: - Placement(int8_t zoom); + static Placement getIcon(Anchor &anchor, const Rect<uint16_t> &image, float iconBoxScale, + const std::vector<Coordinate> &line, const StyleBucketSymbol &props); - void addFeature(TextBucket &bucket, const std::vector<Coordinate> &line, - const StyleBucketText &info, - const GlyphPositions &face, - const Shaping &shaping); + static Placement getGlyphs(Anchor &anchor, const vec2<float> &origin, const Shaping &shaping, + const GlyphPositions &face, float boxScale, bool horizontal, + const std::vector<Coordinate> &line, const StyleBucketSymbol &props); -private: - const int8_t zoom; - Collision collision; - const float zOffset; - const float maxPlacementScale; + static const float globalMinScale; -public: - static const int tileExtent; - static const int glyphSize; - static const float minScale; + GlyphBoxes boxes; + PlacedGlyphs shapes; + float minScale; }; } diff --git a/include/mbgl/text/types.hpp b/include/mbgl/text/types.hpp index 9a9284b588..e2539bff62 100644 --- a/include/mbgl/text/types.hpp +++ b/include/mbgl/text/types.hpp @@ -6,6 +6,8 @@ #include <array> #include <vector> +#include <boost/optional.hpp> + namespace mbgl { typedef vec2<float> CollisionPoint; @@ -35,23 +37,23 @@ struct CollisionRect { // These are the glyph boxes that we want to have placed. struct GlyphBox { explicit GlyphBox() {} - explicit GlyphBox(const CollisionRect &bbox, const CollisionRect &box, - float minScale) - : bbox(bbox), box(box), minScale(minScale) {} - explicit GlyphBox(const CollisionRect &box, float minScale, float maxScale, - const CollisionAnchor &anchor, bool rotate) - : anchor(anchor), - box(box), - rotate(rotate), - minScale(minScale), - maxScale(maxScale) {} + explicit GlyphBox(const CollisionRect &box, + const CollisionAnchor &anchor, + float minScale, + float maxScale, + float padding) + : box(box), anchor(anchor), minScale(minScale), maxScale(maxScale), padding(padding) {} + explicit GlyphBox(const CollisionRect &box, + float minScale, + float padding) + : box(box), minScale(minScale), padding(padding) {} - CollisionAnchor anchor; - CollisionRect bbox; CollisionRect box; - bool rotate = false; + CollisionAnchor anchor; float minScale = 0.0f; float maxScale = std::numeric_limits<float>::infinity(); + float padding = 0.0f; + boost::optional<CollisionRect> hBox; }; typedef std::vector<GlyphBox> GlyphBoxes; @@ -61,19 +63,23 @@ typedef std::vector<GlyphBox> GlyphBoxes; struct PlacedGlyph { explicit PlacedGlyph(const vec2<float> &tl, const vec2<float> &tr, const vec2<float> &bl, const vec2<float> &br, - const Rect<uint16_t> &tex, float angle, const GlyphBox &glyphBox) + const Rect<uint16_t> &tex, float angle, const vec2<float> &anchor, + float minScale, float maxScale) : tl(tl), tr(tr), bl(bl), br(br), tex(tex), angle(angle), - glyphBox(glyphBox) {} + anchor(anchor), + minScale(minScale), + maxScale(maxScale) {} vec2<float> tl, tr, bl, br; Rect<uint16_t> tex; float angle; - GlyphBox glyphBox; + vec2<float> anchor; + float minScale, maxScale; }; typedef std::vector<PlacedGlyph> PlacedGlyphs; @@ -81,9 +87,8 @@ typedef std::vector<PlacedGlyph> PlacedGlyphs; // These are the placed boxes contained in the rtree. struct PlacementBox { CollisionAnchor anchor; - CollisionRect bbox; CollisionRect box; - bool rotate = false; + boost::optional<CollisionRect> hBox; PlacementRange placementRange = {{0.0f, 0.0f}}; float placementScale = 0.0f; float maxScale = std::numeric_limits<float>::infinity(); diff --git a/include/mbgl/util/filesource.hpp b/include/mbgl/util/filesource.hpp new file mode 100644 index 0000000000..0d339cbac7 --- /dev/null +++ b/include/mbgl/util/filesource.hpp @@ -0,0 +1,45 @@ +#ifndef MBGL_UTIL_FILESOURCE +#define MBGL_UTIL_FILESOURCE + +#include <string> +#include <memory> +#include <functional> + +namespace uv { +class loop; +} + +namespace mbgl { + +namespace platform { +struct Response; +} + +enum class ResourceType : uint8_t { + Unknown, + Tile, + Glyphs, + Image, + JSON +}; + +class FileSource { +public: + FileSource(); + + void setBase(const std::string &value); + const std::string &getBase() const; + + void load(ResourceType type, const std::string &url, std::function<void(platform::Response *)> callback, const std::shared_ptr<uv::loop> loop = nullptr); + +private: + // Stores a URL that is used as a base for loading resources with relative path. + std::string base; + + // Stores the absolute path to the cache directory. + const std::string cache; +}; + +} + +#endif diff --git a/include/mbgl/util/rect.hpp b/include/mbgl/util/rect.hpp index a216b8e145..dedd4afc94 100644 --- a/include/mbgl/util/rect.hpp +++ b/include/mbgl/util/rect.hpp @@ -5,7 +5,8 @@ namespace mbgl { template <typename T> struct Rect { - explicit Rect(T x, T y, T w, T h) : x(x), y(y), w(w), h(h) {} + inline Rect() {} + inline Rect(T x, T y, T w, T h) : x(x), y(y), w(w), h(h) {} T x = 0, y = 0; T w = 0, h = 0; @@ -14,7 +15,7 @@ struct Rect { return Rect(x * value, y * value, w * value, h * value); } - operator bool() const { return w == 0 || h == 0; } + operator bool() const { return w != 0 && h != 0; } }; } diff --git a/include/mbgl/util/token.hpp b/include/mbgl/util/token.hpp index a794e0489c..786654133a 100644 --- a/include/mbgl/util/token.hpp +++ b/include/mbgl/util/token.hpp @@ -15,7 +15,7 @@ namespace mbgl { namespace util { namespace detail { -const regex_impl::regex tokenRegex("\\{(\\w+)\\}"); +const regex_impl::regex tokenRegex("\\{([\\w-]+)\\}"); const regex_impl::sregex_token_iterator tokensEnd = regex_impl::sregex_token_iterator(); } diff --git a/include/mbgl/util/uv.hpp b/include/mbgl/util/uv.hpp index f36da83e4d..518e007afe 100644 --- a/include/mbgl/util/uv.hpp +++ b/include/mbgl/util/uv.hpp @@ -16,18 +16,37 @@ #pragma clang diagnostic pop #endif +#include <string> + namespace uv { +inline std::string cwd() { + size_t max = 0; + std::string dir; + do { + max += 256; + dir.resize(max); + uv_cwd(const_cast<char *>(dir.data()), &max); + } while (max == dir.size()); + dir.resize(max - 1); + return dir; +} + class loop { public: - inline loop() : l(uv_loop_new()) {} - inline ~loop() { uv_loop_delete(l); } + inline loop() { + if (uv_loop_init(&l) != 0) { + throw std::runtime_error("failed to initialize loop"); + } + } + + inline ~loop() { uv_loop_close(&l); } - inline uv_loop_t *operator*() { return l; } + inline uv_loop_t *operator*() { return &l; } private: - uv_loop_t *l; + uv_loop_t l; }; class mutex { diff --git a/ios/mapbox-gl-cocoa b/ios/mapbox-gl-cocoa -Subproject 6d29b1a2be6cf29e84349605a42140c038d80c8 +Subproject ced284d8a7bfc66540d8af50f811fd4df0e0886 diff --git a/linux/main.cpp b/linux/main.cpp index e603be55e4..b1241d64e0 100644 --- a/linux/main.cpp +++ b/linux/main.cpp @@ -45,22 +45,13 @@ int main(int argc, char *argv[]) { sigIntHandler.sa_flags = 0; sigaction(SIGINT, &sigIntHandler, NULL); - // read default stylesheet from disk - std::ifstream stylefile("./style.min.js"); - if (!stylefile.good()) { - fprintf(stderr, "Cannot read style file\n"); - return 1; - } - std::stringstream stylejson; - stylejson << stylefile.rdbuf(); - view = new GLFWView(); mbgl::Map map(*view); // Load settings mbgl::Settings_JSON settings; map.setLonLatZoom(settings.longitude, settings.latitude, settings.zoom); - map.setAngle(settings.angle); + map.setBearing(settings.bearing); map.setDebug(settings.debug); // Set access token if present @@ -72,13 +63,14 @@ int main(int argc, char *argv[]) { } // Load style - map.setStyleJSON(stylejson.str()); + const std::string style = std::string("file://") + uv::cwd() + std::string("/styles/bright/style.json"); + map.setStyleURL(style); int ret = view->run(); // Save settings map.getLonLatZoom(settings.longitude, settings.latitude, settings.zoom); - settings.angle = map.getAngle(); + settings.bearing = map.getBearing(); settings.debug = map.getDebug(); settings.save(); diff --git a/linux/mapboxgl-app.gyp b/linux/mapboxgl-app.gyp index 1d09b5444b..7b703ed75c 100644 --- a/linux/mapboxgl-app.gyp +++ b/linux/mapboxgl-app.gyp @@ -53,7 +53,7 @@ ], 'dependencies': [ '../mapboxgl.gyp:mapboxgl', - '../mapboxgl.gyp:copy_default_stylesheet', + '../mapboxgl.gyp:copy_styles', '../mapboxgl.gyp:copy_certificate_bundle', ], }, diff --git a/macosx/Info.plist b/macosx/Info.plist index 366a5d8abc..514db118ea 100644 --- a/macosx/Info.plist +++ b/macosx/Info.plist @@ -32,5 +32,16 @@ <string>0.0.1</string> <key>NSHighResolutionCapable</key> <true/> + <key>CFBundleURLTypes</key> + <array> + <dict> + <key>CFBundleURLName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleURLSchemes</key> + <array> + <string>mapboxgl</string> + </array> + </dict> + </array> </dict> </plist>
\ No newline at end of file diff --git a/macosx/main.mm b/macosx/main.mm index da78f089c5..9ecc307c05 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -4,35 +4,102 @@ #import <Foundation/Foundation.h> +@interface URLHandler : NSObject +@property (nonatomic) mbgl::Map *map; + +- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event + withReplyEvent:(NSAppleEventDescriptor *)replyEvent; +@end + +@implementation URLHandler + +- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event + withReplyEvent:(NSAppleEventDescriptor *)replyEvent { + (void)replyEvent; + + NSString* urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; + NSURL *url = [NSURL URLWithString:urlString]; + NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; + for (NSString *param in [[url query] componentsSeparatedByString:@"&"]) { + NSArray *parts = [param componentsSeparatedByString:@"="]; + if([parts count] < 2) continue; + [params setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]]; + } + + double latitude = 0, longitude = 0, zoom = 0, bearing = 0; + bool hasCenter = false, hasZoom = false, hasBearing = false; + + NSString *centerString = [params objectForKey:@"center"]; + if (centerString) { + NSArray *latlon = [centerString componentsSeparatedByString:@","]; + if ([latlon count] == 2) { + latitude = [[latlon objectAtIndex:0] doubleValue]; + longitude = [[latlon objectAtIndex:1] doubleValue]; + hasCenter = true; + } + } + + NSString *zoomString = [params objectForKey:@"zoom"]; + if (zoomString) { + zoom = [zoomString doubleValue]; + hasZoom = true; + } + + NSString *bearingString = [params objectForKey:@"bearing"]; + if (bearingString) { + bearing = [bearingString doubleValue]; + hasBearing = true; + } + + if ([self map]) { + if (hasCenter && hasZoom) { + [self map]->setLonLatZoom(longitude, latitude, zoom); + } else if (hasCenter) { + [self map]->setLonLat(longitude, latitude); + } else if (hasZoom) { + [self map]->setZoom(zoom); + } + + if (hasBearing) { + [self map]->setBearing(bearing); + } + } +} +@end + int main() { mbgl::Log::Set<mbgl::NSLogBackend>(); GLFWView view; mbgl::Map map(view); + URLHandler *handler = [[URLHandler alloc] init]; + [handler setMap:&map]; + NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager]; + [appleEventManager setEventHandler:handler andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; + // Load settings mbgl::Settings_NSUserDefaults settings; map.setLonLatZoom(settings.longitude, settings.latitude, settings.zoom); - map.setAngle(settings.angle); + map.setBearing(settings.bearing); map.setDebug(settings.debug); + // Set access token if present NSString *accessToken = [[NSProcessInfo processInfo] environment][@"MAPBOX_ACCESS_TOKEN"]; if ( ! accessToken) mbgl::Log::Warning(mbgl::Event::Setup, "No access token set. Mapbox vector tiles won't work."); if (accessToken) map.setAccessToken([accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]); // Load style - NSString *path = [[NSBundle mainBundle] pathForResource:@"style.min" ofType:@"js"]; - NSString *json = [NSString stringWithContentsOfFile:path - encoding:[NSString defaultCStringEncoding] - error:nil]; - map.setStyleJSON((std::string)[json cStringUsingEncoding:[NSString defaultCStringEncoding]]); + const std::string path([[[NSBundle mainBundle] pathForResource:@"style" ofType:@"json" inDirectory:@"styles/bright"] UTF8String]); + + map.setStyleURL(std::string("file://") + path); int ret = view.run(); // Save settings map.getLonLatZoom(settings.longitude, settings.latitude, settings.zoom); - settings.angle = map.getAngle(); + settings.bearing = map.getBearing(); settings.debug = map.getDebug(); settings.save(); diff --git a/macosx/mapboxgl-app.gyp b/macosx/mapboxgl-app.gyp index 59dfc66470..480dc2a114 100644 --- a/macosx/mapboxgl-app.gyp +++ b/macosx/mapboxgl-app.gyp @@ -23,7 +23,6 @@ 'mac_bundle': 1, 'mac_bundle_resources': [ 'Icon.icns', - '<(SHARED_INTERMEDIATE_DIR)/bin/style.min.js' ], 'xcode_settings': { 'SDKROOT': 'macosx', @@ -40,7 +39,8 @@ 'CLANG_ENABLE_OBJC_ARC': 'YES' }, 'dependencies': [ - '../mapboxgl.gyp:mapboxgl' + '../mapboxgl.gyp:bundle_styles', + '../mapboxgl.gyp:mapboxgl', ] } ] diff --git a/mapboxgl.gyp b/mapboxgl.gyp index 0c08989ce8..5a526f8e90 100644 --- a/mapboxgl.gyp +++ b/mapboxgl.gyp @@ -54,7 +54,7 @@ ], }, { - 'target_name': 'build_stylesheet', + 'target_name': 'bundle_styles', 'type': 'none', 'hard_dependency': 1, 'dependencies': [ @@ -62,40 +62,39 @@ ], 'actions': [ { - 'action_name': 'Build Stylesheet', - 'inputs': [ - 'bin/default.style.json', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/bin/style.min.js', - ], - 'action': ['<@(node)', 'bin/build-style.js', '<@(_inputs)', '<(SHARED_INTERMEDIATE_DIR)/bin'] + 'action_name': 'Touch Stylesheet Directory', + 'inputs': ['styles'], + 'outputs': ['styles'], + 'action': ['touch', 'styles'], # required for xcode http://openradar.appspot.com/7232149 } ], 'direct_dependent_settings': { - 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/bin/style.min.js', + 'mac_bundle_resources': [ + 'styles', ], } }, { - 'target_name': 'copy_default_stylesheet', + 'target_name': 'copy_styles', 'type': 'none', 'hard_dependency': 1, - 'dependencies': [ - 'build_stylesheet' + 'actions': [ + { + 'action_name': 'Touch Stylesheet Directory', + 'inputs': ['styles'], + 'outputs': ['styles'], + 'action': ['touch', 'styles'], # required for xcode http://openradar.appspot.com/7232149 + } ], 'copies': [ { - 'files': [ - '<(SHARED_INTERMEDIATE_DIR)/bin/style.min.js', - ], + 'files': [ 'styles' ], 'destination': '<(PRODUCT_DIR)' } ] }, { - 'target_name': 'copy_default_stylesheet_fixtures', + 'target_name': 'copy_fixtures', 'type': 'none', 'hard_dependency': 1, 'dependencies': [ @@ -103,9 +102,7 @@ ], 'copies': [ { - 'files': [ - 'bin/default.style.json', - ], + 'files': [ 'styles' ], 'destination': 'test/fixtures/style_parser' } ] @@ -127,7 +124,6 @@ 'type': 'static_library', 'hard_dependency': 1, 'dependencies': [ - 'build_stylesheet', 'shaders', ], 'sources': [ @@ -195,7 +191,6 @@ 'type': 'static_library', 'hard_dependency': 1, 'dependencies': [ - 'build_stylesheet', 'shaders', ], 'sources': [ diff --git a/scripts/compare_images.js b/scripts/compare_images.js deleted file mode 100755 index ef854b038e..0000000000 --- a/scripts/compare_images.js +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env node - -var fs = require('fs'); -var path = require('path'); -var spawn = require('child_process').spawn; - -var base_dir = path.join(path.resolve('.'), 'test/fixtures/styles'); - -var files = fs.readdirSync(base_dir).filter(function(name) { - return name.match(/\.info\.json$/); -}); - -var html = - '<style>\n' + - ' body { font-family: Helvetica; }\n' + - ' h2 a { color:white; text-decoration:none; }\n' + - '</style>\n' + - '<table>\n' + - '<tr>\n' + - ' <th>Actual</th>\n' + - ' <th>Expected</th>\n' + - ' <th>Info</th>\n' + - '</tr>\n'; - -var exitCode = 0; -var failures = 0; - -processFiles(); - -function processFiles() { - if (!files.length) return done(); - - var name = files.shift(); - - var info = require(path.join(base_dir, name)); - - var keys = Object.keys(info); - - processFileTest(); - - function processFileTest() { - if (!keys.length) return processFiles(); - - var key = keys.shift(); - - var base = path.basename(name, '.info.json'); - - var actual = path.join(base_dir, base + '/' + key + '.actual.png'); - var expected = path.join(base_dir, base + '/' + key + '.expected.png'); - var diff = path.join(base_dir, base + '/' + key + '.diff.png'); - - var compare = spawn('compare', ['-metric', 'MAE', actual, expected, diff ]); - var error = ''; - compare.stderr.on('data', function(data) { - error += data.toString(); - }); - compare.on('exit', function(code, signal) { - // The compare program returns 2 on error otherwise 0 if the images are similar or 1 if they are dissimilar. - if (code == 2) { - writeResult(base, key, info[key], error.trim(), Infinity); - exitCode = 2; - } else { - var match = error.match(/^\d+(?:\.\d+)?\s+\(([^\)]+)\)\s*$/); - var difference = match ? parseFloat(match[1]) : Infinity; - writeResult(base, key, info[key], match ? '' : error, difference); - - } - processFileTest(); - }); - compare.stdin.end(); - } -} - -function writeResult(base, key, info, error, difference) { - var color = 'green'; - var allowedDifference = ('diff' in info) ? info.diff : 0.01; - if (difference > allowedDifference) { - color = 'red'; - if (exitCode < 1) { - exitCode = 1; - } - failures++; - } - - html += - '<tr>\n' + - ' <td><img src="' + base + '/' + key + '.actual.png" onmouseover="this.src=\'' + base + '/' + key + '.expected.png\'" onmouseout="this.src=\'' + base + '/' + key + '.actual.png\'"></td>\n' + - ' <td><img src="' + base + '/' + key + '.expected.png" onmouseover="this.src=\'' + base + '/' + key + '.diff.png\'" onmouseout="this.src=\'' + base + '/' + key + '.expected.png\'"></td>\n' + - ' <td>\n' + - ' <h2 style="text-align:center; background:' + color + '"><a href="' + base + '.style.json">' + base + '/' + key + '</a></h2>\n' + - (error ? ' <p>' + error + '</p>\n' : '') + - ' <ul>\n' + - ' <li>diff: <strong>' + difference + '</strong></li>\n' + - ' <li>zoom: <strong>' + (info.zoom || 0) + '</strong></li>\n' + - ' <li>center: <strong>' + (info.center || [0, 0]) + '</strong></li>\n' + - ' <li>bearing: <strong>' + (info.bearing || 0) + '</strong></li>\n' + - ' <li>width: <strong>' + (info.width || 512) + '</strong></li>\n' + - ' <li>height: <strong>' + (info.height || 512) + '</strong></li>\n' + - ' </ul>\n' + - ' </td>\n' + - '</tr>\n' - ; -} - -function done() { - html += "</table>\n"; - - fs.writeFileSync(path.join(base_dir, 'index.html'), html); - console.warn('Results at: ' + path.join(base_dir, 'index.html')); - if (failures) { - console.warn('\x1B[1m\x1B[31m' + failures + ' ' + (failures == 1 ? 'image doesn\'t' : 'images don\'t') + ' match\x1B[39m\x1B[22m'); - } else { - console.warn('\x1B[1m\x1B[32mAll images match\x1B[39m\x1B[22m'); - } - - process.exit(exitCode); -}
\ No newline at end of file diff --git a/scripts/deploy_results.sh b/scripts/deploy_results.sh deleted file mode 100755 index bf13def012..0000000000 --- a/scripts/deploy_results.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -o pipefail - -aws s3 cp test/fixtures/styles/ s3://mapbox-gl-testing/headless/$TRAVIS_JOB_NUMBER/ --acl public-read --recursive - -echo http://mapbox-gl-testing.s3.amazonaws.com/headless/$TRAVIS_JOB_NUMBER/index.html diff --git a/src/geometry/icon_buffer.cpp b/src/geometry/icon_buffer.cpp index a25b2de068..c571dfa69e 100644 --- a/src/geometry/icon_buffer.cpp +++ b/src/geometry/icon_buffer.cpp @@ -1,18 +1,35 @@ #include <mbgl/geometry/icon_buffer.hpp> #include <mbgl/platform/gl.hpp> +#include <mbgl/util/math.hpp> #include <cmath> -using namespace mbgl; +namespace mbgl { -void IconVertexBuffer::add(vertex_type x, vertex_type y, uint16_t tx, uint16_t ty) { +const double IconVertexBuffer::angleFactor = 128.0 / M_PI; + +size_t IconVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float angle, float minzoom, std::array<float, 2> range, float maxzoom, float labelminzoom) { + const size_t idx = index(); void *data = addElement(); - vertex_type *vertices = static_cast<vertex_type *>(data); - vertices[0] = x; - vertices[1] = y; + int16_t *shorts = static_cast<int16_t *>(data); + shorts[0] /* pos */ = x; + shorts[1] /* pos */ = y; + shorts[2] /* offset */ = std::round(ox * 64); // use 1/64 pixels for placement + shorts[3] /* offset */ = std::round(oy * 64); + + uint8_t *ubytes = static_cast<uint8_t *>(data); + ubytes[8] /* labelminzoom */ = labelminzoom * 10; + ubytes[9] /* minzoom */ = minzoom * 10; // 1/10 zoom levels: z16 == 160. + ubytes[10] /* maxzoom */ = std::fmin(maxzoom, 25) * 10; // 1/10 zoom levels: z16 == 160. + ubytes[11] /* angle */ = (int16_t)std::round(angle * angleFactor) % 256; + ubytes[12] /* rangeend */ = util::max((int16_t)std::round(range[0] * angleFactor), (int16_t)0) % 256; + ubytes[13] /* rangestart */ = util::min((int16_t)std::round(range[1] * angleFactor), (int16_t)255) % 256; + + shorts[8] /* tex */ = tx; + shorts[9] /* tex */ = ty; + + return idx; +} - uint16_t *texture = static_cast<uint16_t *>(data); - texture[2] = tx; - texture[3] = ty; } diff --git a/src/geometry/interpolate.cpp b/src/geometry/interpolate.cpp index d9cfdfa1db..618136ff47 100644 --- a/src/geometry/interpolate.cpp +++ b/src/geometry/interpolate.cpp @@ -1,9 +1,30 @@ #include <mbgl/geometry/interpolate.hpp> +#include <mbgl/util/math.hpp> + +#include <cmath> + namespace mbgl { +const float minScale = 0.5f; +const std::array<std::vector<float>, 4> minScaleArrays = {{ + /*1:*/ { minScale }, + /*2:*/ { minScale, 2 }, + /*4:*/ { minScale, 4, 2, 4 }, + /*8:*/ { minScale, 8, 4, 8, 2, 8, 4, 8 } +}}; + + Anchors interpolate(const std::vector<Coordinate> &vertices, float spacing, - float minScale, int start) { + const float /*minScale*/, float maxScale, const float tilePixelRatio, + const int start) { + + maxScale = std::round(std::fmax(std::fmin(8.0f, maxScale / 2.0f), 1.0f)); + spacing *= tilePixelRatio / maxScale; + const size_t index = util::clamp<size_t>(std::floor(std::log(maxScale) / std::log(2)), 0, minScaleArrays.size() - 1); + const std::vector<float> &minScales = minScaleArrays[index]; + const size_t len = minScales.size(); + float distance = 0.0f; float markedDistance = 0.0f; int added = start; @@ -23,9 +44,7 @@ Anchors interpolate(const std::vector<Coordinate> &vertices, float spacing, float t = (markedDistance - distance) / segmentDist, x = util::interp(a.x, b.x, t), y = util::interp(a.y, b.y, t), - s = added % 8 == 0 ? minScale : added % 4 == 0 - ? 2 - : added % 2 == 0 ? 4 : 8; + s = minScales[added % len]; if (x >= 0 && x < 4096 && y >= 0 && y < 4096) { points.emplace_back(x, y, angle, s, i); diff --git a/src/geometry/sprite_atlas.cpp b/src/geometry/sprite_atlas.cpp index 8fa3e83888..71d3cbee99 100644 --- a/src/geometry/sprite_atlas.cpp +++ b/src/geometry/sprite_atlas.cpp @@ -88,27 +88,6 @@ void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, con } } -void draw_circle(uint32_t *dst, const int dst_stride, const int dst_x, const int dst_y, - const int width, const int height, const float blur, - const uint8_t r = 0xFF, const uint8_t g = 0xFF, const uint8_t b = 0xFF) { - const int sprite_stride = dst_stride; - const int radius = util::min(width, height); - for (int y = 0; y < height; y++) { - const int img_y = (dst_y + y) * sprite_stride + dst_x; - for (int x = 0; x < height; x++) { - const float dist = util::length(float(x) / radius - 0.5f, float(y) / radius - 0.5f); - const float t = util::smoothstep(0.5f, 0.5f - blur, dist); - const uint8_t alpha = t * 255; - - uint32_t color = (uint32_t(r * t) << 0) | - (uint32_t(g * t) << 8) | - (uint32_t(b * t) << 16) | - (uint32_t(alpha) << 24); - dst[img_y + x] = color; - } - } -} - Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(size_t width, size_t height) { // We have to allocate a new area in the bin, and store an empty image in it. // Add a 1px border around every image. @@ -125,44 +104,6 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(size_t width, size_t hei return rect; } -Rect<SpriteAtlas::dimension> SpriteAtlas::getIcon(const int size, const std::string &name) { - std::lock_guard<std::mutex> lock(mtx); - - auto rect_it = images.find(name); - if (rect_it != images.end()) { - return rect_it->second; - } - - Rect<dimension> rect = allocateImage(size, size); - if (rect.w == 0) { - if (debug::spriteWarnings) { - fprintf(stderr, "[WARNING] sprite atlas bitmap overflow\n"); - } - return rect; - } - - images.emplace(name, rect); - - allocate(); - - // Draw an antialiased circle. - draw_circle( - reinterpret_cast<uint32_t *>(data), - width * pixelRatio, - rect.x * pixelRatio, - rect.y * pixelRatio, - size * pixelRatio, - size * pixelRatio, - 1.0f / size / pixelRatio - ); - - uninitialized.emplace(name); - - dirty = true; - - return rect; -} - Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string &name, const Sprite &sprite) { std::lock_guard<std::mutex> lock(mtx); @@ -191,6 +132,12 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::getImage(const std::string &name, cons return rect; } + +Rect<SpriteAtlas::dimension> SpriteAtlas::waitForImage(const std::string &name, const Sprite &sprite) { + sprite.waitUntilLoaded(); + return getImage(name, sprite); +} + void SpriteAtlas::allocate() { if (!data) { dimension w = static_cast<dimension>(width * pixelRatio); diff --git a/src/map/map.cpp b/src/map/map.cpp index c7039cc927..ef7b9548bd 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -1,5 +1,6 @@ #include <mbgl/map/map.hpp> #include <mbgl/map/source.hpp> +#include <mbgl/map/view.hpp> #include <mbgl/platform/platform.hpp> #include <mbgl/map/sprite.hpp> #include <mbgl/util/transition.hpp> @@ -10,25 +11,36 @@ #include <mbgl/util/constants.hpp> #include <mbgl/util/uv.hpp> #include <mbgl/util/std.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/text/glyph_store.hpp> +#include <mbgl/geometry/glyph_atlas.hpp> #include <mbgl/style/style_layer_group.hpp> #include <mbgl/style/style_bucket.hpp> +#include <mbgl/util/texturepool.hpp> #include <mbgl/geometry/sprite_atlas.hpp> +#include <mbgl/util/filesource.hpp> +#include <mbgl/platform/log.hpp> #include <algorithm> #include <memory> #include <iostream> +#define _USE_MATH_DEFINES +#include <cmath> + using namespace mbgl; Map::Map(View& view) - : view(view), + : loop(std::make_shared<uv::loop>()), + view(view), transform(view), + fileSource(std::make_shared<FileSource>()), style(std::make_shared<Style>()), glyphAtlas(std::make_shared<GlyphAtlas>(1024, 1024)), + glyphStore(std::make_shared<GlyphStore>(fileSource)), spriteAtlas(std::make_shared<SpriteAtlas>(512, 512)), texturepool(std::make_shared<Texturepool>()), - painter(*this), - loop(std::make_shared<uv::loop>()) { + painter(*this) { view.initialize(this); @@ -160,7 +172,7 @@ void Map::render(uv_async_t *async) { void Map::terminate(uv_async_t *async) { // Closes all open handles on the loop. This means that the loop will automatically terminate. uv_loop_t *loop = static_cast<uv_loop_t *>(async->data); - uv_walk(loop, [](uv_handle_t *handle, void *arg) { + uv_walk(loop, [](uv_handle_t *handle, void */*arg*/) { if (!uv_is_closing(handle)) { uv_close(handle, NULL); } @@ -175,9 +187,30 @@ void Map::setup() { painter.setup(); } -void Map::setStyleJSON(std::string newStyleJSON) { +void Map::setStyleURL(const std::string &url) { + fileSource->load(ResourceType::JSON, url, [&](platform::Response *res) { + if (res->code == 200) { + // Calculate the base + const size_t pos = url.rfind('/'); + std::string base = ""; + if (pos != std::string::npos) { + base = url.substr(0, pos + 1); + } + + this->setStyleJSON(res->body, base); + } else { + Log::Error(Event::Setup, "loading style failed: %d (%s)", res->code, res->error_message.c_str()); + } + }, loop); +} + + +void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) { styleJSON.swap(newStyleJSON); + sprite.reset(); style->loadJSON((const uint8_t *)styleJSON.c_str()); + fileSource->setBase(base); + glyphStore->setURL(style->glyph_url); update(); } @@ -193,6 +226,17 @@ std::string Map::getAccessToken() const { return accessToken; } +std::shared_ptr<Sprite> Map::getSprite() { + const float pixelRatio = state.getPixelRatio(); + const std::string &sprite_url = style->getSpriteURL(); + if (!sprite || sprite->pixelRatio != pixelRatio) { + sprite = Sprite::Create(sprite_url, pixelRatio, fileSource); + } + + return sprite; +} + + #pragma mark - Size // Note: This function is called from another thread. Make sure you only call threadsafe functions! @@ -333,20 +377,20 @@ void Map::rotateBy(double sx, double sy, double ex, double ey, double duration) } // Note: This function is called from another thread. Make sure you only call threadsafe functions! -void Map::setAngle(double angle, double duration) { - transform.setAngle(angle, duration * 1_second); +void Map::setBearing(double degrees, double duration) { + transform.setAngle(-degrees * M_PI / 180, duration * 1_second); update(); } // Note: This function is called from another thread. Make sure you only call threadsafe functions! -void Map::setAngle(double angle, double cx, double cy) { - transform.setAngle(angle, cx, cy); +void Map::setBearing(double degrees, double cx, double cy) { + transform.setAngle(-degrees * M_PI / 180, cx, cy); update(); } // Note: This function is called from another thread. Make sure you only call threadsafe functions! -double Map::getAngle() const { - return transform.getAngle(); +double Map::getBearing() const { + return -transform.getAngle() / M_PI * 180; } // Note: This function is called from another thread. Make sure you only call threadsafe functions! @@ -421,13 +465,13 @@ void Map::updateSources() { updateSources(style->layers); // Then, construct or destroy the actual source object, depending on enabled state. - for (const std::shared_ptr<StyleSource> &source : activeSources) { - if (source->enabled) { - if (!source->source) { - source->source = std::make_shared<Source>(*source, getAccessToken()); + for (const std::shared_ptr<StyleSource> &style_source : activeSources) { + if (style_source->enabled) { + if (!style_source->source) { + style_source->source = std::make_shared<Source>(style_source->info, getAccessToken()); } } else { - source->source.reset(); + style_source->source.reset(); } } @@ -494,24 +538,6 @@ void Map::prepare() { bool dimensionsChanged = oldState.getFramebufferWidth() != state.getFramebufferWidth() || oldState.getFramebufferHeight() != state.getFramebufferHeight(); - if (pixelRatioChanged) { - style->sprite = std::make_shared<Sprite>(*this, state.getPixelRatio()); - if (style->sprite_url.size()) { - style->sprite->load(style->sprite_url); - } - spriteAtlas->resize(state.getPixelRatio()); - } - - // Create a new glyph store object in case the glyph URL changed. - // TODO: Move this to a less frequently called place; we only need to do this when the - // stylesheet changes. - if (glyphStore && glyphStore->glyphURL != style->glyph_url) { - glyphStore.reset(); - } - if (!glyphStore && style->glyph_url.size()) { - glyphStore = std::make_shared<GlyphStore>(style->glyph_url); - } - if (pixelRatioChanged || dimensionsChanged) { painter.clearFramebuffers(); } @@ -521,9 +547,8 @@ void Map::prepare() { style->updateProperties(state.getNormalizedZoom(), animationTime); // Allow the sprite atlas to potentially pull new sprite images if needed. - if (style->sprite && style->sprite->isLoaded()) { - spriteAtlas->update(*style->sprite); - } + spriteAtlas->resize(state.getPixelRatio()); + spriteAtlas->update(*getSprite()); updateTiles(); } @@ -557,8 +582,6 @@ void Map::render() { source->source->finishRender(painter); } - painter.renderMatte(); - // Schedule another rerender when we definitely need a next frame. if (transform.needsTransition() || style->hasTransitions()) { update(); @@ -637,7 +660,7 @@ void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass) { } } } - } else if (layer_desc->id == "background") { + } else if (layer_desc->type == StyleLayerType::Background) { // This layer defines the background color. } else { // This is a singular layer. @@ -653,6 +676,20 @@ void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass) { StyleSource &style_source = *layer_desc->bucket->style_source; + // Skip this layer if there is no data. + if (!style_source.source) { + return; + } + + // Skip this layer if it's outside the range of min/maxzoom. + // This may occur when there /is/ a bucket created for this layer, but the min/max-zoom + // is set to a fractional value, or value that is larger than the source maxzoom. + const double zoom = state.getZoom(); + if (layer_desc->bucket->min_zoom > zoom || + layer_desc->bucket->max_zoom <= zoom) { + return; + } + // Abort early if we can already deduce from the bucket type that // we're not going to render anything anyway during this pass. switch (layer_desc->type) { @@ -663,13 +700,9 @@ void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass) { if (pass == Opaque) return; if (!layer_desc->getProperties<LineProperties>().isVisible()) return; break; - case StyleLayerType::Icon: + case StyleLayerType::Symbol: if (pass == Opaque) return; - if (!layer_desc->getProperties<IconProperties>().isVisible()) return; - break; - case StyleLayerType::Text: - if (pass == Opaque) return; - if (!layer_desc->getProperties<TextProperties>().isVisible()) return; + if (!layer_desc->getProperties<SymbolProperties>().isVisible()) return; break; case StyleLayerType::Raster: if (pass == Translucent) return; @@ -684,8 +717,6 @@ void Map::renderLayer(std::shared_ptr<StyleLayer> layer_desc, RenderPass pass) { << layer_desc->type << ")" << std::endl; } - if (style_source.source) { - style_source.source->render(painter, layer_desc); - } + style_source.source->render(painter, layer_desc); } } diff --git a/src/map/raster_tile_data.cpp b/src/map/raster_tile_data.cpp index ef6bea7e62..a3c6d21a68 100644 --- a/src/map/raster_tile_data.cpp +++ b/src/map/raster_tile_data.cpp @@ -5,8 +5,8 @@ using namespace mbgl; -RasterTileData::RasterTileData(Tile::ID id, Map &map, const std::string url) - : TileData(id, map, url), +RasterTileData::RasterTileData(Tile::ID id, Map &map, const SourceInfo &source) + : TileData(id, map, source), bucket(map.getTexturepool()) { } @@ -29,6 +29,6 @@ void RasterTileData::render(Painter &painter, std::shared_ptr<StyleLayer> layer_ bucket.render(painter, layer_desc, id); } -bool RasterTileData::hasData(std::shared_ptr<StyleLayer> layer_desc) const { +bool RasterTileData::hasData(std::shared_ptr<StyleLayer> /*layer_desc*/) const { return bucket.hasData(); } diff --git a/src/map/source.cpp b/src/map/source.cpp index e46bb0abd8..f9e34d926d 100644 --- a/src/map/source.cpp +++ b/src/map/source.cpp @@ -7,7 +7,6 @@ #include <mbgl/util/string.hpp> #include <mbgl/util/texturepool.hpp> #include <mbgl/util/vec.hpp> -#include <mbgl/util/token.hpp> #include <mbgl/util/std.hpp> #include <mbgl/geometry/glyph_atlas.hpp> #include <mbgl/style/style_layer.hpp> @@ -18,21 +17,14 @@ namespace mbgl { -Source::Source(StyleSource style_source, const std::string &access_token) - : type(style_source.type), - url(normalizeSourceURL(style_source.url, access_token)), - tile_size(style_source.tile_size), - min_zoom(style_source.min_zoom), - max_zoom(style_source.max_zoom) {} - -Source::Source(SourceType type, const std::string &url, - uint32_t tile_size, uint32_t min_zoom, uint32_t max_zoom, - const std::string &access_token) - : type(type), - url(normalizeSourceURL(url, access_token)), - tile_size(tile_size), - min_zoom(min_zoom), - max_zoom(max_zoom) {} +Source::Source(SourceInfo info, const std::string &access_token) + : info( + info.type, + normalizeSourceURL(info.url, access_token), + info.tile_size, + info.min_zoom, + info.max_zoom + ) {} std::string Source::normalizeSourceURL(const std::string &url, const std::string &access_token) { const std::string t = "mapbox://"; @@ -153,18 +145,10 @@ TileData::State Source::addTile(Map &map, const Tile::ID& id) { if (!new_tile.data) { // If we don't find working tile data, we're just going to load it. - const std::string formed_url = util::replaceTokens(url, [&](const std::string &token) -> std::string { - if (token == "z") return std::to_string(normalized_id.z); - if (token == "x") return std::to_string(normalized_id.x); - if (token == "y") return std::to_string(normalized_id.y); - if (token == "ratio") return (map.getState().getPixelRatio() > 1.0 ? "@2x" : ""); - return ""; - }); - - if (type == SourceType::Vector) { - new_tile.data = std::make_shared<VectorTileData>(normalized_id, map, formed_url); - } else if (type == SourceType::Raster) { - new_tile.data = std::make_shared<RasterTileData>(normalized_id, map, formed_url); + if (info.type == SourceType::Vector) { + new_tile.data = std::make_shared<VectorTileData>(normalized_id, map, info); + } else if (info.type == SourceType::Raster) { + new_tile.data = std::make_shared<RasterTileData>(normalized_id, map, info); } else { throw std::runtime_error("source type not implemented"); } @@ -232,14 +216,14 @@ bool Source::updateTiles(Map &map) { // Figure out what tiles we need to load int32_t clamped_zoom = map.getState().getIntegerZoom(); - if (clamped_zoom > max_zoom) clamped_zoom = max_zoom; - if (clamped_zoom < min_zoom) clamped_zoom = min_zoom; + if (clamped_zoom > info.max_zoom) clamped_zoom = info.max_zoom; + if (clamped_zoom < info.min_zoom) clamped_zoom = info.min_zoom; int32_t max_covering_zoom = clamped_zoom + 1; - if (max_covering_zoom > max_zoom) max_covering_zoom = max_zoom; + if (max_covering_zoom > info.max_zoom) max_covering_zoom = info.max_zoom; int32_t min_covering_zoom = clamped_zoom - 10; - if (min_covering_zoom < min_zoom) min_covering_zoom = min_zoom; + if (min_covering_zoom < info.min_zoom) min_covering_zoom = info.min_zoom; // Map four viewport corners to pixel coordinates box box = map.getState().cornersToBox(clamped_zoom); @@ -379,7 +363,7 @@ void _scanTriangle(const mbgl::vec2<double> a, const mbgl::vec2<double> b, const } double Source::getZoom(const TransformState &state) const { - double offset = log(util::tileSize / tile_size) / log(2); + double offset = log(util::tileSize / info.tile_size) / log(2); offset += (state.getPixelRatio() > 1.0 ? 1 :0); return state.getZoom() + offset; } @@ -387,7 +371,7 @@ double Source::getZoom(const TransformState &state) const { std::forward_list<mbgl::Tile::ID> Source::covering_tiles(const TransformState &state, int32_t clamped_zoom, const box& points) { int32_t dim = std::pow(2, clamped_zoom); std::forward_list<mbgl::Tile::ID> tiles; - bool is_raster = (type == SourceType::Raster); + bool is_raster = (info.type == SourceType::Raster); double search_zoom = getZoom(state); auto scanLine = [&tiles, clamped_zoom, is_raster, search_zoom](int32_t x0, int32_t x1, int32_t y, int32_t ymax) { diff --git a/src/map/sprite.cpp b/src/map/sprite.cpp index fd8a4ec7b5..f396fec07d 100644 --- a/src/map/sprite.cpp +++ b/src/map/sprite.cpp @@ -5,6 +5,7 @@ #include <string> #include <mbgl/platform/platform.hpp> +#include <mbgl/util/filesource.hpp> #include <mbgl/util/uv.hpp> #include <mbgl/util/std.hpp> @@ -20,76 +21,98 @@ SpritePosition::SpritePosition(uint16_t x, uint16_t y, uint16_t width, uint16_t pixelRatio(pixelRatio) { } -Sprite::Sprite(Map &map, float pixelRatio) - : pixelRatio(pixelRatio), +std::shared_ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, const std::shared_ptr<FileSource> &fileSource) { + std::shared_ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio)); + sprite->load(fileSource); + return sprite; +} + +Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio) + : valid(base_url.length() > 0), + pixelRatio(pixelRatio), + spriteURL(base_url + (pixelRatio > 1 ? "@2x" : "") + ".png"), + jsonURL(base_url + (pixelRatio > 1 ? "@2x" : "") + ".json"), raster(), - map(map), - loaded(false) { + loadedImage(false), + loadedJSON(false), + future(promise.get_future()) { +} + +void Sprite::waitUntilLoaded() const { + future.wait(); +} + +Sprite::operator bool() const { + return valid && isLoaded() && !pos.empty(); } -void Sprite::load(const std::string& base_url) { - loaded = false; - url = base_url; - std::shared_ptr<Sprite> sprite = shared_from_this(); - std::string suffix = (pixelRatio > 1 ? "@2x" : ""); +// Note: This is a separate function that must be called exactly once after creation +// The reason this isn't part of the constructor is that calling shared_from_this() in +// the constructor fails. +void Sprite::load(const std::shared_ptr<FileSource> &fileSource) { + if (!valid) { + // Treat a non-existent sprite as a successfully loaded empty sprite. + loadedImage = true; + loadedJSON = true; + promise.set_value(); + return; + } + + std::shared_ptr<Sprite> sprite = shared_from_this(); - platform::request_http(base_url + suffix + ".json", [sprite](platform::Response *res) { + fileSource->load(ResourceType::JSON, jsonURL, [sprite](platform::Response *res) { if (res->code == 200) { sprite->body.swap(res->body); - sprite->asyncParseJSON(); + sprite->parseJSON(); + sprite->complete(); } else { - fprintf(stderr, "failed to load sprite info\n"); - fprintf(stderr, "error %d: %s\n", res->code, res->error_message.c_str()); + Log::Warning(Event::Sprite, "Failed to load sprite info: Error %d: %s", res->code, res->error_message.c_str()); + if (!sprite->future.valid()) { + sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res->error_message))); + } } - }, map.getLoop()); + }); - platform::request_http(base_url + suffix + ".png", [sprite](platform::Response *res) { + fileSource->load(ResourceType::Image, spriteURL, [sprite](platform::Response *res) { if (res->code == 200) { - sprite->image.swap(res->body); - sprite->asyncParseImage(); + sprite->image.swap(res->body); + sprite->parseImage(); + sprite->complete(); } else { - fprintf(stderr, "failed to load sprite image\n"); - fprintf(stderr, "error %d: %s\n", res->code, res->error_message.c_str()); + Log::Warning(Event::Sprite, "Failed to load sprite image: Error %d: %s", res->code, res->error_message.c_str()); + if (!sprite->future.valid()) { + sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res->error_message))); + } } - }, map.getLoop()); + }); } -void Sprite::complete(std::shared_ptr<Sprite> &sprite) { - const bool raster = bool(sprite->raster); - const bool json = bool(sprite->pos.size()); - if (raster && json && !sprite->loaded) { - sprite->loaded = true; - sprite->map.update(); - Log::Info(Event::Sprite, "loaded %s", sprite->url.c_str()); +void Sprite::complete() { + if (loadedImage && loadedJSON) { + Log::Info(Event::Sprite, "loaded %s", spriteURL.c_str()); + promise.set_value(); } } bool Sprite::isLoaded() const { - return loaded; + return loadedImage && loadedJSON; } -void Sprite::asyncParseImage() { - new uv::work<std::shared_ptr<Sprite>>(map.getLoop(), parseImage, complete, shared_from_this()); +void Sprite::parseImage() { + raster = std::make_unique<util::Image>(image); + image.clear(); + loadedImage = true; } -void Sprite::asyncParseJSON() { - new uv::work<std::shared_ptr<Sprite>>(map.getLoop(), parseJSON, complete, shared_from_this()); -} - -void Sprite::parseImage(std::shared_ptr<Sprite> &sprite) { - sprite->raster = std::make_unique<util::Image>(sprite->image); - sprite->image.clear(); -} - -void Sprite::parseJSON(std::shared_ptr<Sprite> &sprite) { +void Sprite::parseJSON() { rapidjson::Document d; - d.Parse<0>(sprite->body.c_str()); - sprite->body.clear(); - - if (d.IsObject()) { - std::unordered_map<std::string, SpritePosition> pos; + d.Parse<0>(body.c_str()); + body.clear(); + if (d.HasParseError()) { + Log::Warning(Event::Sprite, "sprite JSON is invalid"); + } else if (d.IsObject()) { for (rapidjson::Value::ConstMemberIterator itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr) { const std::string& name = itr->name.GetString(); const rapidjson::Value& value = itr->value; @@ -109,9 +132,11 @@ void Sprite::parseJSON(std::shared_ptr<Sprite> &sprite) { pos.emplace(name, SpritePosition { x, y, width, height, pixelRatio }); } } - - sprite->pos.swap(pos); + } else { + Log::Warning(Event::Sprite, "sprite JSON root is not an object"); } + + loadedJSON = true; } const SpritePosition &Sprite::getSpritePosition(const std::string& name) const { diff --git a/src/map/tile_data.cpp b/src/map/tile_data.cpp index 82f8fdedfc..a94eb00fbf 100644 --- a/src/map/tile_data.cpp +++ b/src/map/tile_data.cpp @@ -1,15 +1,25 @@ #include <mbgl/map/tile_data.hpp> #include <mbgl/map/map.hpp> +#include <mbgl/style/style_source.hpp> +#include <mbgl/util/token.hpp> #include <mbgl/util/string.hpp> +#include <mbgl/util/filesource.hpp> using namespace mbgl; -TileData::TileData(Tile::ID id, Map &map, const std::string url) +TileData::TileData(Tile::ID id, Map &map, const SourceInfo &source) : id(id), state(State::initial), map(map), - url(url), + source(source), + url(util::replaceTokens(source.url, [&](const std::string &token) -> std::string { + if (token == "z") return std::to_string(id.z); + if (token == "x") return std::to_string(id.x); + if (token == "y") return std::to_string(id.y); + if (token == "ratio") return (map.getState().getPixelRatio() > 1.0 ? "@2x" : ""); + return ""; + })), debugBucket(debugFontBuffer) { // Initialize tile debug coordinates const std::string str = util::sprintf<32>("%d/%d/%d", id.z, id.x, id.y); @@ -29,7 +39,7 @@ void TileData::request() { // Note: Somehow this feels slower than the change to request_http() std::weak_ptr<TileData> weak_tile = shared_from_this(); - req = platform::request_http(url, [weak_tile](platform::Response *res) { + map.getFileSource()->load(ResourceType::Tile, url, [weak_tile](platform::Response *res) { std::shared_ptr<TileData> tile = weak_tile.lock(); if (!tile || tile->state == State::obsolete) { // noop. Tile is obsolete and we're now just waiting for the refcount @@ -46,7 +56,7 @@ void TileData::request() { fprintf(stderr, "[%s] tile loading failed: %d, %s\n", tile->url.c_str(), res->code, res->error_message.c_str()); #endif } - }, map.getLoop()); + }); } void TileData::cancel() { diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp index 8af61c6aab..97b071a163 100644 --- a/src/map/tile_parser.cpp +++ b/src/map/tile_parser.cpp @@ -6,8 +6,7 @@ #include <mbgl/style/style_layer_group.hpp> #include <mbgl/renderer/fill_bucket.hpp> #include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/renderer/icon_bucket.hpp> -#include <mbgl/renderer/text_bucket.hpp> +#include <mbgl/renderer/symbol_bucket.hpp> #include <mbgl/renderer/raster_bucket.hpp> #include <mbgl/util/raster.hpp> #include <mbgl/util/constants.hpp> @@ -35,14 +34,16 @@ TileParser::TileParser(const std::string &data, VectorTileData &tile, const std::shared_ptr<const Style> &style, const std::shared_ptr<GlyphAtlas> &glyphAtlas, const std::shared_ptr<GlyphStore> &glyphStore, - const std::shared_ptr<SpriteAtlas> &spriteAtlas) + const std::shared_ptr<SpriteAtlas> &spriteAtlas, + const std::shared_ptr<Sprite> &sprite) : vector_data(pbf((const uint8_t *)data.data(), data.size())), tile(tile), style(style), glyphAtlas(glyphAtlas), glyphStore(glyphStore), spriteAtlas(spriteAtlas), - placement(tile.id.z) { + sprite(sprite), + collision(tile.id.z, 4096, tile.source.tile_size, tile.depth) { } void TileParser::parse() { @@ -51,21 +52,6 @@ void TileParser::parse() { bool TileParser::obsolete() const { return tile.state == TileData::State::obsolete; } -void TileParser::addGlyph(uint64_t tileid, const std::string stackname, - const std::u32string &string, const FontStack &fontStack, - GlyphAtlas &glyphAtlas, GlyphPositions &face) { - const std::map<uint32_t, SDFGlyph> &sdfs = fontStack.getSDFs(); - // Loop through all characters and add glyph to atlas, positions. - for (uint32_t chr : string) { - auto sdf_it = sdfs.find(chr); - if (sdf_it != sdfs.end()) { - const SDFGlyph& sdf = sdf_it->second; - const Rect<uint16_t> rect = glyphAtlas.addGlyph(tileid, stackname, sdf); - face.emplace(chr, Glyph{rect, sdf.metrics}); - } - } -} - void TileParser::parseStyleLayers(std::shared_ptr<StyleLayerGroup> group) { if (!group) { return; @@ -108,6 +94,10 @@ std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bu return nullptr; } + // Skip this bucket if we are to not render this + if (tile.id.z < std::floor(bucket_desc->min_zoom) && std::floor(bucket_desc->min_zoom) < tile.source.max_zoom) return nullptr; + if (tile.id.z >= std::ceil(bucket_desc->max_zoom)) return nullptr; + auto layer_it = vector_data.layers.find(bucket_desc->source_layer); if (layer_it != vector_data.layers.end()) { const VectorTileLayer &layer = layer_it->second; @@ -115,10 +105,8 @@ std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bu return createFillBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketFill>()); } else if (bucket_desc->render.is<StyleBucketLine>()) { return createLineBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketLine>()); - } else if (bucket_desc->render.is<StyleBucketIcon>()) { - return createIconBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketIcon>()); - } else if (bucket_desc->render.is<StyleBucketText>()) { - return createTextBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketText>()); + } else if (bucket_desc->render.is<StyleBucketSymbol>()) { + return createSymbolBucket(layer, bucket_desc->filter, bucket_desc->render.get<StyleBucketSymbol>()); } else if (bucket_desc->render.is<StyleBucketRaster>()) { return nullptr; } else { @@ -136,7 +124,7 @@ std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bu } template <class Bucket> -void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter) { +void TileParser::addBucketGeometries(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter) { FilteredVectorTileLayer filtered_layer(layer, filter); for (pbf feature : filtered_layer) { if (obsolete()) @@ -153,103 +141,20 @@ void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, } } -template <class Bucket, typename... Args> -void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter, Args&& ...args) { - FilteredVectorTileLayer filtered_layer(layer, filter); - for (const pbf &feature_pbf : filtered_layer) { - if (obsolete()) { - return; - } - bucket->addFeature({feature_pbf, layer}, std::forward<Args>(args)...); - } -} - - std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill) { std::unique_ptr<FillBucket> bucket = std::make_unique<FillBucket>(tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, fill); - addBucketFeatures(bucket, layer, filter); + addBucketGeometries(bucket, layer, filter); return obsolete() ? nullptr : std::move(bucket); } std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line) { std::unique_ptr<LineBucket> bucket = std::make_unique<LineBucket>(tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, line); - addBucketFeatures(bucket, layer, filter); + addBucketGeometries(bucket, layer, filter); return obsolete() ? nullptr : std::move(bucket); } -std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon) { - std::unique_ptr<IconBucket> bucket = std::make_unique<IconBucket>(tile.iconVertexBuffer, icon); - addBucketFeatures(bucket, layer, filter, *spriteAtlas); +std::unique_ptr<Bucket> TileParser::createSymbolBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketSymbol &symbol) { + std::unique_ptr<SymbolBucket> bucket = std::make_unique<SymbolBucket>(symbol, collision); + bucket->addFeatures(layer, filter, tile.id, *spriteAtlas, *sprite, *glyphAtlas, *glyphStore); return obsolete() ? nullptr : std::move(bucket); } - -std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketText &text) { - - // Make sure that we always have a valid glyph store. If this is not set, the stylesheet - // doesn't specify a glyph URL. - if (!glyphStore) { - return nullptr; - } - - const StyleBucketText &properties = text; - - std::unique_ptr<TextBucket> bucket = std::make_unique<TextBucket>( - tile.textVertexBuffer, tile.triangleElementsBuffer, properties, placement); - - util::utf8_to_utf32 ucs4conv; - - std::vector<std::pair<std::u32string, pbf>> labels; - - // Determine and load glyph ranges - { - std::set<GlyphRange> ranges; - - FilteredVectorTileLayer filtered_layer(layer, filter); - for (const pbf &feature_pbf : filtered_layer) { - if (obsolete()) - return nullptr; - VectorTileFeature feature{feature_pbf, layer}; - - std::string u8string = util::replaceTokens(properties.field, feature.properties); - - auto& convert = std::use_facet<std::ctype<char>>(std::locale()); - if (properties.transform == TextTransformType::Uppercase) { - convert.toupper(&u8string[0], &u8string[0] + u8string.size()); - } else if (properties.transform == TextTransformType::Lowercase) { - convert.tolower(&u8string[0], &u8string[0] + u8string.size()); - } - - std::u32string string = ucs4conv.convert(u8string); - - // Loop through all characters of this text and collect unique codepoints. - for (char32_t chr : string) { - ranges.insert(getGlyphRange(chr)); - } - - labels.emplace_back(string, feature.geometry); - } - - glyphStore->waitForGlyphRanges(properties.font, ranges); - } - - // Create a copy! - const FontStack &fontStack = glyphStore->getFontStack(properties.font); - GlyphPositions face; - - // Shape and place all labels. - for (const std::pair<std::u32string, pbf> &label : labels) { - - // Shape labels. - const Shaping shaping = fontStack.getShaping(label.first, properties.max_width, - properties.line_height, properties.alignment, - properties.vertical_alignment, properties.letter_spacing); - - // Place labels. - addGlyph(tile.id.to_uint64(), properties.font, label.first, fontStack, *glyphAtlas, - face); - - bucket->addFeature(label.second, face, shaping); - } - - return std::move(bucket); -} diff --git a/src/map/transform.cpp b/src/map/transform.cpp index ccf92f974b..6338df6a48 100644 --- a/src/map/transform.cpp +++ b/src/map/transform.cpp @@ -1,9 +1,11 @@ #include <mbgl/map/transform.hpp> +#include <mbgl/map/view.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/mat4.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/time.hpp> +#include <mbgl/util/transition.hpp> #include <mbgl/platform/platform.hpp> #include <cstdio> @@ -96,8 +98,8 @@ void Transform::setLonLat(const double lon, const double lat, const timestamp du uv::writelock lock(mtx); const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999); - double xn = -std::round(lon * Bc); - double yn = std::round(0.5 * Cc * std::log((1 + f) / (1 - f))); + double xn = -lon * Bc; + double yn = 0.5 * Cc * std::log((1 + f) / (1 - f)); _setScaleXY(current.scale, xn, yn, duration); } @@ -109,13 +111,12 @@ void Transform::setLonLatZoom(const double lon, const double lat, const double z double new_scale = std::pow(2.0, zoom); const double s = new_scale * util::tileSize; - zc = s / 2; Bc = s / 360; Cc = s / (2 * M_PI); const double f = std::fmin(std::fmax(std::sin(D2R * lat), -0.9999), 0.9999); - double xn = -std::round(lon * Bc); - double yn = std::round(0.5 * Cc * log((1 + f) / (1 - f))); + double xn = -lon * Bc; + double yn = 0.5 * Cc * log((1 + f) / (1 - f)); _setScaleXY(new_scale, xn, yn, duration); } @@ -307,7 +308,6 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl } const double s = final.scale * util::tileSize; - zc = s / 2; Bc = s / 360; Cc = s / (2 * M_PI); diff --git a/src/map/vector_tile_data.cpp b/src/map/vector_tile_data.cpp index 7d0e0fb5a8..2bf339b65f 100644 --- a/src/map/vector_tile_data.cpp +++ b/src/map/vector_tile_data.cpp @@ -1,13 +1,16 @@ #include <mbgl/map/vector_tile_data.hpp> +#include <mbgl/map/tile_parser.hpp> #include <mbgl/util/std.hpp> #include <mbgl/map/map.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/style/style_bucket.hpp> +#include <mbgl/geometry/glyph_atlas.hpp> using namespace mbgl; -VectorTileData::VectorTileData(Tile::ID id, Map &map, const std::string url) - : TileData(id, map, url) { +VectorTileData::VectorTileData(Tile::ID id, Map &map, const SourceInfo &source) + : TileData(id, map, source), + depth(id.z >= source.max_zoom ? map.getMaxZoom() - id.z : 1) { } VectorTileData::~VectorTileData() { @@ -18,7 +21,8 @@ VectorTileData::~VectorTileData() { } void VectorTileData::beforeParse() { - parser = std::make_unique<TileParser>(data, *this, map.getStyle(), map.getGlyphAtlas(), map.getGlyphStore(), map.getSpriteAtlas()); + + parser = std::make_unique<TileParser>(data, *this, map.getStyle(), map.getGlyphAtlas(), map.getGlyphStore(), map.getSpriteAtlas(), map.getSprite()); } void VectorTileData::parse() { @@ -63,7 +67,7 @@ bool VectorTileData::hasData(std::shared_ptr<StyleLayer> layer_desc) const { auto databucket_it = buckets.find(layer_desc->bucket->name); if (databucket_it != buckets.end()) { assert(databucket_it->second); - return databucket_it->second->hasData(); + return databucket_it->second->hasData(); } } return false; diff --git a/src/renderer/debug_bucket.cpp b/src/renderer/debug_bucket.cpp index c9a68e2efb..e51dd75b1e 100644 --- a/src/renderer/debug_bucket.cpp +++ b/src/renderer/debug_bucket.cpp @@ -14,7 +14,7 @@ DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer) : fontBuffer(fontBuffer) { } -void DebugBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& /*id*/) { +void DebugBucket::render(Painter& painter, std::shared_ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/) { painter.renderDebugText(*this); } diff --git a/src/renderer/icon_bucket.cpp b/src/renderer/icon_bucket.cpp deleted file mode 100644 index af988fee80..0000000000 --- a/src/renderer/icon_bucket.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include <mbgl/renderer/icon_bucket.hpp> -#include <mbgl/geometry/icon_buffer.hpp> -#include <mbgl/geometry/elements_buffer.hpp> -#include <mbgl/geometry/geometry.hpp> -#include <mbgl/geometry/sprite_atlas.hpp> - -#include <mbgl/renderer/painter.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/map/sprite.hpp> -#include <mbgl/map/vector_tile.hpp> - -#include <mbgl/platform/gl.hpp> -#include <mbgl/util/constants.hpp> -#include <mbgl/util/token.hpp> - -#include <cassert> - -struct geometry_too_long_exception : std::exception {}; - -using namespace mbgl; - -IconBucket::IconBucket(IconVertexBuffer& vertexBuffer, - const StyleBucketIcon& properties) - : properties(properties), - vertexBuffer(vertexBuffer), - vertex_start(vertexBuffer.index()) { -} - -void IconBucket::addFeature(const VectorTileFeature &feature, SpriteAtlas &sprite_atlas) { - std::string field; - - if (properties.icon.size()) { - field = util::replaceTokens(properties.icon, feature.properties); - } - - if (!field.size()) { - field = "<circle>"; - } - - const Rect<uint16_t> rect = sprite_atlas.getIcon(properties.size, field); - const uint16_t tx = rect.x + rect.w / 2; - const uint16_t ty = rect.y + rect.h / 2; - - Geometry::command cmd; - pbf geom = feature.geometry; - Geometry geometry(geom); - int32_t x, y; - while ((cmd = geometry.next(x, y)) != Geometry::end) { - if (cmd == Geometry::move_to) { - vertexBuffer.add(x, y, tx, ty); - } else { - if (debug::tileParseWarnings) { - fprintf(stderr, "[WARNING] other command than move_to in icon geometry\n"); - } - } - } - - vertex_end = vertexBuffer.index(); -} - -void IconBucket::render(Painter& painter, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) { - painter.renderIcon(*this, layer_desc, id); -} - -bool IconBucket::hasData() const { - return vertex_end > 0; -} - -void IconBucket::drawIcons(IconShader& shader) { - char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); - array.bind(shader, vertexBuffer, vertex_index); - glDrawArrays(GL_POINTS, 0, (GLsizei)(vertex_end - vertex_start)); -} - -void IconBucket::drawIcons(DotShader& shader) { - char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); - array.bind(shader, vertexBuffer, vertex_index); - glDrawArrays(GL_POINTS, 0, (GLsizei)(vertex_end - vertex_start)); -} diff --git a/src/renderer/line_bucket.cpp b/src/renderer/line_bucket.cpp index 44fe784b1a..2e04820e42 100644 --- a/src/renderer/line_bucket.cpp +++ b/src/renderer/line_bucket.cpp @@ -80,7 +80,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) { CapType beginCap = properties.cap; CapType endCap = closed ? CapType::Butt : properties.cap; - JoinType currentJoin = JoinType::None; + JoinType currentJoin = JoinType::Miter; Coordinate currentVertex = Coordinate::null(), prevVertex = Coordinate::null(), diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp index a323b0582a..e6977171a4 100644 --- a/src/renderer/painter.cpp +++ b/src/renderer/painter.cpp @@ -1,5 +1,6 @@ #include <mbgl/renderer/painter.hpp> #include <mbgl/map/map.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/util/std.hpp> #include <mbgl/util/string.hpp> @@ -205,21 +206,3 @@ const mat4 &Painter::translatedMatrix(const std::array<float, 2> &translation, c return vtxMatrix; } } - -void Painter::renderMatte() { - gl::group group("matte"); - glDisable(GL_DEPTH_TEST); - glStencilFunc(GL_EQUAL, 0x0, 0xFF); - - Color matte = {{ 0, 0, 0, 1 }}; - - useProgram(plainShader->program); - plainShader->setMatrix(nativeMatrix); - - // Draw the clipping mask - matteArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0)); - plainShader->setColor(matte); - glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index()); - - glEnable(GL_DEPTH_TEST); -} diff --git a/src/renderer/painter_fill.cpp b/src/renderer/painter_fill.cpp index f78f006df0..29d77e05d9 100644 --- a/src/renderer/painter_fill.cpp +++ b/src/renderer/painter_fill.cpp @@ -1,5 +1,6 @@ #include <mbgl/renderer/painter.hpp> #include <mbgl/renderer/fill_bucket.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/style_layer.hpp> #include <mbgl/map/map.hpp> #include <mbgl/map/sprite.hpp> @@ -70,10 +71,10 @@ void Painter::renderFill(FillBucket& bucket, const FillProperties& properties, c } if ((fill_color[3] >= 1.0f) == (pass == Opaque)) { - const std::shared_ptr<Sprite> &sprite = map.getStyle()->sprite; + Sprite &sprite = *map.getSprite(); if (properties.image.size() && sprite) { SpriteAtlas &spriteAtlas = *map.getSpriteAtlas(); - Rect<uint16_t> imagePos = spriteAtlas.getImage(properties.image, *sprite); + Rect<uint16_t> imagePos = spriteAtlas.getImage(properties.image, sprite); float factor = 8.0 / std::pow(2, map.getState().getIntegerZoom() - id.z); diff --git a/src/renderer/painter_framebuffers.cpp b/src/renderer/painter_framebuffers.cpp index 83ad189877..f221b884ce 100644 --- a/src/renderer/painter_framebuffers.cpp +++ b/src/renderer/painter_framebuffers.cpp @@ -1,6 +1,7 @@ #include <mbgl/renderer/painter.hpp> #include <mbgl/renderer/fill_bucket.hpp> #include <mbgl/map/map.hpp> +#include <mbgl/map/view.hpp> #include <mbgl/util/clip_ids.hpp> using namespace mbgl; diff --git a/src/renderer/painter_icon.cpp b/src/renderer/painter_icon.cpp deleted file mode 100644 index 158030df19..0000000000 --- a/src/renderer/painter_icon.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/icon_bucket.hpp> -#include <mbgl/map/map.hpp> -#include <mbgl/map/sprite.hpp> -#include <mbgl/style/style_layer.hpp> -#include <mbgl/geometry/sprite_atlas.hpp> -#include <mbgl/util/math.hpp> - -using namespace mbgl; - -void Painter::renderIcon(IconBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) { - // Abort early. - if (!bucket.hasData()) return; - if (pass == Opaque) return; - - const IconProperties &properties = layer_desc->getProperties<IconProperties>(); - -// TODO: when translating icon, are we doing this in the bucket already? -// const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor); - - SpriteAtlas &spriteAtlas = *map.getSpriteAtlas(); - - useProgram(iconShader->program); - iconShader->setMatrix(matrix); -// TODO: update - iconShader->setColor({{ 1, 1, 1, 1 }}); - iconShader->setImage(0); - iconShader->setRatio(map.getState().getPixelRatio()); - iconShader->setDimension({{ - spriteAtlas.getTextureWidth(), - spriteAtlas.getTextureHeight(), - }}); - - spriteAtlas.bind(map.getState().isChanging()); - - const float iconSize = bucket.properties.size * map.getState().getPixelRatio(); - iconShader->setSize(iconSize); -#ifndef GL_ES_VERSION_2_0 - glPointSize(iconSize); - glEnable(GL_POINT_SPRITE); -#endif - - glDepthRange(strata, 1.0f); - bucket.drawIcons(*iconShader); -} diff --git a/src/renderer/painter_line.cpp b/src/renderer/painter_line.cpp index 00b3a48474..dc09f58951 100644 --- a/src/renderer/painter_line.cpp +++ b/src/renderer/painter_line.cpp @@ -14,11 +14,13 @@ void Painter::renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_d float width = properties.width; float offset = properties.offset / 2; + float antialiasing = 1 / map.getState().getPixelRatio(); + float blur = properties.blur + antialiasing; // These are the radii of the line. We are limiting it to 16, which will result // in a point size of 64 on retina. - float inset = std::fmin((std::fmax(-1, offset - width / 2 - 0.5) + 1), 16.0f); - float outset = std::fmin(offset + width / 2 + 0.5, 16.0f); + float inset = std::fmin((std::fmax(-1, offset - width / 2 - antialiasing / 2) + 1), 16.0f); + float outset = std::fmin(offset + width / 2 + antialiasing / 2, 16.0f); Color color = properties.color; color[0] *= properties.opacity; @@ -80,6 +82,7 @@ void Painter::renderLine(LineBucket& bucket, std::shared_ptr<StyleLayer> layer_d lineShader->setDashArray({{ dash_length, dash_gap }}); lineShader->setLineWidth({{ outset, inset }}); lineShader->setRatio(map.getState().getPixelRatio()); + lineShader->setBlur(blur); lineShader->setColor(color); bucket.drawLines(*lineShader); } diff --git a/src/renderer/painter_prerender.cpp b/src/renderer/painter_prerender.cpp index ea461ca5d2..5d61b95535 100644 --- a/src/renderer/painter_prerender.cpp +++ b/src/renderer/painter_prerender.cpp @@ -17,7 +17,7 @@ void Painter::preparePrerender(PrerenderedTexture &texture) { glViewport(0, 0, texture.properties.size, texture.properties.size); } -void Painter::finishPrerender(PrerenderedTexture &texture) { +void Painter::finishPrerender(PrerenderedTexture &/*texture*/) { glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); diff --git a/src/renderer/painter_raster.cpp b/src/renderer/painter_raster.cpp index 228d609aaa..ef2eab2a6c 100644 --- a/src/renderer/painter_raster.cpp +++ b/src/renderer/painter_raster.cpp @@ -5,10 +5,10 @@ using namespace mbgl; -void Painter::renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& /*id*/) { +void Painter::renderRaster(RasterBucket& bucket, std::shared_ptr<StyleLayer> /*layer_desc*/, const Tile::ID& /*id*/) { if (pass == Translucent) return; - const RasterProperties &properties = layer_desc->getProperties<RasterProperties>(); + // const RasterProperties &properties = layer_desc->getProperties<RasterProperties>(); depthMask(false); diff --git a/src/renderer/painter_symbol.cpp b/src/renderer/painter_symbol.cpp new file mode 100644 index 0000000000..a2238a3544 --- /dev/null +++ b/src/renderer/painter_symbol.cpp @@ -0,0 +1,217 @@ +#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/symbol_bucket.hpp> +#include <mbgl/style/style_layer.hpp> +#include <mbgl/geometry/glyph_atlas.hpp> +#include <mbgl/geometry/sprite_atlas.hpp> +#include <mbgl/map/map.hpp> +#include <mbgl/util/math.hpp> +#include <cmath> + +namespace mbgl { + +void Painter::renderSymbol(SymbolBucket &bucket, std::shared_ptr<StyleLayer> layer_desc, + const Tile::ID &/*id*/) { + // Abort early. + if (pass == Opaque) { + return; + } + + const SymbolProperties &properties = layer_desc->getProperties<SymbolProperties>(); + + + if (bucket.hasTextData()) { + mat4 exMatrix; + matrix::copy(exMatrix, projMatrix); + if (bucket.properties.placement == PlacementType::Line) { + matrix::rotate_z(exMatrix, exMatrix, map.getState().getAngle()); + } + + // If layerStyle.size > bucket.info.fontSize then labels may collide + float fontSize = std::fmin(properties.text.size, bucket.properties.text.max_size); + matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f); + + useProgram(textShader->program); + textShader->setMatrix(matrix); + textShader->setExtrudeMatrix(exMatrix); + + GlyphAtlas &glyphAtlas = *map.getGlyphAtlas(); + glyphAtlas.bind(); + textShader->setTextureSize( + {{static_cast<float>(glyphAtlas.width), static_cast<float>(glyphAtlas.height)}}); + + // Convert the -pi..pi to an int8 range. + float angle = std::round((map.getState().getAngle()) / M_PI * 128); + + // adjust min/max zooms for variable font sies + float zoomAdjust = log(fontSize / bucket.properties.text.max_size) / log(2); + + textShader->setAngle((int32_t)(angle + 256) % 256); + textShader->setFlip(bucket.properties.placement == PlacementType::Line ? 1 : 0); + textShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * + 10); // current zoom level + + // Label fading + const timestamp duration = 300_milliseconds; + const timestamp currentTime = util::now(); + + std::deque<FrameSnapshot> &history = frameHistory.history; + + // Remove frames until only one is outside the duration, or until there are only three + while (history.size() > 3 && history[1].t + duration < currentTime) { + history.pop_front(); + } + + if (history[1].t + duration < currentTime) { + history[0].z = history[1].z; + } + + assert("there should never be less than three frames in the history" && + (history.size() >= 3)); + + // Find the range of zoom levels we want to fade between + float startingZ = history.front().z; + const FrameSnapshot lastFrame = history.back(); + float endingZ = lastFrame.z; + float lowZ = std::fmin(startingZ, endingZ); + float highZ = std::fmax(startingZ, endingZ); + + // Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one + // duration + float zoomDiff = endingZ - history[1].z, timeDiff = lastFrame.t - history[1].t; + float fadedist = zoomDiff / (timeDiff / duration); + +#if defined(DEBUG) +// if (std::isnan(fadedist)) +// fprintf(stderr, "fadedist should never be NaN\n"); +#endif + + // At end of a zoom when the zoom stops changing continue pretending to zoom at that speed + // bump is how much farther it would have been if it had continued zooming at the same rate + float bump = (currentTime - lastFrame.t) / duration * fadedist; + + textShader->setFadeDist(fadedist * 10); + textShader->setMinFadeZoom(std::floor(lowZ * 10)); + textShader->setMaxFadeZoom(std::floor(highZ * 10)); + textShader->setFadeZoom((map.getState().getNormalizedZoom() + bump) * 10); + + // This defines the gamma around the SDF cutoff value. + const float sdfGamma = 1.0f / 10.0f; + + // Our signed distance fields are scaled so that 1 pixel is scaled to 32 pixels. + // Our cutoff between positive and negative values is hard coded to 64 (== 2 pixels). + // This means that our 6/8 of the value range lies outside the glyph outline. + const float sdfOffset = (256.0f - 64.0f) / 32.0f; + + // Currently, all of our fonts are rendered with a font size of 24px. + const float sdfFontSize = 24.0f; + + // The default gamma value has to be adjust for the current pixelratio so that we're not + // drawing + // blurry font on retina screens. + const float gamma = sdfGamma * sdfFontSize / fontSize / map.getState().getPixelRatio(); + + // We're drawing in the translucent pass which is bottom-to-top, so we need + // to draw the halo first. + if (properties.text.halo_color[3] > 0.0f) { + const float haloWidth = util::clamp( + (sdfOffset - properties.text.halo_width / (fontSize / sdfFontSize)) / 8.0f, 0.0f, + 1.0f); + + if (properties.text.halo_blur != 0.0f) { + // We are converting the halo_blur value to current screen pixels. + // Then we're dividing it by two because the gamma value is added/subtracted into + // both + // directions in the shader, but the halo_blur describes the entire width of the + // blur. + // Note that this does *not* have to be adjusted for retina screens, because we want + // the + // same blur width when we explicitly specify one. + textShader->setGamma((properties.text.halo_blur / (fontSize / sdfFontSize)) / 8.0f / + 2.0f); + } else { + textShader->setGamma(sdfGamma); + } + + if (properties.text.opacity < 1.0f) { + Color color = properties.text.halo_color; + color[0] *= properties.text.opacity; + color[1] *= properties.text.opacity; + color[2] *= properties.text.opacity; + color[3] *= properties.text.opacity; + textShader->setColor(color); + } else { + textShader->setColor(properties.text.halo_color); + } + textShader->setBuffer(haloWidth); + glDepthRange(strata, 1.0f); + bucket.drawGlyphs(*textShader); + } + + if (properties.text.color[3] > 0.0f) { + // Then, we draw the text over the halo + textShader->setGamma(gamma); + if (properties.text.opacity < 1.0f) { + Color color = properties.text.color; + color[0] *= properties.text.opacity; + color[1] *= properties.text.opacity; + color[2] *= properties.text.opacity; + color[3] *= properties.text.opacity; + textShader->setColor(color); + } else { + textShader->setColor(properties.text.color); + } + textShader->setBuffer((256.0f - 64.0f) / 256.0f); + glDepthRange(strata + strata_epsilon, 1.0f); + bucket.drawGlyphs(*textShader); + } + } + + if (bucket.hasIconData()) { + mat4 exMatrix; + matrix::copy(exMatrix, projMatrix); + + const float angleOffset = + bucket.properties.icon.rotation_alignment == RotationAlignmentType::Map + ? map.getState().getAngle() + : 0; + + if (angleOffset) { + matrix::rotate_z(exMatrix, exMatrix, angleOffset); + } + + // If layerStyle.size > bucket.info.fontSize then labels may collide + const float fontSize = properties.icon.size != 0 ? properties.icon.size : bucket.properties.icon.max_size; + const float fontScale = fontSize / 1.0f; + matrix::scale(exMatrix, exMatrix, fontScale, fontScale, 1.0f); + + useProgram(iconShader->program); + iconShader->setMatrix(matrix); + iconShader->setExtrudeMatrix(exMatrix); + + SpriteAtlas &spriteAtlas = *map.getSpriteAtlas(); + spriteAtlas.bind(map.getState().isChanging() || bucket.properties.placement == PlacementType::Line || angleOffset != 0 || fontScale != 1); + iconShader->setTextureSize( + {{static_cast<float>(spriteAtlas.getWidth()), static_cast<float>(spriteAtlas.getHeight())}}); + + // Convert the -pi..pi to an int8 range. + const float angle = std::round((map.getState().getAngle()) / M_PI * 128); + + // adjust min/max zooms for variable font sies + float zoomAdjust = log(fontSize / bucket.properties.icon.max_size) / log(2); + + iconShader->setAngle((int32_t)(angle + 256) % 256); + iconShader->setFlip(bucket.properties.placement == PlacementType::Line ? 1 : 0); + iconShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * + 10); // current zoom level + + iconShader->setFadeDist(0 * 10); + iconShader->setMinFadeZoom(map.getState().getNormalizedZoom() * 10); + iconShader->setMaxFadeZoom(map.getState().getNormalizedZoom() * 10); + iconShader->setFadeZoom(map.getState().getNormalizedZoom() * 10); + iconShader->setOpacity(properties.icon.opacity); + + glDepthRange(strata, 1.0f); + bucket.drawIcons(*iconShader); + } +} +} diff --git a/src/renderer/painter_text.cpp b/src/renderer/painter_text.cpp deleted file mode 100644 index 54fa1a7371..0000000000 --- a/src/renderer/painter_text.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include <mbgl/renderer/painter.hpp> -#include <mbgl/renderer/text_bucket.hpp> -#include <mbgl/style/style_layer.hpp> -#include <mbgl/map/map.hpp> -#include <mbgl/util/math.hpp> -#include <cmath> - -using namespace mbgl; - -void Painter::renderText(TextBucket& bucket, std::shared_ptr<StyleLayer> layer_desc, const Tile::ID& id) { - // Abort early. - if (pass == Opaque) return; - if (!bucket.hasData()) return; - - const TextProperties &properties = layer_desc->getProperties<TextProperties>(); - - mat4 exMatrix; - matrix::copy(exMatrix, projMatrix); - if (bucket.properties.path == TextPathType::Curve) { - matrix::rotate_z(exMatrix, exMatrix, map.getState().getAngle()); - } - - // If layerStyle.size > bucket.info.fontSize then labels may collide - float fontSize = std::fmin(properties.size, bucket.properties.max_size); - matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f); - -// TODO: figure out whether we actually need to account for this while painting; we might already have -// done this during label placement. -// const mat4 &vtxMatrix = translatedMatrix(properties.translate, id, properties.translateAnchor); - - useProgram(textShader->program); - textShader->setMatrix(matrix); - textShader->setExtrudeMatrix(exMatrix); - - GlyphAtlas &glyphAtlas = *map.getGlyphAtlas(); - glyphAtlas.bind(); - textShader->setTextureSize({{static_cast<float>(glyphAtlas.width), - static_cast<float>(glyphAtlas.height)}}); - - // Convert the -pi..pi to an int8 range. - float angle = std::round((map.getState().getAngle()) / M_PI * 128); - - // adjust min/max zooms for variable font sies - float zoomAdjust = log(fontSize / bucket.properties.max_size) / log(2); - - textShader->setAngle((int32_t)(angle + 256) % 256); - textShader->setFlip(bucket.properties.path == TextPathType::Curve ? 1 : 0); - textShader->setZoom((map.getState().getNormalizedZoom() - zoomAdjust) * 10); // current zoom level - - // Label fading - const timestamp duration = 300_milliseconds; - const timestamp currentTime = util::now(); - - std::deque<FrameSnapshot> &history = frameHistory.history; - - // Remove frames until only one is outside the duration, or until there are only three - while (history.size() > 3 && history[1].t + duration < currentTime) { - history.pop_front(); - } - - if (history[1].t + duration < currentTime) { - history[0].z = history[1].z; - } - - assert("there should never be less than three frames in the history" && (history.size() >= 3)); - - // Find the range of zoom levels we want to fade between - float startingZ = history.front().z; - const FrameSnapshot lastFrame = history.back(); - float endingZ = lastFrame.z; - float lowZ = std::fmin(startingZ, endingZ); - float highZ = std::fmax(startingZ, endingZ); - - // Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one duration - float zoomDiff = endingZ - history[1].z, - timeDiff = lastFrame.t - history[1].t; - float fadedist = zoomDiff / (timeDiff / duration); - -#if defined(DEBUG) - if (std::isnan(fadedist)) fprintf(stderr, "fadedist should never be NaN\n"); -#endif - - // At end of a zoom when the zoom stops changing continue pretending to zoom at that speed - // bump is how much farther it would have been if it had continued zooming at the same rate - float bump = (currentTime - lastFrame.t) / duration * fadedist; - - textShader->setFadeDist(fadedist * 10); - textShader->setMinFadeZoom(std::floor(lowZ * 10)); - textShader->setMaxFadeZoom(std::floor(highZ * 10)); - textShader->setFadeZoom((map.getState().getNormalizedZoom() + bump) * 10); - - // This defines the gamma around the SDF cutoff value. - const float sdfGamma = 0.75f / 10.0f; - - // Our signed distance fields are scaled so that 1 pixel is scaled to 32 pixels. - // Our cutoff between positive and negative values is hard coded to 64 (== 2 pixels). - // This means that our 6/8 of the value range lies outside the glyph outline. - const float sdfOffset = (256.0f - 64.0f) / 32.0f; - - // Currently, all of our fonts are rendered with a font size of 24px. - const float sdfFontSize = 24.0f; - - // The default gamma value has to be adjust for the current pixelratio so that we're not drawing - // blurry font on retina screens. - const float gamma = sdfGamma * sdfFontSize / fontSize / map.getState().getPixelRatio(); - - // We're drawing in the translucent pass which is bottom-to-top, so we need - // to draw the halo first. - if (properties.halo_color[3] > 0.0f) { - const float haloWidth = util::clamp((sdfOffset - properties.halo_width / (fontSize / sdfFontSize)) / 8.0f, 0.0f, 1.0f); - - if (properties.halo_blur != 0.0f) { - // We are converting the halo_blur value to current screen pixels. - // Then we're dividing it by two because the gamma value is added/subtracted into both - // directions in the shader, but the halo_blur describes the entire width of the blur. - // Note that this does *not* have to be adjusted for retina screens, because we want the - // same blur width when we explicitly specify one. - textShader->setGamma((properties.halo_blur / (fontSize / sdfFontSize)) / 8.0f / 2.0f); - } else { - textShader->setGamma(sdfGamma); - } - - if (properties.opacity < 1.0f) { - Color color = properties.halo_color; - color[0] *= properties.opacity; - color[1] *= properties.opacity; - color[2] *= properties.opacity; - color[3] *= properties.opacity; - textShader->setColor(color); - } else { - textShader->setColor(properties.halo_color); - } - textShader->setBuffer(haloWidth); - glDepthRange(strata, 1.0f); - bucket.drawGlyphs(*textShader); - } - - if (properties.color[3] > 0.0f) { - // Then, we draw the text over the halo - textShader->setGamma(gamma); - if (properties.opacity < 1.0f) { - Color color = properties.color; - color[0] *= properties.opacity; - color[1] *= properties.opacity; - color[2] *= properties.opacity; - color[3] *= properties.opacity; - textShader->setColor(color); - } else { - textShader->setColor(properties.color); - } - textShader->setBuffer((256.0f - 64.0f) / 256.0f); - glDepthRange(strata + strata_epsilon, 1.0f); - bucket.drawGlyphs(*textShader); - } -} diff --git a/src/renderer/symbol_bucket.cpp b/src/renderer/symbol_bucket.cpp new file mode 100644 index 0000000000..4072cf0c61 --- /dev/null +++ b/src/renderer/symbol_bucket.cpp @@ -0,0 +1,394 @@ +#include <mbgl/renderer/symbol_bucket.hpp> +#include <mbgl/geometry/text_buffer.hpp> +#include <mbgl/geometry/icon_buffer.hpp> +#include <mbgl/geometry/glyph_atlas.hpp> +#include <mbgl/geometry/sprite_atlas.hpp> +#include <mbgl/geometry/geometry.hpp> +#include <mbgl/geometry/anchor.hpp> +#include <mbgl/geometry/interpolate.hpp> +#include <mbgl/renderer/painter.hpp> +#include <mbgl/text/glyph_store.hpp> +#include <mbgl/text/placement.hpp> +#include <mbgl/platform/log.hpp> +#include <mbgl/text/collision.hpp> +#include <mbgl/map/sprite.hpp> + +#include <mbgl/util/utf.hpp> +#include <mbgl/util/token.hpp> +#include <mbgl/util/math.hpp> + +namespace mbgl { + +SymbolBucket::SymbolBucket(const StyleBucketSymbol &properties, Collision &collision) + : properties(properties), collision(collision) {} + +void SymbolBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, + const Tile::ID &id) { + painter.renderSymbol(*this, layer_desc, id); +} + +bool SymbolBucket::hasData() const { return hasTextData() || hasIconData(); } + +bool SymbolBucket::hasTextData() const { return !text.groups.empty(); } + +bool SymbolBucket::hasIconData() const { return !icon.groups.empty(); } + +void SymbolBucket::addGlyphsToAtlas(uint64_t tileid, const std::string stackname, + const std::u32string &string, const FontStack &fontStack, + GlyphAtlas &glyphAtlas, GlyphPositions &face) { + const std::map<uint32_t, SDFGlyph> &sdfs = fontStack.getSDFs(); + // Loop through all characters and add glyph to atlas, positions. + for (uint32_t chr : string) { + auto sdf_it = sdfs.find(chr); + if (sdf_it != sdfs.end()) { + const SDFGlyph &sdf = sdf_it->second; + const Rect<uint16_t> rect = glyphAtlas.addGlyph(tileid, stackname, sdf); + face.emplace(chr, Glyph{rect, sdf.metrics}); + } + } +} + +std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &layer, + const FilterExpression &filter, + GlyphStore &glyphStore, + const Sprite &sprite) { + const bool text = properties.text.field.size(); + const bool icon = properties.icon.image.size(); + + std::vector<SymbolFeature> features; + + if (!text && !icon) { + return features; + } + + util::utf8_to_utf32 ucs4conv; + + // Determine and load glyph ranges + std::set<GlyphRange> ranges; + + FilteredVectorTileLayer filtered_layer(layer, filter); + for (const pbf &feature_pbf : filtered_layer) { + const VectorTileFeature feature{feature_pbf, layer}; + + SymbolFeature ft; + + if (text) { + std::string u8string = util::replaceTokens(properties.text.field, feature.properties); + + auto &convert = std::use_facet<std::ctype<char>>(std::locale()); + if (properties.text.transform == TextTransformType::Uppercase) { + convert.toupper(&u8string[0], &u8string[0] + u8string.size()); + } else if (properties.text.transform == TextTransformType::Lowercase) { + convert.tolower(&u8string[0], &u8string[0] + u8string.size()); + } + + ft.label = ucs4conv.convert(u8string); + + if (ft.label.size()) { + // Loop through all characters of this text and collect unique codepoints. + for (char32_t chr : ft.label) { + ranges.insert(getGlyphRange(chr)); + } + } + } + + if (icon) { + ft.sprite = util::replaceTokens(properties.icon.image, feature.properties); + } + + if (ft.label.length() || ft.sprite.length()) { + ft.geometry = feature.geometry; + features.push_back(std::move(ft)); + } + } + + glyphStore.waitForGlyphRanges(properties.text.font, ranges); + sprite.waitUntilLoaded(); + + return features; +} + +void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpression &filter, + const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite, + GlyphAtlas &glyphAtlas, GlyphStore &glyphStore) { + + const std::vector<SymbolFeature> features = processFeatures(layer, filter, glyphStore, sprite); + + float horizontalAlign = 0.5; + if (properties.text.horizontal_align == TextHorizontalAlignType::Right) + horizontalAlign = 1; + else if (properties.text.horizontal_align == TextHorizontalAlignType::Left) + horizontalAlign = 0; + + float verticalAlign = 0.5; + if (properties.text.vertical_align == TextVerticalAlignType::Bottom) + verticalAlign = 1; + else if (properties.text.vertical_align == TextVerticalAlignType::Top) + verticalAlign = 0; + + float justify = 0.5; + if (properties.text.justify == TextJustifyType::Right) justify = 1; + else if (properties.text.justify == TextJustifyType::Left) justify = 0; + + const FontStack &fontStack = glyphStore.getFontStack(properties.text.font); + + for (const SymbolFeature &feature : features) { + Shaping shaping; + Rect<uint16_t> image; + GlyphPositions face; + + // if feature has text, shape the text + if (feature.label.length()) { + shaping = fontStack.getShaping( + /* string */ feature.label, + /* maxWidth */ properties.text.max_width, + /* lineHeight */ properties.text.line_height, + /* horizontalAlign */ horizontalAlign, + /* verticalAlign */ verticalAlign, + /* justify */ justify, + /* spacing */ properties.text.letter_spacing, + /* translate */ properties.text.offset); + + // Add the glyphs we need for this label to the glyph atlas. + if (shaping.size()) { + addGlyphsToAtlas(id.to_uint64(), properties.text.font, feature.label, fontStack, + glyphAtlas, face); + } + } + + // if feature has icon, get sprite atlas position + if (feature.sprite.length()) { + image = spriteAtlas.waitForImage(feature.sprite, sprite); + } + + // if either shaping or icon position is present, add the feature + if (shaping.size() || image) { + addFeature(feature.geometry, shaping, face, image); + } + } +} + +void SymbolBucket::addFeature(const pbf &geom_pbf, const Shaping &shaping, + const GlyphPositions &face, const Rect<uint16_t> &image) { + // Decode all lines. + std::vector<Coordinate> line; + Geometry::command cmd; + + Coordinate coord; + pbf geom(geom_pbf); + Geometry geometry(geom); + int32_t x, y; + while ((cmd = geometry.next(x, y)) != Geometry::end) { + if (cmd == Geometry::move_to) { + if (!line.empty()) { + addFeature(line, shaping, face, image); + line.clear(); + } + } + line.emplace_back(x, y); + } + if (line.size()) { + addFeature(line, shaping, face, image); + } +} + +bool byScale(const Anchor &a, const Anchor &b) { return a.scale < b.scale; } + +const PlacementRange fullRange{{2 * M_PI, 0}}; + +void SymbolBucket::addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, + const GlyphPositions &face, const Rect<uint16_t> &image) { + assert(line.size()); + + const float minScale = 0.5f; + const float glyphSize = 24.0f; + + const bool horizontalText = + properties.text.rotation_alignment == RotationAlignmentType::Viewport; + const bool horizontalIcon = + properties.icon.rotation_alignment == RotationAlignmentType::Viewport; + const float fontScale = properties.text.max_size / glyphSize; + const float textBoxScale = collision.tilePixelRatio * fontScale; + const float iconBoxScale = collision.tilePixelRatio * properties.icon.max_size; + const bool iconWithoutText = properties.text.optional || !shaping.size(); + const bool textWithoutIcon = properties.icon.optional || !image; + + Anchors anchors; + + if (properties.placement == PlacementType::Line) { + // Line labels + anchors = interpolate(line, properties.min_distance, minScale, collision.maxPlacementScale, + collision.tilePixelRatio); + + // Sort anchors by segment so that we can start placement with the + // anchors that can be shown at the lowest zoom levels. + std::sort(anchors.begin(), anchors.end(), byScale); + + } else { + // Point labels + anchors = {Anchor{float(line[0].x), float(line[0].y), 0, minScale}}; + } + + // TODO: figure out correct ascender height. + const vec2<float> origin = {0, -17}; + + for (Anchor &anchor : anchors) { + + // Calculate the scales at which the text and icons can be first shown without overlap + Placement glyphPlacement; + Placement iconPlacement; + float glyphScale = 0; + float iconScale = 0; + + if (shaping.size()) { + glyphPlacement = Placement::getGlyphs(anchor, origin, shaping, face, textBoxScale, + horizontalText, line, properties); + glyphScale = + properties.text.allow_overlap + ? glyphPlacement.minScale + : collision.getPlacementScale(glyphPlacement.boxes, glyphPlacement.minScale); + if (!glyphScale && !iconWithoutText) + continue; + } + + if (image) { + iconPlacement = Placement::getIcon(anchor, image, iconBoxScale, line, properties); + iconScale = + properties.icon.allow_overlap + ? iconPlacement.minScale + : collision.getPlacementScale(iconPlacement.boxes, iconPlacement.minScale); + if (!iconScale && !textWithoutIcon) + continue; + } + + if (!iconWithoutText && !textWithoutIcon) { + iconScale = glyphScale = util::max(iconScale, glyphScale); + } else if (!textWithoutIcon && glyphScale) { + glyphScale = util::max(iconScale, glyphScale); + } else if (!iconWithoutText && iconScale) { + iconScale = util::max(iconScale, glyphScale); + } + + // Get the rotation ranges it is safe to show the glyphs + PlacementRange glyphRange = + (!glyphScale || properties.text.allow_overlap) + ? fullRange + : collision.getPlacementRange(glyphPlacement.boxes, glyphScale, horizontalText); + PlacementRange iconRange = + (!iconScale || properties.icon.allow_overlap) + ? fullRange + : collision.getPlacementRange(iconPlacement.boxes, iconScale, horizontalIcon); + + const PlacementRange maxRange = {{ + util::min(iconRange[0], glyphRange[0]), util::max(iconRange[1], glyphRange[1]), + }}; + + if (!iconWithoutText && !textWithoutIcon) { + iconRange = glyphRange = maxRange; + } else if (!textWithoutIcon) { + glyphRange = maxRange; + } else if (!iconWithoutText) { + iconRange = maxRange; + } + + // Insert final placement into collision tree and add glyphs/icons to buffers + if (glyphScale) { + if (!properties.text.ignore_placement) { + collision.insert(glyphPlacement.boxes, anchor, glyphScale, glyphRange, + horizontalText); + } + addSymbols(text, glyphPlacement.shapes, glyphScale, glyphRange); + } + + if (iconScale) { + if (!properties.icon.ignore_placement) { + collision.insert(iconPlacement.boxes, anchor, iconScale, iconRange, horizontalIcon); + } + addSymbols(icon, iconPlacement.shapes, iconScale, iconRange); + } + } +} + +template <typename Buffer> +void SymbolBucket::addSymbols(Buffer &buffer, const PlacedGlyphs &symbols, float scale, + PlacementRange placementRange) { + const float zoom = collision.zoom; + + const float placementZoom = std::log(scale) / std::log(2) + zoom; + + for (const PlacedGlyph &symbol : symbols) { + const auto &tl = symbol.tl; + const auto &tr = symbol.tr; + const auto &bl = symbol.bl; + const auto &br = symbol.br; + const auto &tex = symbol.tex; + const auto &angle = symbol.angle; + + float minZoom = + util::max(static_cast<float>(zoom + log(symbol.minScale) / log(2)), placementZoom); + float maxZoom = util::min(static_cast<float>(zoom + log(symbol.maxScale) / log(2)), 25.0f); + const auto &glyphAnchor = symbol.anchor; + + if (maxZoom <= minZoom) + continue; + + // Lower min zoom so that while fading out the label + // it can be shown outside of collision-free zoom levels + if (minZoom == placementZoom) { + minZoom = 0; + } + + const int glyph_vertex_length = 4; + + if (!buffer.groups.size() || + (buffer.groups.back().vertex_length + glyph_vertex_length > 65535)) { + // Move to a new group because the old one can't hold the geometry. + buffer.groups.emplace_back(); + } + + // We're generating triangle fans, so we always start with the first + // coordinate in this polygon. + ElementGroup &triangleGroup = buffer.groups.back(); + uint32_t triangleIndex = triangleGroup.vertex_length; + + // coordinates (2 triangles) + buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, tex.y, angle, minZoom, + placementRange, maxZoom, placementZoom); + buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, tex.x + tex.w, tex.y, angle, + minZoom, placementRange, maxZoom, placementZoom); + buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, tex.y + tex.h, angle, + minZoom, placementRange, maxZoom, placementZoom); + buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, + angle, minZoom, placementRange, maxZoom, placementZoom); + + // add the two triangles, referencing the four coordinates we just inserted. + buffer.triangles.add(triangleIndex + 0, triangleIndex + 1, triangleIndex + 2); + buffer.triangles.add(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3); + + triangleGroup.vertex_length += glyph_vertex_length; + triangleGroup.elements_length += 2; + } +} + +void SymbolBucket::drawGlyphs(TextShader &shader) { + char *vertex_index = BUFFER_OFFSET(0); + char *elements_index = BUFFER_OFFSET(0); + for (ElementGroup &group : text.groups) { + group.array.bind(shader, text.vertices, text.triangles, vertex_index); + glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index); + vertex_index += group.vertex_length * text.vertices.itemSize; + elements_index += group.elements_length * text.triangles.itemSize; + } +} + +void SymbolBucket::drawIcons(IconShader &shader) { + char *vertex_index = BUFFER_OFFSET(0); + char *elements_index = BUFFER_OFFSET(0); + for (ElementGroup &group : icon.groups) { + group.array.bind(shader, icon.vertices, icon.triangles, vertex_index); + glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index); + vertex_index += group.vertex_length * icon.vertices.itemSize; + elements_index += group.elements_length * icon.triangles.itemSize; + } +} +} diff --git a/src/renderer/text_bucket.cpp b/src/renderer/text_bucket.cpp deleted file mode 100644 index 45c7bf971f..0000000000 --- a/src/renderer/text_bucket.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include <mbgl/renderer/text_bucket.hpp> -#include <mbgl/geometry/text_buffer.hpp> -#include <mbgl/geometry/elements_buffer.hpp> -#include <mbgl/geometry/geometry.hpp> - -#include <mbgl/renderer/painter.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/map/vector_tile.hpp> -#include <mbgl/text/placement.hpp> -#include <mbgl/text/glyph_store.hpp> -#include <mbgl/util/constants.hpp> - -#include <mbgl/util/math.hpp> -#include <mbgl/platform/gl.hpp> - -#include <iostream> - -#include <cassert> - -using namespace mbgl; - -TextBucket::TextBucket( - TextVertexBuffer &vertexBuffer, - TriangleElementsBuffer &triangleElementsBuffer, - const StyleBucketText &properties, Placement &placement) - : properties(properties), - vertexBuffer(vertexBuffer), - triangleElementsBuffer(triangleElementsBuffer), - placement(placement), - vertex_start(vertexBuffer.index()), - triangle_elements_start(triangleElementsBuffer.index()) {} - -void TextBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom, - PlacementRange placementRange, float zoom) { - placementZoom += zoom; - - for (const PlacedGlyph &glyph : glyphs) { - const auto &tl = glyph.tl; - const auto &tr = glyph.tr; - const auto &bl = glyph.bl; - const auto &br = glyph.br; - const auto &tex = glyph.tex; - const auto &angle = glyph.angle; - - float minZoom = util::max( - static_cast<float>(zoom + log(glyph.glyphBox.minScale) / log(2)), - placementZoom); - float maxZoom = util::min( - static_cast<float>(zoom + log(glyph.glyphBox.maxScale) / log(2)), - 25.0f); - const auto &glyphAnchor = glyph.glyphBox.anchor; - - if (maxZoom <= minZoom) - continue; - - // Lower min zoom so that while fading out the label - // it can be shown outside of collision-free zoom levels - if (minZoom == placementZoom) { - minZoom = 0; - } - - const int glyph_vertex_length = 4; - - if (!triangleGroups.size() || - (triangleGroups.back().vertex_length + glyph_vertex_length > - 65535)) { - // Move to a new group because the old one can't hold the geometry. - triangleGroups.emplace_back(); - } - - // We're generating triangle fans, so we always start with the first - // coordinate in this polygon. - triangle_group_type &triangleGroup = triangleGroups.back(); - uint32_t triangleIndex = triangleGroup.vertex_length; - - // coordinates (2 triangles) - vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, - tex.y, angle, minZoom, placementRange, maxZoom, - placementZoom); - vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, - tex.x + tex.w, tex.y, angle, minZoom, placementRange, - maxZoom, placementZoom); - vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, - tex.y + tex.h, angle, minZoom, placementRange, - maxZoom, placementZoom); - vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y, - tex.x + tex.w, tex.y + tex.h, angle, minZoom, - placementRange, maxZoom, placementZoom); - - // add the two triangles, referencing the four coordinates we just - // inserted. - triangleElementsBuffer.add(triangleIndex + 0, triangleIndex + 1, - triangleIndex + 2); - triangleElementsBuffer.add(triangleIndex + 1, triangleIndex + 2, - triangleIndex + 3); - - triangleGroup.vertex_length += glyph_vertex_length; - triangleGroup.elements_length += 2; - } -}; - -void TextBucket::addFeature(const pbf &geom_pbf, - const GlyphPositions &face, - const Shaping &shaping) { - // Decode all lines. - std::vector<Coordinate> line; - Geometry::command cmd; - - Coordinate coord; - pbf geom(geom_pbf); - Geometry geometry(geom); - int32_t x, y; - while ((cmd = geometry.next(x, y)) != Geometry::end) { - if (cmd == Geometry::move_to) { - if (!line.empty()) { - placement.addFeature(*this, line, properties, face, shaping); - line.clear(); - } - } - line.emplace_back(x, y); - } - if (line.size()) { - placement.addFeature(*this, line, properties, face, shaping); - } -} - -void TextBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc, - const Tile::ID &id) { - painter.renderText(*this, layer_desc, id); -} - -bool TextBucket::hasData() const { - return !triangleGroups.empty(); -} - -void TextBucket::drawGlyphs(TextShader &shader) { - char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); - char *elements_index = - BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); - for (triangle_group_type &group : triangleGroups) { - group.array.bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); - glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index); - vertex_index += group.vertex_length * vertexBuffer.itemSize; - elements_index += group.elements_length * triangleElementsBuffer.itemSize; - } -} diff --git a/src/shader/icon.fragment.glsl b/src/shader/icon.fragment.glsl index 52c9e65aa5..6e8130ef1e 100644 --- a/src/shader/icon.fragment.glsl +++ b/src/shader/icon.fragment.glsl @@ -1,11 +1,10 @@ -uniform sampler2D u_image; -uniform vec2 u_dimension; -uniform vec4 u_color; -uniform float u_size; +uniform sampler2D u_texture; varying vec2 v_tex; +varying float v_alpha; void main() { - vec2 pos = (v_tex + (gl_PointCoord - 0.5) * u_size) / u_dimension; - gl_FragColor = u_color * texture2D(u_image, pos); + // Note: We don't need to premultiply here as the image we use is already + // premultiplied in the sprite atlas. + gl_FragColor = texture2D(u_texture, v_tex) * v_alpha; } diff --git a/src/shader/icon.vertex.glsl b/src/shader/icon.vertex.glsl index d0ffe4b33e..f8d94a2639 100644 --- a/src/shader/icon.vertex.glsl +++ b/src/shader/icon.vertex.glsl @@ -1,14 +1,73 @@ attribute vec2 a_pos; +attribute vec2 a_offset; attribute vec2 a_tex; +attribute float a_angle; +attribute float a_minzoom; +attribute float a_maxzoom; +attribute float a_rangeend; +attribute float a_rangestart; +attribute float a_labelminzoom; + +// posmatrix is for the vertex position, exmatrix is for rotating and projecting +// the extrusion vector. uniform mat4 u_matrix; -uniform float u_size; -uniform float u_ratio; +uniform mat4 u_exmatrix; +uniform float u_angle; +uniform float u_zoom; +uniform float u_flip; +uniform float u_fadedist; +uniform float u_minfadezoom; +uniform float u_maxfadezoom; +uniform float u_fadezoom; +uniform float u_opacity; + +uniform vec2 u_texsize; varying vec2 v_tex; +varying float v_alpha; void main() { - gl_Position = u_matrix * vec4(a_pos, 0, 1); - gl_PointSize = u_size; - v_tex = a_tex * u_ratio; + + float a_fadedist = 10.0; + float rev = 0.0; + + // u_angle is angle of the map, -128..128 representing 0..2PI + // a_angle is angle of the label, 0..256 representing 0..2PI, where 0 is horizontal text + float rotated = mod(a_angle + u_angle, 256.0); + // if the label rotates with the map, and if the rotated label is upside down, hide it + if (u_flip > 0.0 && rotated >= 64.0 && rotated < 192.0) rev = 1.0; + + // If the label should be invisible, we move the vertex outside + // of the view plane so that the triangle gets clipped. This makes it easier + // for us to create degenerate triangle strips. + // u_zoom is the current zoom level adjusted for the change in font size + float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom)) + rev; + + // fade out labels + float alpha = clamp((u_fadezoom - a_labelminzoom) / u_fadedist, 0.0, 1.0); + + if (u_fadedist >= 0.0) { + v_alpha = alpha; + } else { + v_alpha = 1.0 - alpha; + } + if (u_maxfadezoom < a_labelminzoom) { + v_alpha = 0.0; + } + if (u_minfadezoom >= a_labelminzoom) { + v_alpha = 1.0; + } + + // if label has been faded out, clip it + z += step(v_alpha, 0.0); + + // all the angles are 0..256 representing 0..2PI + // hide if (angle >= a_rangeend && angle < rangestart) + z += step(a_rangeend, u_angle) * (1.0 - step(a_rangestart, u_angle)); + + gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset / 64.0, z, 0); + v_tex = a_tex / u_texsize; + + v_alpha *= u_opacity; } diff --git a/src/shader/icon_shader.cpp b/src/shader/icon_shader.cpp index b3ac9272ba..6455d870a2 100644 --- a/src/shader/icon_shader.cpp +++ b/src/shader/icon_shader.cpp @@ -17,62 +17,138 @@ IconShader::IconShader() } a_pos = glGetAttribLocation(program, "a_pos"); + a_offset = glGetAttribLocation(program, "a_offset"); a_tex = glGetAttribLocation(program, "a_tex"); + a_angle = glGetAttribLocation(program, "a_angle"); + a_minzoom = glGetAttribLocation(program, "a_minzoom"); + a_maxzoom = glGetAttribLocation(program, "a_maxzoom"); + a_rangeend = glGetAttribLocation(program, "a_rangeend"); + a_rangestart = glGetAttribLocation(program, "a_rangestart"); + a_labelminzoom = glGetAttribLocation(program, "a_labelminzoom"); u_matrix = glGetUniformLocation(program, "u_matrix"); - u_color = glGetUniformLocation(program, "u_color"); - u_size = glGetUniformLocation(program, "u_size"); - u_ratio = glGetUniformLocation(program, "u_ratio"); - u_dimension = glGetUniformLocation(program, "u_dimension"); + u_exmatrix = glGetUniformLocation(program, "u_exmatrix"); + u_angle = glGetUniformLocation(program, "u_angle"); + u_zoom = glGetUniformLocation(program, "u_zoom"); + u_flip = glGetUniformLocation(program, "u_flip"); + u_fadedist = glGetUniformLocation(program, "u_fadedist"); + u_minfadezoom = glGetUniformLocation(program, "u_minfadezoom"); + u_maxfadezoom = glGetUniformLocation(program, "u_maxfadezoom"); + u_fadezoom = glGetUniformLocation(program, "u_fadezoom"); + u_opacity = glGetUniformLocation(program, "u_opacity"); + u_texsize = glGetUniformLocation(program, "u_texsize"); // fprintf(stderr, "IconShader:\n"); // fprintf(stderr, " - u_matrix: %d\n", u_matrix); - // fprintf(stderr, " - u_color: %d\n", u_color); - // fprintf(stderr, " - u_size: %d\n", u_size); - // fprintf(stderr, " - u_ratio: %d\n", u_ratio); - // fprintf(stderr, " - u_dimension: %d\n", u_dimension); - // fprintf(stderr, " - u_image: %d\n", u_image); + // fprintf(stderr, " - u_exmatrix: %d\n", u_exmatrix); + // fprintf(stderr, " - u_angle: %d\n", u_angle); + // fprintf(stderr, " - u_zoom: %d\n", u_zoom); + // fprintf(stderr, " - u_flip: %d\n", u_flip); + // fprintf(stderr, " - u_fadedist: %d\n", u_fadedist); + // fprintf(stderr, " - u_minfadezoom: %d\n", u_minfadezoom); + // fprintf(stderr, " - u_maxfadezoom: %d\n", u_maxfadezoom); + // fprintf(stderr, " - u_fadezoom: %d\n", u_fadezoom); + // fprintf(stderr, " - u_opacity: %d\n", u_opacity); } void IconShader::bind(char *offset) { + const int stride = 20; + glEnableVertexAttribArray(a_pos); - glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset); + glVertexAttribPointer(a_pos, 2, GL_SHORT, false, stride, offset + 0); + + glEnableVertexAttribArray(a_offset); + glVertexAttribPointer(a_offset, 2, GL_SHORT, false, stride, offset + 4); + + glEnableVertexAttribArray(a_labelminzoom); + glVertexAttribPointer(a_labelminzoom, 1, GL_UNSIGNED_BYTE, false, stride, offset + 8); + + glEnableVertexAttribArray(a_minzoom); + glVertexAttribPointer(a_minzoom, 1, GL_UNSIGNED_BYTE, false, stride, offset + 9); + + glEnableVertexAttribArray(a_maxzoom); + glVertexAttribPointer(a_maxzoom, 1, GL_UNSIGNED_BYTE, false, stride, offset + 10); + + glEnableVertexAttribArray(a_angle); + glVertexAttribPointer(a_angle, 1, GL_UNSIGNED_BYTE, false, stride, offset + 11); + + glEnableVertexAttribArray(a_rangeend); + glVertexAttribPointer(a_rangeend, 1, GL_UNSIGNED_BYTE, false, stride, offset + 12); + + glEnableVertexAttribArray(a_rangestart); + glVertexAttribPointer(a_rangestart, 1, GL_UNSIGNED_BYTE, false, stride, offset + 13); glEnableVertexAttribArray(a_tex); - glVertexAttribPointer(a_tex, 2, GL_UNSIGNED_SHORT, false, 8, offset + 4); + glVertexAttribPointer(a_tex, 2, GL_SHORT, false, stride, offset + 16); +} + +void IconShader::setExtrudeMatrix(const std::array<float, 16>& new_exmatrix) { + if (exmatrix != new_exmatrix) { + glUniformMatrix4fv(u_exmatrix, 1, GL_FALSE, new_exmatrix.data()); + exmatrix = new_exmatrix; + } +} + +void IconShader::setAngle(float new_angle) { + if (angle != new_angle) { + glUniform1f(u_angle, new_angle); + angle = new_angle; + } +} + +void IconShader::setZoom(float new_zoom) { + if (zoom != new_zoom) { + glUniform1f(u_zoom, new_zoom); + zoom = new_zoom; + } +} + +void IconShader::setFlip(float new_flip) { + if (flip != new_flip) { + glUniform1f(u_flip, new_flip); + flip = new_flip; + } +} + +void IconShader::setFadeDist(float new_fadedist) { + if (fadedist != new_fadedist) { + glUniform1f(u_fadedist, new_fadedist); + fadedist = new_fadedist; + } } -void IconShader::setImage(int32_t new_image) { - if (image != new_image) { - glUniform1i(u_image, new_image); - image = new_image; +void IconShader::setMinFadeZoom(float new_minfadezoom) { + if (minfadezoom != new_minfadezoom) { + glUniform1f(u_minfadezoom, new_minfadezoom); + minfadezoom = new_minfadezoom; } } -void IconShader::setColor(const std::array<float, 4>& new_color) { - if (color != new_color) { - glUniform4fv(u_color, 1, new_color.data()); - color = new_color; +void IconShader::setMaxFadeZoom(float new_maxfadezoom) { + if (maxfadezoom != new_maxfadezoom) { + glUniform1f(u_maxfadezoom, new_maxfadezoom); + maxfadezoom = new_maxfadezoom; } } -void IconShader::setSize(float new_size) { - if (size != new_size) { - glUniform1f(u_size, new_size); - size = new_size; +void IconShader::setFadeZoom(float new_fadezoom) { + if (fadezoom != new_fadezoom) { + glUniform1f(u_fadezoom, new_fadezoom); + fadezoom = new_fadezoom; } } -void IconShader::setRatio(float new_ratio) { - if (ratio != new_ratio) { - glUniform1f(u_ratio, new_ratio); - ratio = new_ratio; +void IconShader::setOpacity(float new_opacity) { + if (opacity != new_opacity) { + glUniform1f(u_opacity, new_opacity); + opacity = new_opacity; } } -void IconShader::setDimension(const std::array<float, 2>& new_dimension) { - if (dimension != new_dimension) { - glUniform2fv(u_dimension, 1, new_dimension.data()); - dimension = new_dimension; +void IconShader::setTextureSize(const std::array<float, 2> &new_texsize) { + if (texsize != new_texsize) { + glUniform2fv(u_texsize, 1, new_texsize.data()); + texsize = new_texsize; } } + diff --git a/src/shader/line.fragment.glsl b/src/shader/line.fragment.glsl index ca8fed9bb2..f4ac1458b3 100644 --- a/src/shader/line.fragment.glsl +++ b/src/shader/line.fragment.glsl @@ -1,5 +1,6 @@ uniform vec2 u_linewidth; uniform vec4 u_color; +uniform float u_blur; uniform vec2 u_dasharray; @@ -13,7 +14,7 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_linewidth.t) or when fading out // (v_linewidth.s) - float alpha = clamp(min(dist - (u_linewidth.t - 1.0), u_linewidth.s - dist), 0.0, 1.0); + float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0); // Calculate the antialiasing fade factor based on distance to the dash. // Only affects alpha when line is dashed @@ -21,4 +22,4 @@ void main() { alpha *= max(step(0.0, -u_dasharray.y), clamp(min(pos, u_dasharray.x - pos), 0.0, 1.0)); gl_FragColor = u_color * alpha; -}
\ No newline at end of file +} diff --git a/src/shader/line_shader.cpp b/src/shader/line_shader.cpp index 1330089896..529ab0d316 100644 --- a/src/shader/line_shader.cpp +++ b/src/shader/line_shader.cpp @@ -26,6 +26,7 @@ LineShader::LineShader() u_color = glGetUniformLocation(program, "u_color"); u_ratio = glGetUniformLocation(program, "u_ratio"); u_dasharray = glGetUniformLocation(program, "u_dasharray"); + u_blur = glGetUniformLocation(program, "u_blur"); // fprintf(stderr, "LineShader:\n"); // fprintf(stderr, " - u_matrix: %d\n", u_matrix); @@ -81,3 +82,10 @@ void LineShader::setDashArray(const std::array<float, 2>& new_dasharray) { dasharray = new_dasharray; } } + +void LineShader::setBlur(float new_blur) { + if (blur != new_blur) { + glUniform1f(u_blur, new_blur); + blur = new_blur; + } +} diff --git a/src/shader/text_shader.cpp b/src/shader/text_shader.cpp index c7cbbf560e..c9e70825ce 100644 --- a/src/shader/text_shader.cpp +++ b/src/shader/text_shader.cpp @@ -52,6 +52,7 @@ TextShader::TextShader() // fprintf(stderr, " - u_maxfadezoom: %d\n", u_maxfadezoom); // fprintf(stderr, " - u_fadezoom: %d\n", u_fadezoom); // fprintf(stderr, " - u_texsize: %d\n", u_texsize); + // fprintf(stderr, " - u_opacity: %d\n", u_opacity); } void TextShader::bind(char *offset) { diff --git a/src/style/property_fallback.cpp b/src/style/property_fallback.cpp index 949dc1a5cb..4401c8105a 100644 --- a/src/style/property_fallback.cpp +++ b/src/style/property_fallback.cpp @@ -1,48 +1,60 @@ #include <mbgl/style/property_fallback.hpp> +#include <mbgl/style/style_properties.hpp> namespace mbgl { const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = { - { PropertyKey::FillAntialias, true }, - { PropertyKey::FillOpacity, 1.0f }, - { PropertyKey::FillColor, Color({{ 0, 0, 0, 1 }}) }, - { PropertyKey::FillTranslateX, 0.0f }, - { PropertyKey::FillTranslateY, 0.0f }, - { PropertyKey::FillTranslateAnchor, TranslateAnchorType::Map }, - - { PropertyKey::LineOpacity, 1.0f }, - { PropertyKey::LineColor, Color({{ 0, 0, 0, 1 }}) }, - { PropertyKey::LineTranslateX, 0.0f }, - { PropertyKey::LineTranslateY, 0.0f }, - { PropertyKey::LineTranslateAnchor, TranslateAnchorType::Map }, - { PropertyKey::LineWidth, 1.0f }, - { PropertyKey::LineOffset, 0.0f }, - { PropertyKey::LineBlur, 1.0f }, - { PropertyKey::LineDashLand, 1.0f }, - { PropertyKey::LineDashGap, -1.0f }, - - { PropertyKey::IconOpacity, 1.0f }, - { PropertyKey::IconRotate, 0.0f }, - { PropertyKey::IconRotateAnchor, RotateAnchorType::Viewport }, - - { PropertyKey::TextOpacity, 1.0f }, - { PropertyKey::TextSize, 16.0f }, - { PropertyKey::TextColor, Color({{ 0, 0, 0, 1 }}) }, - { PropertyKey::TextHaloColor, Color({{ 0, 0, 0, 0 }}) }, - { PropertyKey::TextHaloWidth, 0.25f }, - { PropertyKey::TextHaloBlur, 1.0f }, - - { PropertyKey::CompositeOpacity, 1.0f }, - - { PropertyKey::RasterOpacity, 1.0f }, - { PropertyKey::RasterSpin, 0.0f }, - { PropertyKey::RasterBrightnessLow, 0.0f }, - { PropertyKey::RasterBrightnessHigh, 1.0f }, - { PropertyKey::RasterSaturation, 0.0f }, - { PropertyKey::RasterContrast, 0.0f }, - { PropertyKey::RasterFade, 0.0f }, - - { PropertyKey::BackgroundColor, Color({{ 0, 0, 0, 0 }}) }, + { PropertyKey::FillAntialias, defaultStyleProperties<FillProperties>().antialias }, + { PropertyKey::FillOpacity, defaultStyleProperties<FillProperties>().opacity }, + { PropertyKey::FillColor, defaultStyleProperties<FillProperties>().fill_color }, + // no FillOutlineColor on purpose. + { PropertyKey::FillTranslateX, defaultStyleProperties<FillProperties>().translate[0] }, + { PropertyKey::FillTranslateY, defaultStyleProperties<FillProperties>().translate[1] }, + { PropertyKey::FillTranslateAnchor, defaultStyleProperties<FillProperties>().translateAnchor }, + + { PropertyKey::LineOpacity, defaultStyleProperties<LineProperties>().opacity }, + { PropertyKey::LineColor, defaultStyleProperties<LineProperties>().color }, + { PropertyKey::LineTranslateX, defaultStyleProperties<LineProperties>().translate[0] }, + { PropertyKey::LineTranslateY, defaultStyleProperties<LineProperties>().translate[1] }, + { PropertyKey::LineTranslateAnchor, defaultStyleProperties<LineProperties>().translateAnchor }, + { PropertyKey::LineWidth, defaultStyleProperties<LineProperties>().width }, + { PropertyKey::LineOffset, defaultStyleProperties<LineProperties>().offset }, + { PropertyKey::LineBlur, defaultStyleProperties<LineProperties>().blur }, + { PropertyKey::LineDashLand, defaultStyleProperties<LineProperties>().dash_array[0] }, + { PropertyKey::LineDashGap, defaultStyleProperties<LineProperties>().dash_array[1] }, + + { PropertyKey::IconOpacity, defaultStyleProperties<SymbolProperties>().icon.opacity }, + { PropertyKey::IconRotate, defaultStyleProperties<SymbolProperties>().icon.rotate }, + { PropertyKey::IconSize, defaultStyleProperties<SymbolProperties>().icon.size }, + { PropertyKey::IconColor, defaultStyleProperties<SymbolProperties>().icon.color }, + { PropertyKey::IconHaloColor, defaultStyleProperties<SymbolProperties>().icon.halo_color }, + { PropertyKey::IconHaloWidth, defaultStyleProperties<SymbolProperties>().icon.halo_width }, + { PropertyKey::IconHaloBlur, defaultStyleProperties<SymbolProperties>().icon.halo_blur }, + { PropertyKey::IconTranslateX, defaultStyleProperties<SymbolProperties>().icon.translate[0] }, + { PropertyKey::IconTranslateY, defaultStyleProperties<SymbolProperties>().icon.translate[1] }, + { PropertyKey::IconTranslateAnchor, defaultStyleProperties<SymbolProperties>().icon.translate_anchor }, + + { PropertyKey::TextOpacity, defaultStyleProperties<SymbolProperties>().text.opacity }, + { PropertyKey::TextSize, defaultStyleProperties<SymbolProperties>().text.size }, + { PropertyKey::TextColor, defaultStyleProperties<SymbolProperties>().text.color }, + { PropertyKey::TextHaloColor, defaultStyleProperties<SymbolProperties>().text.halo_color }, + { PropertyKey::TextHaloWidth, defaultStyleProperties<SymbolProperties>().text.halo_width }, + { PropertyKey::TextHaloBlur, defaultStyleProperties<SymbolProperties>().text.halo_blur }, + { PropertyKey::TextTranslateX, defaultStyleProperties<SymbolProperties>().text.translate[0] }, + { PropertyKey::TextTranslateY, defaultStyleProperties<SymbolProperties>().text.translate[1] }, + { PropertyKey::TextTranslateAnchor, defaultStyleProperties<SymbolProperties>().text.translate_anchor }, + + { PropertyKey::CompositeOpacity, defaultStyleProperties<CompositeProperties>().opacity }, + + { PropertyKey::RasterOpacity, defaultStyleProperties<RasterProperties>().opacity }, + { PropertyKey::RasterSpin, defaultStyleProperties<RasterProperties>().spin }, + { PropertyKey::RasterBrightnessLow, defaultStyleProperties<RasterProperties>().brightness[0] }, + { PropertyKey::RasterBrightnessHigh, defaultStyleProperties<RasterProperties>().brightness[1] }, + { PropertyKey::RasterSaturation, defaultStyleProperties<RasterProperties>().saturation }, + { PropertyKey::RasterContrast, defaultStyleProperties<RasterProperties>().contrast }, + { PropertyKey::RasterFade, defaultStyleProperties<RasterProperties>().fade }, + + { PropertyKey::BackgroundColor, defaultStyleProperties<BackgroundProperties>().color }, }; const PropertyValue PropertyFallbackValue::defaultProperty = false; diff --git a/src/style/style.cpp b/src/style/style.cpp index f867616970..03881f96a0 100644 --- a/src/style/style.cpp +++ b/src/style/style.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/style.hpp> +#include <mbgl/map/sprite.hpp> #include <mbgl/style/style_layer_group.hpp> #include <mbgl/style/style_parser.hpp> #include <mbgl/style/style_bucket.hpp> @@ -29,6 +30,10 @@ void Style::updateProperties(float z, timestamp now) { } } +const std::string &Style::getSpriteURL() const { + return sprite_url; +} + void Style::setDefaultTransitionDuration(uint16_t duration_milliseconds) { defaultTransition.duration = duration_milliseconds; } @@ -93,7 +98,7 @@ void Style::loadJSON(const uint8_t *const data) { const BackgroundProperties &Style::getBackgroundProperties() const { if (layers && layers->layers.size()) { const auto first = layers->layers.front(); - if (first && first->id == "background") { + if (first && first->type == StyleLayerType::Background) { return first->getProperties<BackgroundProperties>(); } } diff --git a/src/style/style_bucket.cpp b/src/style/style_bucket.cpp index afd4bc09f7..9a40c2386b 100644 --- a/src/style/style_bucket.cpp +++ b/src/style/style_bucket.cpp @@ -6,8 +6,7 @@ StyleBucket::StyleBucket(StyleLayerType type) { switch (type) { case StyleLayerType::Fill: render = StyleBucketFill{}; break; case StyleLayerType::Line: render = StyleBucketLine{}; break; - case StyleLayerType::Icon: render = StyleBucketIcon{}; break; - case StyleLayerType::Text: render = StyleBucketText{}; break; + case StyleLayerType::Symbol: render = StyleBucketSymbol{}; break; case StyleLayerType::Raster: render = StyleBucketRaster{}; break; default: break; } diff --git a/src/style/style_layer.cpp b/src/style/style_layer.cpp index 69c5a8c8ca..1672ad4a8b 100644 --- a/src/style/style_layer.cpp +++ b/src/style/style_layer.cpp @@ -10,7 +10,7 @@ StyleLayer::StyleLayer(const std::string &id, std::map<ClassID, ClassProperties> : id(id), styles(std::move(styles)), rasterize(std::move(rasterize)) {} bool StyleLayer::isBackground() const { - return type == StyleLayerType::Background && id == "background"; + return type == StyleLayerType::Background; } void StyleLayer::setClasses(const std::vector<std::string> &class_names, const timestamp now, @@ -191,24 +191,30 @@ void StyleLayer::applyStyleProperties<LineProperties>(const float z, const times } template <> -void StyleLayer::applyStyleProperties<IconProperties>(const float z, const timestamp now) { - properties.set<IconProperties>(); - IconProperties &icon = properties.get<IconProperties>(); - applyStyleProperty(PropertyKey::IconOpacity, icon.opacity, z, now); - applyStyleProperty(PropertyKey::IconRotate, icon.rotate, z, now); - applyStyleProperty(PropertyKey::IconRotateAnchor, icon.rotate_anchor, z, now); -} +void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const timestamp now) { + properties.set<SymbolProperties>(); + SymbolProperties &symbol = properties.get<SymbolProperties>(); + applyStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now); + applyStyleProperty(PropertyKey::IconRotate, symbol.icon.rotate, z, now); + applyStyleProperty(PropertyKey::IconSize, symbol.icon.size, z, now); + applyStyleProperty(PropertyKey::IconColor, symbol.icon.color, z, now); + applyStyleProperty(PropertyKey::IconHaloColor, symbol.icon.halo_color, z, now); + applyStyleProperty(PropertyKey::IconHaloWidth, symbol.icon.halo_width, z, now); + applyStyleProperty(PropertyKey::IconHaloBlur, symbol.icon.halo_blur, z, now); + applyStyleProperty(PropertyKey::IconTranslateX, symbol.icon.translate[0], z, now); + applyStyleProperty(PropertyKey::IconTranslateY, symbol.icon.translate[1], z, now); + applyStyleProperty(PropertyKey::IconTranslateAnchor, symbol.icon.translate_anchor, z, now); + + applyStyleProperty(PropertyKey::TextOpacity, symbol.text.opacity, z, now); + applyStyleProperty(PropertyKey::TextSize, symbol.text.size, z, now); + applyStyleProperty(PropertyKey::TextColor, symbol.text.color, z, now); + applyStyleProperty(PropertyKey::TextHaloColor, symbol.text.halo_color, z, now); + applyStyleProperty(PropertyKey::TextHaloWidth, symbol.text.halo_width, z, now); + applyStyleProperty(PropertyKey::TextHaloBlur, symbol.text.halo_blur, z, now); + applyStyleProperty(PropertyKey::TextTranslateX, symbol.text.translate[0], z, now); + applyStyleProperty(PropertyKey::TextTranslateY, symbol.text.translate[1], z, now); + applyStyleProperty(PropertyKey::TextTranslateAnchor, symbol.text.translate_anchor, z, now); -template <> -void StyleLayer::applyStyleProperties<TextProperties>(const float z, const timestamp now) { - properties.set<TextProperties>(); - TextProperties &text = properties.get<TextProperties>(); - applyStyleProperty(PropertyKey::TextOpacity, text.opacity, z, now); - applyStyleProperty(PropertyKey::TextSize, text.size, z, now); - applyStyleProperty(PropertyKey::TextColor, text.color, z, now); - applyStyleProperty(PropertyKey::TextHaloColor, text.halo_color, z, now); - applyStyleProperty(PropertyKey::TextHaloWidth, text.halo_width, z, now); - applyStyleProperty(PropertyKey::TextHaloBlur, text.halo_blur, z, now); } template <> @@ -248,8 +254,7 @@ void StyleLayer::updateProperties(float z, const timestamp now) { switch (type) { case StyleLayerType::Fill: applyStyleProperties<FillProperties>(z, now); break; case StyleLayerType::Line: applyStyleProperties<LineProperties>(z, now); break; - case StyleLayerType::Icon: applyStyleProperties<IconProperties>(z, now); break; - case StyleLayerType::Text: applyStyleProperties<TextProperties>(z, now); break; + case StyleLayerType::Symbol: applyStyleProperties<SymbolProperties>(z, now); break; case StyleLayerType::Raster: applyStyleProperties<RasterProperties>(z, now); break; case StyleLayerType::Composite: applyStyleProperties<CompositeProperties>(z, now); break; case StyleLayerType::Background: applyStyleProperties<BackgroundProperties>(z, now); break; diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp index e5aa3acc15..bc3878144f 100644 --- a/src/style/style_parser.cpp +++ b/src/style/style_parser.cpp @@ -63,6 +63,20 @@ JSVal StyleParser::replaceConstant(JSVal value) { #pragma mark - Parse Render Properties +template<> bool StyleParser::parseRenderProperty(JSVal value, bool &target, const char *name) { + if (value.HasMember(name)) { + JSVal property = replaceConstant(value[name]); + if (property.IsBool()) { + target = property.GetBool(); + return true; + } else { + fprintf(stderr, "[WARNING] '%s' must be a boolean\n", name); + } + } + return false; +} + + template<> bool StyleParser::parseRenderProperty(JSVal value, std::string &target, const char *name) { if (value.HasMember(name)) { JSVal property = replaceConstant(value[name]); @@ -139,12 +153,12 @@ template<> bool StyleParser::parseRenderProperty(JSVal value, vec2<float> &targe return false; } -template<typename T, typename Parser> -bool StyleParser::parseRenderProperty(JSVal value, T &target, const char *name, Parser &parser) { +template<typename Parser, typename T> +bool StyleParser::parseRenderProperty(JSVal value, T &target, const char *name) { if (value.HasMember(name)) { JSVal property = replaceConstant(value[name]); if (property.IsString()) { - target = parser({ property.GetString(), property.GetStringLength() }); + target = Parser({ property.GetString(), property.GetStringLength() }); return true; } else { Log::Warning(Event::ParseStyle, "%s must have one of the enum values", name); @@ -167,7 +181,7 @@ void StyleParser::parseSources(JSVal value) { int32_t min_zoom = 0; int32_t max_zoom = 22; - parseRenderProperty(itr->value, type, "type", parseSourceType); + parseRenderProperty<SourceTypeClass>(itr->value, type, "type"); parseRenderProperty(itr->value, url, "url"); if (type == SourceType::Raster) { parseRenderProperty(itr->value, tile_size, "tileSize"); @@ -175,7 +189,7 @@ void StyleParser::parseSources(JSVal value) { parseRenderProperty(itr->value, min_zoom, "minZoom"); parseRenderProperty(itr->value, max_zoom, "maxZoom"); - sources.emplace(std::move(name), std::make_shared<StyleSource>(type, url, tile_size, min_zoom, max_zoom)); + sources.emplace(std::move(name), std::make_shared<StyleSource>(SourceInfo { type, url, tile_size, min_zoom, max_zoom })); } } else { Log::Warning(Event::ParseStyle, "sources must be an object"); @@ -348,22 +362,22 @@ template<> std::tuple<bool, std::string> StyleParser::parseProperty(JSVal value, template<> std::tuple<bool, TranslateAnchorType> StyleParser::parseProperty(JSVal value, const char *property_name) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return std::tuple<bool, TranslateAnchorType> { false, TranslateAnchorType::Default }; + return std::tuple<bool, TranslateAnchorType> { false, TranslateAnchorType::Map }; } - return std::tuple<bool, TranslateAnchorType> { true, parseTranslateAnchorType({ value.GetString(), value.GetStringLength() }) }; + return std::tuple<bool, TranslateAnchorType> { true, TranslateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; } template<> std::tuple<bool, RotateAnchorType> StyleParser::parseProperty<RotateAnchorType>(JSVal value, const char *property_name) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return std::tuple<bool, RotateAnchorType> { false, RotateAnchorType::Default }; + return std::tuple<bool, RotateAnchorType> { false, RotateAnchorType::Map }; } - return std::tuple<bool, RotateAnchorType> { true, parseRotateAnchorType({ value.GetString(), value.GetStringLength() }) }; + return std::tuple<bool, RotateAnchorType> { true, RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; } -template<> std::tuple<bool, PropertyTransition> StyleParser::parseProperty(JSVal value, const char *property_name) { +template<> std::tuple<bool, PropertyTransition> StyleParser::parseProperty(JSVal value, const char */*property_name*/) { PropertyTransition transition; if (value.IsObject()) { if (value.HasMember("duration") && value["duration"].IsNumber()) { @@ -592,7 +606,18 @@ void StyleParser::parseStyle(JSVal value, ClassProperties &klass) { parseOptionalProperty<Function<float>>("icon-opacity", Key::IconOpacity, klass, value); parseOptionalProperty<PropertyTransition>("transition-icon-opacity", Key::IconOpacity, klass, value); parseOptionalProperty<Function<float>>("icon-rotate", Key::IconRotate, klass, value); - parseOptionalProperty<RotateAnchorType>("icon-rotate-anchor", Key::IconRotateAnchor, klass, value); + parseOptionalProperty<Function<float>>("icon-size", Key::IconSize, klass, value); + parseOptionalProperty<PropertyTransition>("transition-icon-size", Key::IconSize, klass, value); + parseOptionalProperty<Function<Color>>("icon-color", Key::IconColor, klass, value); + parseOptionalProperty<PropertyTransition>("transition-icon-color", Key::IconColor, klass, value); + parseOptionalProperty<Function<Color>>("icon-halo-color", Key::IconHaloColor, klass, value); + parseOptionalProperty<PropertyTransition>("transition-icon-halo-color", Key::IconHaloColor, klass, value); + parseOptionalProperty<Function<float>>("icon-halo-width", Key::IconHaloWidth, klass, value); + parseOptionalProperty<PropertyTransition>("transition-icon-halo-width", Key::IconHaloWidth, klass, value); + parseOptionalProperty<Function<float>>("icon-halo-blur", Key::IconHaloBlur, klass, value); + parseOptionalProperty<PropertyTransition>("transition-icon-halo-blur", Key::IconHaloBlur, klass, value); + parseOptionalProperty<Function<float>>("icon-translate", { Key::IconTranslateX, Key::IconTranslateY }, klass, value); + parseOptionalProperty<PropertyTransition>("transition-icon-translate", Key::IconTranslate, klass, value); parseOptionalProperty<Function<float>>("text-opacity", Key::TextOpacity, klass, value); parseOptionalProperty<PropertyTransition>("transition-text-opacity", Key::TextOpacity, klass, value); @@ -606,6 +631,8 @@ void StyleParser::parseStyle(JSVal value, ClassProperties &klass) { parseOptionalProperty<PropertyTransition>("transition-text-halo-width", Key::TextHaloWidth, klass, value); parseOptionalProperty<Function<float>>("text-halo-blur", Key::TextHaloBlur, klass, value); parseOptionalProperty<PropertyTransition>("transition-text-halo-blur", Key::TextHaloBlur, klass, value); + parseOptionalProperty<Function<float>>("text-translate", { Key::TextTranslateX, Key::TextTranslateY }, klass, value); + parseOptionalProperty<PropertyTransition>("transition-text-translate", Key::TextTranslate, klass, value); parseOptionalProperty<Function<float>>("composite-opacity", Key::CompositeOpacity, klass, value); parseOptionalProperty<PropertyTransition>("transition-composite-opacity", Key::CompositeOpacity, klass, value); @@ -714,6 +741,24 @@ void StyleParser::parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer) { JSVal value_render = replaceConstant(value["render"]); parseRender(value_render, layer); } + + if (value.HasMember("min-zoom")) { + JSVal min_zoom = value["min-zoom"]; + if (min_zoom.IsNumber()) { + layer->bucket->min_zoom = min_zoom.GetDouble(); + } else { + Log::Warning(Event::ParseStyle, "min-zoom of layer %s must be numeric", layer->id.c_str()); + } + } + + if (value.HasMember("max-zoom")) { + JSVal max_zoom = value["max-zoom"]; + if (max_zoom.IsNumber()) { + layer->bucket->min_zoom = max_zoom.GetDouble(); + } else { + Log::Warning(Event::ParseStyle, "max-zoom of layer %s must be numeric", layer->id.c_str()); + } + } } FilterExpression StyleParser::parseFilter(JSVal value) { @@ -815,61 +860,70 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer) { case StyleLayerType::Fill: { StyleBucketFill &render = bucket.render.get<StyleBucketFill>(); - parseRenderProperty(value, render.winding, "fill-winding", parseWindingType); + parseRenderProperty<WindingTypeClass>(value, render.winding, "fill-winding"); } break; case StyleLayerType::Line: { StyleBucketLine &render = bucket.render.get<StyleBucketLine>(); - parseRenderProperty(value, render.cap, "line-cap", parseCapType); - parseRenderProperty(value, render.join, "line-join", parseJoinType); + parseRenderProperty<CapTypeClass>(value, render.cap, "line-cap"); + parseRenderProperty<JoinTypeClass>(value, render.join, "line-join"); parseRenderProperty(value, render.miter_limit, "line-miter-limit"); parseRenderProperty(value, render.round_limit, "line-round-limit"); } break; - case StyleLayerType::Icon: { - StyleBucketIcon &render = bucket.render.get<StyleBucketIcon>(); - - parseRenderProperty(value, render.size, "icon-size"); - parseRenderProperty(value, render.icon, "icon-image"); - parseRenderProperty(value, render.spacing, "icon-spacing"); - parseRenderProperty(value, render.padding, "icon-padding"); - if (parseRenderProperty(value, render.translate, "icon-translate")) { - render.translate.x *= 24; - render.translate.y *= -24; - } - parseRenderProperty(value, render.translate_anchor, "icon-translate-anchor", parseTranslateAnchorType); - } break; - - case StyleLayerType::Text: { - StyleBucketText &render = bucket.render.get<StyleBucketText>(); - - parseRenderProperty(value, render.field, "text-field"); - parseRenderProperty(value, render.path, "text-path", parseTextPathType); - parseRenderProperty(value, render.transform, "text-transform", parseTextTransformType); - parseRenderProperty(value, render.font, "text-font"); - parseRenderProperty(value, render.max_size, "text-max-size"); - if (parseRenderProperty(value, render.max_width, "text-max-width")) { - render.max_width *= 24; // em - } - if (parseRenderProperty(value, render.line_height, "text-line-height")) { - render.line_height *= 24; // em - } - if (parseRenderProperty(value, render.letter_spacing, "text-letter-spacing")) { - render.letter_spacing *= 24; // em - } - parseRenderProperty(value, render.alignment, "text-alignment", parseAlignmentType); - parseRenderProperty(value, render.vertical_alignment, "text-vertical-alignment", parseVerticalAlignmentType); - parseRenderProperty(value, render.max_angle_delta, "text-max-angle"); - parseRenderProperty(value, render.min_distance, "text-min-distance"); - parseRenderProperty(value, render.rotate, "text-rotate"); - parseRenderProperty(value, render.slant, "text-slant"); - parseRenderProperty(value, render.padding, "text-padding"); - if (parseRenderProperty(value, render.translate, "text-translate")) { - render.translate.x *= 24; - render.translate.y *= -24; - } - parseRenderProperty(value, render.translate_anchor, "text-translate-anchor", parseTranslateAnchorType); + case StyleLayerType::Symbol: { + StyleBucketSymbol &render = bucket.render.get<StyleBucketSymbol>(); + + parseRenderProperty<PlacementTypeClass>(value, render.placement, "symbol-placement"); + if (render.placement == PlacementType::Line) { + // Change the default value in case of line placement. + render.text.rotation_alignment = RotationAlignmentType::Map; + render.icon.rotation_alignment = RotationAlignmentType::Map; + } + + parseRenderProperty(value, render.min_distance, "symbol-min-distance"); + + parseRenderProperty(value, render.icon.allow_overlap, "icon-allow-overlap"); + parseRenderProperty(value, render.icon.ignore_placement, "icon-ignore-placement"); + parseRenderProperty(value, render.icon.optional, "icon-optional"); + parseRenderProperty<RotationAlignmentTypeClass>(value, render.icon.rotation_alignment, "icon-rotation-alignment"); + parseRenderProperty(value, render.icon.max_size, "icon-max-size"); + parseRenderProperty(value, render.icon.image, "icon-image"); + parseRenderProperty(value, render.icon.rotate, "icon-rotate"); + parseRenderProperty(value, render.icon.padding, "icon-padding"); + parseRenderProperty(value, render.icon.keep_upright, "icon-keep-upright"); + parseRenderProperty(value, render.icon.offset, "icon-offset"); + parseRenderProperty<TranslateAnchorTypeClass>(value, render.icon.translate_anchor, "icon-translate-anchor"); + + + parseRenderProperty<RotationAlignmentTypeClass>(value, render.text.rotation_alignment, "text-rotation-alignment"); + parseRenderProperty(value, render.text.field, "text-field"); + parseRenderProperty(value, render.text.font, "text-font"); + parseRenderProperty(value, render.text.max_size, "text-max-size"); + if (parseRenderProperty(value, render.text.max_width, "text-max-width")) { + render.text.max_width *= 24; // em + } + if (parseRenderProperty(value, render.text.line_height, "text-line-height")) { + render.text.line_height *= 24; // em + } + if (parseRenderProperty(value, render.text.letter_spacing, "text-letter-spacing")) { + render.text.letter_spacing *= 24; // em + } + parseRenderProperty<TextJustifyTypeClass>(value, render.text.justify, "text-justify"); + parseRenderProperty<TextHorizontalAlignTypeClass>(value, render.text.horizontal_align, "text-horizontal-align"); + parseRenderProperty<TextVerticalAlignTypeClass>(value, render.text.vertical_align, "text-vertical-align"); + parseRenderProperty(value, render.text.max_angle, "text-max-angle"); + parseRenderProperty(value, render.text.rotate, "text-rotate"); + parseRenderProperty(value, render.text.slant, "text-slant"); + parseRenderProperty(value, render.text.padding, "text-padding"); + parseRenderProperty(value, render.text.keep_upright, "text-keep-upright"); + parseRenderProperty<TextTransformTypeClass>(value, render.text.transform, "text-transform"); + parseRenderProperty(value, render.text.offset, "text-offset"); + parseRenderProperty<TranslateAnchorTypeClass>(value, render.text.translate_anchor, "text-translate-anchor"); + parseRenderProperty(value, render.text.allow_overlap, "text-allow-overlap"); + parseRenderProperty(value, render.text.ignore_placement, "text-ignore-placement"); + parseRenderProperty(value, render.text.optional, "text-optional"); } break; default: // There are no render properties for these layer types. diff --git a/src/style/style_properties.cpp b/src/style/style_properties.cpp index 3d6bc41b81..1b05528d2f 100644 --- a/src/style/style_properties.cpp +++ b/src/style/style_properties.cpp @@ -4,8 +4,7 @@ namespace mbgl { template<> const FillProperties &defaultStyleProperties() { static const FillProperties p; return p; } template<> const LineProperties &defaultStyleProperties() { static const LineProperties p; return p; } -template<> const IconProperties &defaultStyleProperties() { static const IconProperties p; return p; } -template<> const TextProperties &defaultStyleProperties() { static const TextProperties p; return p; } +template<> const SymbolProperties &defaultStyleProperties() { static const SymbolProperties p; return p; } template<> const CompositeProperties &defaultStyleProperties() { static const CompositeProperties p; return p; } template<> const RasterProperties &defaultStyleProperties() { static const RasterProperties p; return p; } template<> const BackgroundProperties &defaultStyleProperties() { static const BackgroundProperties p; return p; } diff --git a/src/text/collision.cpp b/src/text/collision.cpp index 54b47e438b..4621423569 100644 --- a/src/text/collision.cpp +++ b/src/text/collision.cpp @@ -31,37 +31,59 @@ Collision::~Collision() { delete ((Tree *)hTree); } -Collision::Collision() : cTree(new Tree()), hTree(new Tree()) { +Collision::Collision(float zoom, float tileExtent, float tileSize, float placementDepth) + : hTree(new Tree()), // tree for horizontal labels + cTree(new Tree()), // tree for glyphs from curved labels + + // tile pixels per screen pixels at the tile's zoom level + tilePixelRatio(tileExtent / tileSize), + + zoom(zoom), + + // Calculate the maximum scale we can go down in our fake-3d rtree so that + // placement still makes sense. This is calculated so that the minimum + // placement zoom can be at most 25.5 (we use an unsigned integer x10 to + // store the minimum zoom). + // + // We don't want to place labels all the way to 25.5. This lets too many + // glyphs be placed, slowing down collision checking. Only place labels if + // they will show up within the intended zoom range of the tile. + maxPlacementScale(std::exp(std::log(2) * util::min(3.0f, placementDepth, 25.5f - zoom))) { const float m = 4096; + const float edge = m * tilePixelRatio * 2; // Hack to prevent cross-tile labels insert( - {{GlyphBox{ - CollisionRect{CollisionPoint{0, 0}, CollisionPoint{0, m * 8}}, - CollisionRect{CollisionPoint{0, 0}, CollisionPoint{0, m * 8}}, 0}, - GlyphBox{ - CollisionRect{CollisionPoint{0, 0}, CollisionPoint{m * 8, 0}}, - CollisionRect{CollisionPoint{0, 0}, CollisionPoint{m * 8, 0}}, - 0}}}, - CollisionAnchor(0, 0), 1, {{M_PI * 2, 0}}, false, 2); - - insert({{GlyphBox{ - CollisionRect{CollisionPoint{-m * 8, 0}, CollisionPoint{0, 0}}, - CollisionRect{CollisionPoint{-m * 8, 0}, CollisionPoint{0, 0}}, - 0}, - GlyphBox{ - CollisionRect{CollisionPoint{0, -m * 8}, CollisionPoint{0, 0}}, - CollisionRect{CollisionPoint{0, -m * 8}, CollisionPoint{0, 0}}, - 0}}}, - CollisionAnchor{m, m}, 1, {{M_PI * 2, 0}}, false, 2); + /* glyphs */ { + GlyphBox{/* box */ CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{0, edge}}, + /* minScale */ 0, + /* padding */ 2}, + GlyphBox{/* box */ CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{edge, 0}}, + /* minScale */ 0, + /* padding */ 2}}, + /* anchor */ CollisionAnchor(0, 0), + /* placementScale */ 1, + /* placementRange */ {{M_PI * 2, 0}}, + /* horizontal */ false); + + insert( + /* glyphs */ { + GlyphBox{/* box*/ CollisionRect{CollisionPoint{-edge, 0}, CollisionPoint{edge, edge}}, + /* minScale */ 0, + /* padding */ 2}, + GlyphBox{/* box */ CollisionRect{CollisionPoint{0, -edge}, CollisionPoint{edge, edge}}, + /* minScale */ 0, + /* padding */ 2}}, + /* anchor */ CollisionAnchor(m, m), + /* placementScale */ 1, + /* placementRange */ {{M_PI * 2, 0}}, + /* horizontal */ false); } -GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, bool horizontal, - const CollisionAnchor &anchor) { +GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, const CollisionAnchor &anchor) { GlyphBox mergedGlyphs; const float inf = std::numeric_limits<float>::infinity(); mergedGlyphs.box = CollisionRect{{inf, inf}, {-inf, -inf}}; - mergedGlyphs.rotate = horizontal; mergedGlyphs.anchor = anchor; CollisionRect &box = mergedGlyphs.box; @@ -71,105 +93,51 @@ GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, bool horizontal, box.tl.y = util::min(box.tl.y, gbox.tl.y); box.br.x = util::max(box.br.x, gbox.br.x); box.br.y = util::max(box.br.y, gbox.br.y); - mergedGlyphs.minScale = - util::max(mergedGlyphs.minScale, glyph.minScale); + mergedGlyphs.minScale = util::max(mergedGlyphs.minScale, glyph.minScale); } return mergedGlyphs; } -PlacementProperty Collision::place(const GlyphBoxes &boxes, - const CollisionAnchor &anchor, - float minPlacementScale, - float maxPlacementScale, float padding, - bool horizontal, bool alwaysVisible) { - - float minScale = std::numeric_limits<float>::infinity(); - for (const GlyphBox &glyphBox : boxes) { - minScale = util::min(minScale, glyphBox.minScale); - } - minPlacementScale = util::max(minPlacementScale, minScale); - - // Collision checks between rotating and fixed labels are - // relatively expensive, so we use one box per label, not per glyph - // for horizontal labels. - GlyphBoxes glyphs; - if (horizontal) { - glyphs.push_back(getMergedGlyphs(boxes, horizontal, anchor)); - } else { - glyphs = boxes; - } - - // Calculate bboxes for all the glyphs - for (GlyphBox &glyph : glyphs) { - if (horizontal) { - const CollisionRect &box = glyph.box; - float x12 = box.tl.x * box.tl.x, y12 = box.tl.y * box.tl.y, - x22 = box.br.x * box.br.x, y22 = box.br.y * box.br.y, - diag = std::sqrt( - util::max(x12 + y12, x12 + y22, x22 + y12, x22 + y22)); - - glyph.bbox = CollisionRect{{-diag, -diag}, {diag, diag}}; - } else { - glyph.bbox = glyph.box; - } - } - - // Calculate the minimum scale the entire label can be shown without - // collisions - float scale = alwaysVisible ? minPlacementScale - : getPlacementScale(glyphs, minPlacementScale, - maxPlacementScale, padding); - - // Return if the label can never be placed without collision - if (scale < 0 || scale == std::numeric_limits<float>::infinity()) { - return PlacementProperty{}; - } - - // Calculate the range it is safe to rotate all glyphs - PlacementRange rotationRange = getPlacementRange(glyphs, scale, horizontal); - insert(glyphs, anchor, scale, rotationRange, horizontal, padding); - - float zoom = log(scale) / log(2); - - return PlacementProperty{zoom, rotationRange}; -} +Box getBox(const CollisionAnchor &anchor, const CollisionRect &bbox, float minScale, + float maxScale) { + return Box{ + Point{ + anchor.x + util::min(bbox.tl.x / minScale, bbox.tl.x / maxScale), + anchor.y + util::min(bbox.tl.y / minScale, bbox.tl.y / maxScale), + }, + Point{ + anchor.x + util::max(bbox.br.x / minScale, bbox.br.x / maxScale), + anchor.y + util::max(bbox.br.y / minScale, bbox.br.y / maxScale), + }, + }; +}; -float Collision::getPlacementScale(const GlyphBoxes &glyphs, - float minPlacementScale, - float maxPlacementScale, float pad) { +float Collision::getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale) { for (const GlyphBox &glyph : glyphs) { - const CollisionRect &bbox = glyph.bbox; const CollisionRect &box = glyph.box; + const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box; const CollisionAnchor &anchor = glyph.anchor; + const float pad = glyph.padding; - if (anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 || - anchor.y > 4096) { - return -1; + if (anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 || anchor.y > 4096) { + return 0; } float minScale = std::fmax(minPlacementScale, glyph.minScale); - float maxScale = glyph.maxScale; + float maxScale = glyph.maxScale != 0 ? glyph.maxScale : std::numeric_limits<float>::infinity(); if (minScale >= maxScale) { continue; } // Compute the scaled bounding box of the unrotated glyph - float minPlacedX = anchor.x + bbox.tl.x / minScale; - float minPlacedY = anchor.y + bbox.tl.y / minScale; - float maxPlacedX = anchor.x + bbox.br.x / minScale; - float maxPlacedY = anchor.y + bbox.br.y / minScale; - - Box query_box{Point{minPlacedX, minPlacedY}, - Point{maxPlacedX, maxPlacedY}}; + const Box searchBox = getBox(anchor, bbox, minScale, maxScale); std::vector<PlacementValue> blocking; - ((Tree *)cTree) - ->query(bgi::intersects(query_box), std::back_inserter(blocking)); - ((Tree *)hTree) - ->query(bgi::intersects(query_box), std::back_inserter(blocking)); + ((Tree *)hTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking)); + ((Tree *)cTree)->query(bgi::intersects(searchBox), std::back_inserter(blocking)); if (blocking.size()) { const CollisionAnchor &na = anchor; // new anchor @@ -184,7 +152,7 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs, // NOTE: this isn't right because there can be glyphs with // the same anchor but differing box offsets. if (na == oa) { - return -1; + return 0; } // todo: unhardcode the 8 = tileExtent/tileSize @@ -192,17 +160,13 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs, // Original algorithm: float s1 = (ob.tl.x - nb.br.x - padding) / - (na.x - oa.x); // scale at which new box is to the - // left of old box + (na.x - oa.x); // scale at which new box is to the left of old box float s2 = (ob.br.x - nb.tl.x + padding) / - (na.x - oa.x); // scale at which new box is to the - // right of old box + (na.x - oa.x); // scale at which new box is to the right of old box float s3 = (ob.tl.y - nb.br.y - padding) / - (na.y - oa.y); // scale at which new box is to the - // top of old box + (na.y - oa.y); // scale at which new box is to the top of old box float s4 = (ob.br.y - nb.tl.y + padding) / - (na.y - oa.y); // scale at which new box is to the - // bottom of old box + (na.y - oa.y); // scale at which new box is to the bottom of old box if (isnan(s1) || isnan(s2)) { s1 = s2 = 1; @@ -211,7 +175,7 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs, s3 = s4 = 1; } - float collisionFreeScale = std::fmin(std::fmax(s1, s2), std::fmax(s3, s4)); + const float collisionFreeScale = std::fmin(std::fmax(s1, s2), std::fmax(s3, s4)); // Only update label's min scale if the glyph was // restricted by a collision @@ -223,7 +187,7 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs, } if (minPlacementScale > maxPlacementScale) { - return -1; + return 0; } } } @@ -232,13 +196,12 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs, return minPlacementScale; } -PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs, - float placementScale, +PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs, float placementScale, bool horizontal) { PlacementRange placementRange = {{2.0f * M_PI, 0}}; for (const GlyphBox &glyph : glyphs) { - const CollisionRect &bbox = glyph.bbox; + const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box; const CollisionAnchor &anchor = glyph.anchor; float minPlacedX = anchor.x + bbox.tl.x / placementScale; @@ -246,30 +209,28 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs, float maxPlacedX = anchor.x + bbox.br.x / placementScale; float maxPlacedY = anchor.y + bbox.br.y / placementScale; - Box query_box{Point{minPlacedX, minPlacedY}, - Point{maxPlacedX, maxPlacedY}}; + Box query_box{Point{minPlacedX, minPlacedY}, Point{maxPlacedX, maxPlacedY}}; std::vector<PlacementValue> blocking; - ((Tree *)hTree) - ->query(bgi::intersects(query_box), std::back_inserter(blocking)); + ((Tree *)hTree)->query(bgi::intersects(query_box), std::back_inserter(blocking)); if (horizontal) { - ((Tree *)cTree) - ->query(bgi::intersects(query_box), std::back_inserter(blocking)); + ((Tree *)cTree)->query(bgi::intersects(query_box), std::back_inserter(blocking)); } for (const PlacementValue &value : blocking) { const Box &s = std::get<0>(value); const PlacementBox &b = std::get<1>(value); + const CollisionRect &bbox2 = b.hBox ? b.hBox.get() : b.box; float x1, x2, y1, y2, intersectX, intersectY; // Adjust and compare bboxes to see if the glyphs might intersect if (placementScale > b.placementScale) { - x1 = b.anchor.x + b.bbox.tl.x / placementScale; - y1 = b.anchor.y + b.bbox.tl.y / placementScale; - x2 = b.anchor.x + b.bbox.br.x / placementScale; - y2 = b.anchor.y + b.bbox.br.y / placementScale; + x1 = b.anchor.x + bbox2.tl.x / placementScale; + y1 = b.anchor.y + bbox2.tl.y / placementScale; + x2 = b.anchor.x + bbox2.br.x / placementScale; + y2 = b.anchor.y + bbox2.br.y / placementScale; intersectX = x1 < maxPlacedX && x2 > minPlacedX; intersectY = y1 < maxPlacedY && y2 > minPlacedY; } else { @@ -278,10 +239,8 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs, x2 = anchor.x + bbox.br.x / b.placementScale; y2 = anchor.y + bbox.br.y / b.placementScale; - intersectX = x1 < s.max_corner().get<0>() && - x2 > s.min_corner().get<0>(); - intersectY = y1 < s.max_corner().get<1>() && - y2 > s.min_corner().get<1>(); + intersectX = x1 < s.max_corner().get<0>() && x2 > s.min_corner().get<0>(); + intersectY = y1 < s.max_corner().get<1>() && y2 > s.min_corner().get<1>(); } // If they can't intersect, skip more expensive rotation calculation @@ -301,45 +260,40 @@ PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs, }; void Collision::insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor, - float placementScale, - const PlacementRange &placementRange, bool horizontal, - float padding) { + float placementScale, const PlacementRange &placementRange, + bool horizontal) { assert(placementScale != std::numeric_limits<float>::infinity()); - std::vector<PlacementValue> result; - result.reserve(glyphs.size()); + std::vector<PlacementValue> allBounds; + allBounds.reserve(glyphs.size()); for (const GlyphBox &glyph : glyphs) { - const CollisionRect &bbox = glyph.bbox; const CollisionRect &box = glyph.box; + const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box; - float minScale = std::fmax(placementScale, glyph.minScale); + const float minScale = util::max(placementScale, glyph.minScale); + const float maxScale = glyph.maxScale != 0 ? glyph.maxScale : std::numeric_limits<float>::infinity(); - Box bounds{Point{anchor.x + bbox.tl.x / minScale, - anchor.y + bbox.tl.y / minScale}, - Point{anchor.x + bbox.br.x / minScale, - anchor.y + bbox.br.y / minScale}}; + const Box bounds = getBox(anchor, bbox, minScale, maxScale); PlacementBox placement; placement.anchor = anchor; placement.box = box; - placement.bbox = bbox; - placement.rotate = horizontal; + if (glyph.hBox) { + placement.hBox = bbox; + } placement.placementRange = placementRange; placement.placementScale = minScale; - placement.maxScale = glyph.maxScale; - placement.padding = padding; - - assert(!isnan(bounds.min_corner().get<0>()) && !isnan(bounds.min_corner().get<1>())); - assert(!isnan(bounds.max_corner().get<0>()) && !isnan(bounds.max_corner().get<1>())); + placement.maxScale = maxScale; + placement.padding = glyph.padding; - result.emplace_back(bounds, placement); + allBounds.emplace_back(bounds, placement); } // Bulk-insert all glyph boxes if (horizontal) { - ((Tree *)hTree)->insert(result.begin(), result.end()); + ((Tree *)hTree)->insert(allBounds.begin(), allBounds.end()); } else { - ((Tree *)cTree)->insert(result.begin(), result.end()); + ((Tree *)cTree)->insert(allBounds.begin(), allBounds.end()); } } diff --git a/src/text/glyph_store.cpp b/src/text/glyph_store.cpp index edcecde7ff..4b90b51c24 100644 --- a/src/text/glyph_store.cpp +++ b/src/text/glyph_store.cpp @@ -6,6 +6,8 @@ #include <mbgl/util/pbf.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/token.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/filesource.hpp> #include <mbgl/platform/platform.hpp> #include <uv.h> #include <algorithm> @@ -30,93 +32,111 @@ const std::map<uint32_t, SDFGlyph> &FontStack::getSDFs() const { return sdfs; } -const Shaping FontStack::getShaping(const std::u32string &string, const float &maxWidth, - const float &lineHeight, const float &alignment, const float &verticalAlignment, - const float &letterSpacing) const { - +const Shaping FontStack::getShaping(const std::u32string &string, const float maxWidth, + const float lineHeight, const float horizontalAlign, + const float verticalAlign, const float justify, + const float spacing, const vec2<float> &translate) const { std::lock_guard<std::mutex> lock(mtx); - uint32_t i = 0; - uint32_t x = 0; + Shaping shaping; + + int32_t x = std::round(translate.x * 24); // one em + const int32_t y = std::round(translate.y * 24); // one em + // Loop through all characters of this label and shape. for (uint32_t chr : string) { - shaping.emplace_back(0, chr, x, 0); - i++; + shaping.emplace_back(chr, x, y); auto metric = metrics.find(chr); if (metric != metrics.end()) { - x += metric->second.advance + letterSpacing; + x += metric->second.advance + spacing; } } - lineWrap(shaping, lineHeight, maxWidth, alignment, verticalAlignment); + if (!shaping.size()) + return shaping; + + lineWrap(shaping, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify); return shaping; } -void alignVertically(Shaping &shaping, const uint32_t &lines, const float &lineHeight, const float &verticalAlign) { - const float dy = -(lineHeight * (lines - 1) + 24) * verticalAlign - 5; - for (GlyphPlacement &shape : shaping) { - shape.y += dy; +void align(Shaping &shaping, const float justify, const float horizontalAlign, + const float verticalAlign, const uint32_t maxLineLength, const float lineHeight, + const uint32_t line) { + const float shiftX = (justify - horizontalAlign) * maxLineLength; + const float shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight; + + for (PositionedGlyph &glyph : shaping) { + glyph.x += shiftX; + glyph.y += shiftY; } } -void alignHorizontally(Shaping &shaping, const std::map<uint32_t, GlyphMetrics> &metrics, - const uint32_t &start, const uint32_t &end, const float &alignment) { - auto & shape = shaping.at(end); - auto metric = metrics.find(shape.glyph); +void justifyLine(Shaping &shaping, const std::map<uint32_t, GlyphMetrics> &metrics, uint32_t start, + uint32_t end, float justify) { + PositionedGlyph &glyph = shaping[end]; + auto metric = metrics.find(glyph.glyph); if (metric != metrics.end()) { - uint32_t lastAdvance = metric->second.advance; - int32_t lineIndent = (shape.x + lastAdvance) * alignment; + const uint32_t lastAdvance = metric->second.advance; + const float lineIndent = float(glyph.x + lastAdvance) * justify; + for (uint32_t j = start; j <= end; j++) { shaping[j].x -= lineIndent; } } } -void FontStack::lineWrap(Shaping &shaping, const float &lineHeight, const float &maxWidth, - const float &alignment, const float &verticalAlignment) const { - - std::size_t num_shapes = shaping.size(); - if (!num_shapes) { - return; - } +void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float maxWidth, + const float horizontalAlign, const float verticalAlign, + const float justify) const { uint32_t lastSafeBreak = 0; + uint32_t lengthBeforeCurrentLine = 0; uint32_t lineStartIndex = 0; uint32_t line = 0; - for (uint32_t i = 0; i < shaping.size(); i++) { - GlyphPlacement &shape = shaping[i]; - shape.x -= lengthBeforeCurrentLine; - shape.y += lineHeight * line; + uint32_t maxLineLength = 0; - if (shape.x > maxWidth && lastSafeBreak > 0) { + if (maxWidth) { + for (uint32_t i = 0; i < shaping.size(); i++) { + PositionedGlyph &shape = shaping[i]; - uint32_t lineLength = shaping[lastSafeBreak + 1].x; + shape.x -= lengthBeforeCurrentLine; + shape.y += lineHeight * line; - for (uint32_t k = lastSafeBreak + 1; k <= i; k++) { - shaping[k].y += lineHeight; - shaping[k].x -= lineLength; - } + if (shape.x > maxWidth && lastSafeBreak > 0) { + + uint32_t lineLength = shaping[lastSafeBreak + 1].x; + maxLineLength = util::max(lineLength, maxLineLength); - if (alignment) { - alignHorizontally(shaping, metrics, lineStartIndex, lastSafeBreak - 1, alignment); + for (uint32_t k = lastSafeBreak + 1; k <= i; k++) { + shaping[k].y += lineHeight; + shaping[k].x -= lineLength; + } + + if (justify) { + justifyLine(shaping, metrics, lineStartIndex, lastSafeBreak - 1, justify); + } + + lineStartIndex = lastSafeBreak + 1; + lastSafeBreak = 0; + lengthBeforeCurrentLine += lineLength; + line++; } - lineStartIndex = lastSafeBreak + 1; - lastSafeBreak = 0; - lengthBeforeCurrentLine += lineLength; - line++; - } - if (shape.glyph == 32) { - lastSafeBreak = i; + if (shape.glyph == 32) { + lastSafeBreak = i; + } } } - alignHorizontally(shaping, metrics, lineStartIndex, shaping.size() - 1, alignment); - alignVertically(shaping, line + 1, lineHeight, verticalAlignment); + + maxLineLength = maxLineLength || shaping.back().x; + + justifyLine(shaping, metrics, lineStartIndex, shaping.size() - 1, justify); + align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line); } -GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange) +GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, const std::shared_ptr<FileSource> &fileSource) : future(promise.get_future().share()) { // Load the glyph set URL @@ -133,7 +153,7 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl fprintf(stderr, "%s\n", url.c_str()); #endif - platform::request_http(url, [&](platform::Response *res) { + fileSource->load(ResourceType::Glyphs, url, [&](platform::Response *res) { if (res->code != 200) { // Something went wrong with loading the glyph pbf. Pass on the error to the future listeners. const std::string msg = util::sprintf<255>("[ERROR] failed to load glyphs (%d): %s\n", res->code, res->error_message.c_str()); @@ -207,12 +227,19 @@ void GlyphPBF::parse(FontStack &stack) { data.clear(); } -GlyphStore::GlyphStore(const std::string &glyphURL) - : glyphURL(glyphURL) {} +GlyphStore::GlyphStore(const std::shared_ptr<FileSource> &fileSource) : fileSource(fileSource) {} + +void GlyphStore::setURL(const std::string &url) { + glyphURL = url; +} + void GlyphStore::waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges) { // We are implementing a blocking wait with futures: Every GlyphSet has a future that we are // waiting for until it is loaded. + if (glyphRanges.empty()) { + return; + } FontStack *stack = nullptr; @@ -243,7 +270,7 @@ std::shared_future<GlyphPBF &> GlyphStore::loadGlyphRange(const std::string &fon auto range_it = rangeSets.find(range); if (range_it == rangeSets.end()) { // We don't have this glyph set yet for this font stack. - range_it = rangeSets.emplace(range, std::make_unique<GlyphPBF>(glyphURL, fontStack, range)).first; + range_it = rangeSets.emplace(range, std::make_unique<GlyphPBF>(glyphURL, fontStack, range, fileSource)).first; } return range_it->second->getFuture(); diff --git a/src/text/placement.cpp b/src/text/placement.cpp index e33a4bdec6..26cc9f70f7 100644 --- a/src/text/placement.cpp +++ b/src/text/placement.cpp @@ -1,73 +1,34 @@ #include <mbgl/text/placement.hpp> -#include <mbgl/map/vector_tile.hpp> -#include <mbgl/geometry/interpolate.hpp> -#include <mbgl/renderer/text_bucket.hpp> +#include <mbgl/geometry/anchor.hpp> +#include <mbgl/text/glyph.hpp> +#include <mbgl/text/placement.hpp> +#include <mbgl/text/glyph_store.hpp> +#include <mbgl/style/style_bucket.hpp> + #include <mbgl/util/math.hpp> -#include <mbgl/util/constants.hpp> - -#include <algorithm> - -using namespace mbgl; - -const int Placement::tileExtent = 4096; -const int Placement::glyphSize = - 24; // size in pixels of this glyphs in the tile -const float Placement::minScale = 0.5; // underscale by 1 zoom level - -Placement::Placement(int8_t zoom) - : zoom(zoom), - zOffset(log(256.0 / util::tileSize) / log(2)), - - // Calculate the maximum scale we can go down in our fake-3d rtree so that - // placement still makes sense. This is calculated so that the minimum - // placement zoom can be at most 25.5 (we use an unsigned integer x10 to - // store the minimum zoom). - // - // We don't want to place labels all the way to 25.5. This lets too many - // glyphs be placed, slowing down collision checking. Only place labels if - // they will show up within the intended zoom range of the tile. - // TODO make this not hardcoded to 3 - maxPlacementScale(std::exp(log(2) * util::min((25.5 - zoom), 3.0))) {} - -bool byScale(const Anchor &a, const Anchor &b) { return a.scale < b.scale; } - -static const Glyph null_glyph; - -inline const Glyph &getGlyph(const GlyphPlacement &placed, - const GlyphPositions &face) { - auto it = face.find(placed.glyph); - if (it != face.end()) { - return it->second; - } else { - fprintf(stderr, "glyph %d does not exist\n", placed.glyph); - } - return null_glyph; -} +namespace mbgl { + +const float Placement::globalMinScale = 0.5; // underscale by 1 zoom level struct GlyphInstance { explicit GlyphInstance(const vec2<float> &anchor) : anchor(anchor) {} - explicit GlyphInstance(const vec2<float> &anchor, float offset, - float minScale, float maxScale, float angle) - : anchor(anchor), - offset(offset), - minScale(minScale), - maxScale(maxScale), - angle(angle) {} + explicit GlyphInstance(const vec2<float> &anchor, float offset, float minScale, float maxScale, + float angle) + : anchor(anchor), offset(offset), minScale(minScale), maxScale(maxScale), angle(angle) {} const vec2<float> anchor; const float offset = 0.0f; - const float minScale = Placement::minScale; + const float minScale = Placement::globalMinScale; const float maxScale = std::numeric_limits<float>::infinity(); const float angle = 0.0f; }; typedef std::vector<GlyphInstance> GlyphInstances; -void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, - Anchor &anchor, float offset, - const std::vector<Coordinate> &line, int segment, - int8_t direction, float maxAngleDelta) { +void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor &anchor, + float offset, const std::vector<Coordinate> &line, int segment, + int8_t direction, float maxAngle) { const bool upsideDown = direction < 0; if (offset < 0) @@ -92,14 +53,14 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, while (true) { const float dist = util::dist<float>(newAnchor, end); const float scale = offset / dist; - float angle = -std::atan2(end.x - newAnchor.x, end.y - newAnchor.y) + - direction * M_PI / 2.0f; + float angle = + -std::atan2(end.x - newAnchor.x, end.y - newAnchor.y) + direction * M_PI / 2.0f; if (upsideDown) angle += M_PI; // Don't place around sharp corners float angleDiff = std::fmod((angle - prevAngle), (2.0f * M_PI)); - if (prevAngle && std::fabs(angleDiff) > maxAngleDelta) { + if (prevAngle && std::fabs(angleDiff) > maxAngle) { anchor.scale = prevscale; break; } @@ -109,8 +70,7 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, /* offset */ static_cast<float>(upsideDown ? M_PI : 0.0), /* minScale */ scale, /* maxScale */ prevscale, - /* angle */ static_cast<float>( - std::fmod((angle + 2.0 * M_PI), (2.0 * M_PI)))}; + /* angle */ static_cast<float>(std::fmod((angle + 2.0 * M_PI), (2.0 * M_PI)))}; if (scale <= placementScale) break; @@ -135,49 +95,152 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, } } -void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes, - Anchor &anchor, vec2<float> origin, const Shaping &shaping, - const GlyphPositions &face, float fontScale, - bool horizontal, const std::vector<Coordinate> &line, - float maxAngleDelta, float rotate) { - // The total text advance is the width of this label. - - // TODO: allow setting an alignment - // var alignment = 'center'; - // if (alignment == 'center') { - // origin.x -= advance / 2; - // } else if (alignment == 'right') { - // origin.x -= advance; - // } +GlyphBox getMergedBoxes(const GlyphBoxes &glyphs, const Anchor &anchor) { + // Collision checks between rotating and fixed labels are relatively expensive, + // so we use one box per label, not per glyph for horizontal labels. + + const float inf = std::numeric_limits<float>::infinity(); + + GlyphBox mergedglyphs{/* box */ CollisionRect{inf, inf, -inf, -inf}, + /* anchor */ anchor, + /* minScale */ 0, + /* maxScale */ inf, + /* padding */ -inf}; + + CollisionRect &box = mergedglyphs.box; + + for (const GlyphBox &glyph : glyphs) { + const CollisionRect &gbox = glyph.box; + box.tl.x = util::min(box.tl.x, gbox.tl.x); + box.tl.y = util::min(box.tl.y, gbox.tl.y); + box.br.x = util::max(box.br.x, gbox.br.x); + box.br.y = util::max(box.br.y, gbox.br.y); + mergedglyphs.minScale = util::max(mergedglyphs.minScale, glyph.minScale); + mergedglyphs.padding = util::max(mergedglyphs.padding, glyph.padding); + } + // for all horizontal labels, calculate bbox covering all rotated positions + float x12 = box.tl.x * box.tl.x, y12 = box.tl.y * box.tl.y, x22 = box.br.x * box.br.x, + y22 = box.br.y * box.br.y, + diag = std::sqrt(util::max(x12 + y12, x12 + y22, x22 + y12, x22 + y22)); + + mergedglyphs.hBox = CollisionRect{-diag, -diag, diag, diag}; + + return mergedglyphs; +} + +Placement Placement::getIcon(Anchor &anchor, const Rect<uint16_t> &image, float boxScale, + const std::vector<Coordinate> &line, const StyleBucketSymbol &props) { + const float x = image.w / 2.0f; // No need to divide by image.pixelRatio here? + const float y = image.h / 2.0f; // image.pixelRatio; + + const float dx = props.icon.offset.x; + const float dy = props.icon.offset.y; + float x1 = (dx - x); + float x2 = (dx + x); + float y1 = (dy - y); + float y2 = (dy + y); + + vec2<float> tl{x1, y1}; + vec2<float> tr{x2, y1}; + vec2<float> br{x2, y2}; + vec2<float> bl{x1, y2}; + + float angle = props.icon.rotate * M_PI / 180.0f; + if (anchor.segment >= 0 && props.icon.rotation_alignment != RotationAlignmentType::Viewport) { + const Coordinate &next = line[anchor.segment]; + angle += -std::atan2(next.x - anchor.x, next.y - anchor.y) + M_PI / 2; + } + + if (angle) { + // Compute the transformation matrix. + float angle_sin = std::sin(angle); + float angle_cos = std::cos(angle); + std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; + + tl = tl.matMul(matrix); + tr = tr.matMul(matrix); + bl = bl.matMul(matrix); + br = br.matMul(matrix); + + x1 = util::min(tl.x, tr.x, bl.x, br.x); + x2 = util::max(tl.x, tr.x, bl.x, br.x); + y1 = util::min(tl.y, tr.y, bl.y, br.y); + y2 = util::max(tl.y, tr.y, bl.y, br.y); + } + + const CollisionRect box{/* x1 */ x1 * boxScale, + /* y1 */ y1 * boxScale, + /* x2 */ x2 * boxScale, + /* y2 */ y2 * boxScale}; + + Placement placement; + + placement.boxes.emplace_back( + /* box */ box, + /* anchor */ anchor, + /* minScale */ Placement::globalMinScale, + /* maxScale */ std::numeric_limits<float>::infinity(), + /* padding */ props.icon.padding); + + placement.shapes.emplace_back( + /* tl */ tl, + /* tr */ tr, + /* bl */ bl, + /* br */ br, + /* image */ image, + /* angle */ 0, + /* anchors */ anchor, + /* minScale */ Placement::globalMinScale, + /* maxScale */ std::numeric_limits<float>::infinity()); + + placement.minScale = anchor.scale; + + return placement; +} + +Placement Placement::getGlyphs(Anchor &anchor, const vec2<float> &origin, const Shaping &shaping, + const GlyphPositions &face, float boxScale, bool horizontal, + const std::vector<Coordinate> &line, + const StyleBucketSymbol &props) { + const float maxAngle = props.text.max_angle * M_PI / 180; + const float rotate = props.text.rotate * M_PI / 180; + const float padding = props.text.padding; + const bool alongLine = props.text.rotation_alignment != RotationAlignmentType::Viewport; + const bool keepUpright = props.text.keep_upright; + + Placement placement; const uint32_t buffer = 3; - for (const GlyphPlacement &placed : shaping) { - const Glyph &glyph = getGlyph(placed, face); - if (!glyph) { - // This glyph is empty and doesn't have any pixels that we'd need to - // show. + for (const PositionedGlyph &shape : shaping) { + auto face_it = face.find(shape.glyph); + if (face_it == face.end()) continue; - } + const Glyph &glyph = face_it->second; + const Rect<uint16_t> &rect = glyph.rect; - float x = - (origin.x + placed.x + glyph.metrics.left - buffer + glyph.rect.w / 2) * - fontScale; + if (!glyph) + continue; + + if (!rect) + continue; + + const float x = (origin.x + shape.x + glyph.metrics.left - buffer + rect.w / 2) * boxScale; GlyphInstances glyphInstances; - if (anchor.segment >= 0) { - getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, - line, anchor.segment, 1, maxAngleDelta); - getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, - line, anchor.segment, -1, maxAngleDelta); + if (anchor.segment >= 0 && alongLine) { + getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line, anchor.segment, 1, + maxAngle); + if (keepUpright) + getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, x, line, + anchor.segment, -1, maxAngle); } else { - glyphInstances.emplace_back(GlyphInstance{anchor}); } - const float x1 = origin.x + placed.x + glyph.metrics.left - buffer; - const float y1 = origin.y + placed.y - glyph.metrics.top - buffer; + const float x1 = origin.x + shape.x + glyph.metrics.left - buffer; + const float y1 = origin.y + shape.y - glyph.metrics.top - buffer; const float x2 = x1 + glyph.rect.w; const float y2 = y1 + glyph.rect.h; @@ -186,8 +249,7 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes, const vec2<float> obl{x1, y2}; const vec2<float> obr{x2, y2}; - const CollisionRect obox{fontScale *x1, fontScale *y1, - fontScale *x2, fontScale *y2}; + const CollisionRect obox{boxScale * x1, boxScale * y1, boxScale * x2, boxScale * y2}; for (const GlyphInstance &instance : glyphInstances) { vec2<float> tl = otl; @@ -204,78 +266,47 @@ void getGlyphs(PlacedGlyphs &glyphs, GlyphBoxes &boxes, // Compute the transformation matrix. float angle_sin = std::sin(angle); float angle_cos = std::cos(angle); - std::array<float, 4> matrix = { - {angle_cos, -angle_sin, angle_sin, angle_cos}}; + std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; tl = tl.matMul(matrix); tr = tr.matMul(matrix); bl = bl.matMul(matrix); br = br.matMul(matrix); - - // Calculate the rotated glyph's bounding box offsets from the - // anchor point. - box = CollisionRect{ - fontScale * util::min(tl.x, tr.x, bl.x, br.x), - fontScale * util::min(tl.y, tr.y, bl.y, br.y), - fontScale * util::max(tl.x, tr.x, bl.x, br.x), - fontScale * util::max(tl.y, tr.y, bl.y, br.y)}; } - GlyphBox glyphBox = GlyphBox{ - box, - // Prevent label from extending past the end of the line - util::max(instance.minScale, anchor.scale), - instance.maxScale, - instance.anchor, - horizontal}; + // Prevent label from extending past the end of the line + const float glyphMinScale = std::max(instance.minScale, anchor.scale); // Remember the glyph for later insertion. - glyphs.emplace_back(PlacedGlyph{ - tl, tr, bl, br, glyph.rect, - static_cast<float>( - std::fmod((anchor.angle + rotate + instance.offset + 2 * M_PI), (2 * M_PI))), - glyphBox}); - - if (instance.offset == 0.0f) { - boxes.emplace_back(glyphBox); + placement.shapes.emplace_back( + tl, tr, bl, br, rect, + float(std::fmod((anchor.angle + rotate + instance.offset + 2 * M_PI), (2 * M_PI))), + instance.anchor, glyphMinScale, instance.maxScale); + + if (!instance.offset) { // not a flipped glyph + if (angle) { + // Calculate the rotated glyph's bounding box offsets from the anchor point. + box = CollisionRect{boxScale * util::min(tl.x, tr.x, bl.x, br.x), + boxScale * util::min(tl.y, tr.y, bl.y, br.y), + boxScale * util::max(tl.x, tr.x, bl.x, br.x), + boxScale * util::max(tl.y, tr.y, bl.y, br.y)}; + } + placement.boxes.emplace_back(box, instance.anchor, glyphMinScale, instance.maxScale, padding); } } } -} - -void Placement::addFeature(TextBucket &bucket, const std::vector<Coordinate> &line, - const StyleBucketText &info, const GlyphPositions &face, - const Shaping &shaping) { - const bool horizontal = info.path == TextPathType::Horizontal; - const float fontScale = (tileExtent / util::tileSize) / (glyphSize / info.max_size); - - Anchors anchors; - - if (line.size() == 1) { - // Point labels - anchors = Anchors{{Anchor{ - static_cast<float>(line[0].x), static_cast<float>(line[0].y), - 0, minScale}}}; - } else { - // Line labels - anchors = interpolate(line, info.min_distance, minScale); + // TODO avoid creating the boxes in the first place? + if (horizontal) + placement.boxes = {getMergedBoxes(placement.boxes, anchor)}; - // Sort anchors by segment so that we can start placement with - // the anchors that can be shown at the lowest zoom levels. - std::sort(anchors.begin(), anchors.end(), byScale); + const float minPlacementScale = anchor.scale; + placement.minScale = std::numeric_limits<float>::infinity(); + for (const GlyphBox &box : placement.boxes) { + placement.minScale = util::min(placement.minScale, box.minScale); } + placement.minScale = util::max(minPlacementScale, Placement::globalMinScale); - for (Anchor anchor : anchors) { - PlacedGlyphs glyphs; - GlyphBoxes boxes; - - getGlyphs(glyphs, boxes, anchor, info.translate, shaping, face, fontScale, horizontal, line, - info.max_angle_delta, info.rotate); - PlacementProperty place = collision.place(boxes, anchor, anchor.scale, maxPlacementScale, - info.padding, horizontal, info.always_visible); - if (place) { - bucket.addGlyphs(glyphs, place.zoom, place.rotationRange, zoom - zOffset); - } - } + return placement; +} } diff --git a/src/text/rotation_range.cpp b/src/text/rotation_range.cpp index 943253a3da..2596cf1bb6 100644 --- a/src/text/rotation_range.cpp +++ b/src/text/rotation_range.cpp @@ -239,14 +239,14 @@ CollisionRange rotationRange(const GlyphBox &inserting, static_cast<float>((b.anchor.y - a.anchor.y) * scale)}; // Generate a list of collision interval - if (a.rotate && b.rotate) { + if (a.hBox && b.hBox) { collisions = rotatingRotatingCollisions(a.box, b.box, relativeAnchor); - } else if (a.rotate) { + } else if (a.hBox) { const CollisionRect box { b.box.tl.x + relativeAnchor.x, b.box.tl.y + relativeAnchor.y, b.box.br.x + relativeAnchor.x, b.box.br.y + relativeAnchor.y}; collisions = rotatingFixedCollisions(a.box, box); - } else if (b.rotate) { + } else if (b.hBox) { const CollisionRect box { a.box.tl.x - relativeAnchor.x, a.box.tl.y - relativeAnchor.y, a.box.br.x - relativeAnchor.x, a.box.br.y - relativeAnchor.y}; diff --git a/src/util/filesource.cpp b/src/util/filesource.cpp new file mode 100644 index 0000000000..2bbf146b0c --- /dev/null +++ b/src/util/filesource.cpp @@ -0,0 +1,58 @@ +#include <mbgl/util/filesource.hpp> +#include <mbgl/platform/platform.hpp> + +#include <fstream> +#include <sstream> + +namespace mbgl { + +FileSource::FileSource() {} + + +void FileSource::setBase(const std::string &value) { + base = value; +} + +const std::string &FileSource::getBase() const { + return base; +} + +void FileSource::load(ResourceType type, const std::string &url, std::function<void(platform::Response *)> callback, const std::shared_ptr<uv::loop> loop) { + // convert relative URLs to absolute URLs + + const std::string absoluteURL = [&]() -> std::string { + const size_t separator = url.find("://"); + if (separator == std::string::npos) { + // Relative URL. + return base + url; + } else { + return url; + } + }(); + + const size_t separator = absoluteURL.find("://"); + const std::string protocol = separator != std::string::npos ? absoluteURL.substr(0, separator) : ""; + + if (protocol == "file") { + // load from disk + const std::string path = absoluteURL.substr(separator + 3); + std::ifstream file(path); + + platform::Response response(callback); + if (!file.good()) { + response.error_message = "file not found (" + path + ")"; + } else { + std::stringstream data; + data << file.rdbuf(); + response.code = 200; + response.body = data.str(); + } + + callback(&response); + } else { + // load from the internet + platform::request_http(absoluteURL, callback, loop); + } +} + +}
\ No newline at end of file diff --git a/styles/bright/img/sprite.json b/styles/bright/img/sprite.json new file mode 100644 index 0000000000..d8fd539f42 --- /dev/null +++ b/styles/bright/img/sprite.json @@ -0,0 +1,2818 @@ +{ + "marker-24": { + "x": 0, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "zoo-24": { + "x": 26, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "-24": { + "x": 0, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "wetland-24": { + "x": 26, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "water-24": { + "x": 52, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "airfield-24": { + "x": 52, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "waste-basket-24": { + "x": 0, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "warehouse-24": { + "x": 26, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "airport-24": { + "x": 52, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "village-24": { + "x": 78, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "triangle-stroked-24": { + "x": 78, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "alcohol-shop-24": { + "x": 78, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "triangle-24": { + "x": 0, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "town-hall-24": { + "x": 26, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "america-football-24": { + "x": 52, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "town-24": { + "x": 78, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "toilets-24": { + "x": 104, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "art-gallery-24": { + "x": 104, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "theatre-24": { + "x": 104, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "tennis-24": { + "x": 104, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "bakery-24": { + "x": 0, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "telephone-24": { + "x": 26, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "swimming-24": { + "x": 52, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "bank-24": { + "x": 78, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "suitcase-24": { + "x": 104, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "star-stroked-24": { + "x": 130, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "bar-24": { + "x": 130, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "star-24": { + "x": 130, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "square-stroked-24": { + "x": 130, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "baseball-24": { + "x": 130, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "square-24": { + "x": 0, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "soccer-24": { + "x": 26, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "basketball-24": { + "x": 52, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "slaughterhouse-24": { + "x": 78, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "skiing-24": { + "x": 104, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "beer-24": { + "x": 130, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "shop-24": { + "x": 156, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "scooter-24": { + "x": 156, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "bicycle-24": { + "x": 156, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "school-24": { + "x": 156, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "rocket-24": { + "x": 156, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "building-24": { + "x": 156, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "roadblock-24": { + "x": 0, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "restaurant-24": { + "x": 26, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "bus-24": { + "x": 52, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "religious-muslim-24": { + "x": 78, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "religious-jewish-24": { + "x": 104, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "cafe-24": { + "x": 130, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "religious-christian-24": { + "x": 156, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "rail-underground-24": { + "x": 182, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "camera-24": { + "x": 182, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "rail-metro-24": { + "x": 182, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "rail-light-24": { + "x": 182, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "campsite-24": { + "x": 182, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "rail-above-24": { + "x": 182, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "rail-24": { + "x": 182, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "car-24": { + "x": 0, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "prison-24": { + "x": 26, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "post-24": { + "x": 52, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "cemetery-24": { + "x": 78, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "polling-place-24": { + "x": 104, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "police-24": { + "x": 130, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "chemist-24": { + "x": 156, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "playground-24": { + "x": 182, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "place-of-worship-24": { + "x": 208, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "cinema-24": { + "x": 208, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "pitch-24": { + "x": 208, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "pharmacy-24": { + "x": 208, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "circle-24": { + "x": 208, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "parking-garage-24": { + "x": 208, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "parking-24": { + "x": 208, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "circle-stroked-24": { + "x": 208, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "park2-24": { + "x": 0, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "park-24": { + "x": 26, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "city-24": { + "x": 52, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "oil-well-24": { + "x": 78, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "music-24": { + "x": 104, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "clothing-store-24": { + "x": 130, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "museum-24": { + "x": 156, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "monument-24": { + "x": 182, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "college-24": { + "x": 208, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "mobilephone-24": { + "x": 234, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "minefield-24": { + "x": 234, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "commercial-24": { + "x": 234, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "marker-stroked-24": { + "x": 234, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "london-underground-24": { + "x": 234, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "cricket-24": { + "x": 234, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "logging-24": { + "x": 234, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "lodging-24": { + "x": 234, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "cross-24": { + "x": 234, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "lighthouse-24": { + "x": 0, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "library-24": { + "x": 26, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "dam-24": { + "x": 52, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "laundry-24": { + "x": 78, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "land-use-24": { + "x": 104, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "danger-24": { + "x": 130, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "industrial-24": { + "x": 156, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "hospital-24": { + "x": 182, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "disability-24": { + "x": 208, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "heliport-24": { + "x": 234, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "heart-24": { + "x": 260, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "dog-park-24": { + "x": 260, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "harbor-24": { + "x": 260, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "hairdresser-24": { + "x": 260, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "embassy-24": { + "x": 260, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "grocery-24": { + "x": 260, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "golf-24": { + "x": 260, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "emergency-telephone-24": { + "x": 260, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "garden-24": { + "x": 260, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "fuel-24": { + "x": 260, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "entrance-24": { + "x": 0, + "y": 260, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "fire-station-24": { + "x": 26, + "y": 260, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "ferry-24": { + "x": 52, + "y": 260, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "farm-24": { + "x": 78, + "y": 260, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "fast-food-24": { + "x": 104, + "y": 260, + "width": 24, + "height": 24, + "pixelRatio": 1, + "sdf": false + }, + "police-18": { + "x": 130, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "fast-food-18": { + "x": 150, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "farm-18": { + "x": 170, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "fire-station-18": { + "x": 190, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "fuel-18": { + "x": 210, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "entrance-18": { + "x": 230, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "garden-18": { + "x": 250, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "golf-18": { + "x": 286, + "y": 0, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "emergency-telephone-18": { + "x": 286, + "y": 20, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "grocery-18": { + "x": 286, + "y": 40, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "hairdresser-18": { + "x": 286, + "y": 60, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "embassy-18": { + "x": 286, + "y": 80, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "harbor-18": { + "x": 286, + "y": 100, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "heart-18": { + "x": 286, + "y": 120, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "dog-park-18": { + "x": 286, + "y": 140, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "heliport-18": { + "x": 286, + "y": 160, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "hospital-18": { + "x": 286, + "y": 180, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "disability-18": { + "x": 286, + "y": 200, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "industrial-18": { + "x": 286, + "y": 220, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "land-use-18": { + "x": 286, + "y": 240, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "danger-18": { + "x": 286, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "laundry-18": { + "x": 0, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "library-18": { + "x": 20, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "dam-18": { + "x": 40, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "lighthouse-18": { + "x": 60, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "lodging-18": { + "x": 80, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "cross-18": { + "x": 100, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "logging-18": { + "x": 120, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "london-underground-18": { + "x": 140, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "cricket-18": { + "x": 160, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "marker-18": { + "x": 180, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "-18": { + "x": 200, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "marker-stroked-18": { + "x": 220, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "minefield-18": { + "x": 240, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "commercial-18": { + "x": 260, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "mobilephone-18": { + "x": 280, + "y": 286, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "monument-18": { + "x": 306, + "y": 0, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "college-18": { + "x": 306, + "y": 20, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "motorway_1": { + "x": 0, + "y": 306, + "width": 19, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "motorway_2": { + "x": 21, + "y": 306, + "width": 24, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "motorway_3": { + "x": 47, + "y": 306, + "width": 29, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "motorway_4": { + "x": 78, + "y": 306, + "width": 34, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "motorway_5": { + "x": 114, + "y": 306, + "width": 39, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "motorway_6": { + "x": 155, + "y": 306, + "width": 44, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "museum-18": { + "x": 306, + "y": 40, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "music-18": { + "x": 306, + "y": 60, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "clothing-store-18": { + "x": 306, + "y": 80, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "oil-well-18": { + "x": 306, + "y": 100, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "park-18": { + "x": 306, + "y": 120, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "city-18": { + "x": 306, + "y": 140, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "park2-18": { + "x": 306, + "y": 160, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "parking-18": { + "x": 306, + "y": 180, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "circle-stroked-18": { + "x": 306, + "y": 200, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "parking-garage-18": { + "x": 306, + "y": 220, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "pharmacy-18": { + "x": 306, + "y": 240, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "circle-18": { + "x": 306, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "pitch-18": { + "x": 306, + "y": 280, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "place-of-worship-18": { + "x": 201, + "y": 306, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "cinema-18": { + "x": 221, + "y": 306, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "playground-18": { + "x": 241, + "y": 306, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "ferry-18": { + "x": 261, + "y": 306, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "chemist-18": { + "x": 281, + "y": 306, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "polling-place-18": { + "x": 301, + "y": 306, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "post-18": { + "x": 326, + "y": 0, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "cemetery-18": { + "x": 326, + "y": 20, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "prison-18": { + "x": 326, + "y": 40, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "rail-18": { + "x": 326, + "y": 60, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "car-18": { + "x": 326, + "y": 80, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "rail-above-18": { + "x": 326, + "y": 100, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "rail-light-18": { + "x": 326, + "y": 120, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "campsite-18": { + "x": 326, + "y": 140, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "rail-metro-18": { + "x": 326, + "y": 160, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "rail-underground-18": { + "x": 326, + "y": 180, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "camera-18": { + "x": 326, + "y": 200, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "religious-christian-18": { + "x": 326, + "y": 220, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "religious-jewish-18": { + "x": 326, + "y": 240, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "cafe-18": { + "x": 326, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "religious-muslim-18": { + "x": 326, + "y": 280, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "restaurant-18": { + "x": 326, + "y": 300, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "bus-18": { + "x": 0, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "roadblock-18": { + "x": 20, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "rocket-18": { + "x": 40, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "building-18": { + "x": 60, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "school-18": { + "x": 80, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "scooter-18": { + "x": 100, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "bicycle-18": { + "x": 120, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "shop-18": { + "x": 140, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "skiing-18": { + "x": 160, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "beer-18": { + "x": 180, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "slaughterhouse-18": { + "x": 200, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "soccer-18": { + "x": 220, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "basketball-18": { + "x": 240, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "square-18": { + "x": 260, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "square-stroked-18": { + "x": 280, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "baseball-18": { + "x": 300, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "star-18": { + "x": 320, + "y": 326, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "star-stroked-18": { + "x": 346, + "y": 0, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "bar-18": { + "x": 346, + "y": 20, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "suitcase-18": { + "x": 346, + "y": 40, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "swimming-18": { + "x": 346, + "y": 60, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "bank-18": { + "x": 346, + "y": 80, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "telephone-18": { + "x": 346, + "y": 100, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "tennis-18": { + "x": 346, + "y": 120, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "bakery-18": { + "x": 346, + "y": 140, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "theatre-18": { + "x": 346, + "y": 160, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "toilets-18": { + "x": 346, + "y": 180, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "art-gallery-18": { + "x": 346, + "y": 200, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "town-18": { + "x": 346, + "y": 220, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "town-hall-18": { + "x": 346, + "y": 240, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "america-football-18": { + "x": 346, + "y": 260, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "triangle-18": { + "x": 346, + "y": 280, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "triangle-stroked-18": { + "x": 346, + "y": 300, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "alcohol-shop-18": { + "x": 346, + "y": 320, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "village-18": { + "x": 0, + "y": 346, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "warehouse-18": { + "x": 20, + "y": 346, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "airport-18": { + "x": 40, + "y": 346, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "waste-basket-18": { + "x": 60, + "y": 346, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "water-18": { + "x": 80, + "y": 346, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "airfield-18": { + "x": 100, + "y": 346, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "zoo-18": { + "x": 120, + "y": 346, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "wetland-18": { + "x": 140, + "y": 346, + "width": 18, + "height": 18, + "pixelRatio": 1, + "sdf": false + }, + "cricket-12": { + "x": 270, + "y": 260, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "prison-12": { + "x": 160, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "minefield-12": { + "x": 174, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "cemetery-12": { + "x": 188, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "rail-12": { + "x": 202, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "fuel-12": { + "x": 216, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "grocery-12": { + "x": 230, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "rail-above-12": { + "x": 244, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "mobilephone-12": { + "x": 258, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "car-12": { + "x": 272, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "rail-light-12": { + "x": 286, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "industrial-12": { + "x": 300, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "commercial-12": { + "x": 314, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "rail-metro-12": { + "x": 328, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "monument-12": { + "x": 342, + "y": 346, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "campsite-12": { + "x": 366, + "y": 0, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "rail-underground-12": { + "x": 366, + "y": 14, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "ferry-12": { + "x": 366, + "y": 28, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "disability-12": { + "x": 366, + "y": 42, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "religious-christian-12": { + "x": 366, + "y": 56, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "land-use-12": { + "x": 366, + "y": 70, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "camera-12": { + "x": 366, + "y": 84, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "religious-jewish-12": { + "x": 366, + "y": 98, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "emergency-telephone-12": { + "x": 366, + "y": 112, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "hairdresser-12": { + "x": 366, + "y": 126, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "religious-muslim-12": { + "x": 366, + "y": 140, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "laundry-12": { + "x": 366, + "y": 154, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "cafe-12": { + "x": 366, + "y": 168, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "restaurant-12": { + "x": 366, + "y": 182, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "fire-station-12": { + "x": 366, + "y": 196, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "danger-12": { + "x": 366, + "y": 210, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "roadblock-12": { + "x": 366, + "y": 224, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "museum-12": { + "x": 366, + "y": 238, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "bus-12": { + "x": 366, + "y": 252, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "rocket-12": { + "x": 366, + "y": 266, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "library-12": { + "x": 366, + "y": 280, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "college-12": { + "x": 366, + "y": 294, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "school-12": { + "x": 366, + "y": 308, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "music-12": { + "x": 366, + "y": 322, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "building-12": { + "x": 366, + "y": 336, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "scooter-12": { + "x": 366, + "y": 350, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "garden-12": { + "x": 0, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "harbor-12": { + "x": 14, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "shop-12": { + "x": 28, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "oil-well-12": { + "x": 42, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "bicycle-12": { + "x": 56, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "skiing-12": { + "x": 70, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "lighthouse-12": { + "x": 84, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "clothing-store-12": { + "x": 98, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "slaughterhouse-12": { + "x": 112, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "park-12": { + "x": 126, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "beer-12": { + "x": 140, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "soccer-12": { + "x": 154, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "-12": { + "x": 168, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "dam-12": { + "x": 182, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "square-12": { + "x": 196, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "park2-12": { + "x": 210, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "basketball-12": { + "x": 224, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "square-stroked-12": { + "x": 238, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "lodging-12": { + "x": 252, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "city-12": { + "x": 266, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "star-12": { + "x": 280, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "parking-12": { + "x": 294, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "baseball-12": { + "x": 308, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "star-stroked-12": { + "x": 322, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "embassy-12": { + "x": 336, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "heart-12": { + "x": 350, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "suitcase-12": { + "x": 364, + "y": 366, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "parking-garage-12": { + "x": 380, + "y": 0, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "bar-12": { + "x": 380, + "y": 14, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "swimming-12": { + "x": 380, + "y": 28, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "logging-12": { + "x": 380, + "y": 42, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "circle-stroked-12": { + "x": 380, + "y": 56, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "telephone-12": { + "x": 380, + "y": 70, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "pharmacy-12": { + "x": 380, + "y": 84, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "bank-12": { + "x": 380, + "y": 98, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "tennis-12": { + "x": 380, + "y": 112, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "entrance-12": { + "x": 380, + "y": 126, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "cross-12": { + "x": 380, + "y": 140, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "theatre-12": { + "x": 380, + "y": 154, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "pitch-12": { + "x": 380, + "y": 168, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "bakery-12": { + "x": 380, + "y": 182, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "toilets-12": { + "x": 380, + "y": 196, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "london-underground-12": { + "x": 380, + "y": 210, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "circle-12": { + "x": 380, + "y": 224, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "town-12": { + "x": 380, + "y": 238, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "place-of-worship-12": { + "x": 380, + "y": 252, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "art-gallery-12": { + "x": 380, + "y": 266, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "town-hall-12": { + "x": 380, + "y": 280, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "golf-12": { + "x": 380, + "y": 294, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "heliport-12": { + "x": 380, + "y": 308, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "triangle-12": { + "x": 380, + "y": 322, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "playground-12": { + "x": 380, + "y": 336, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "america-football-12": { + "x": 380, + "y": 350, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "triangle-stroked-12": { + "x": 380, + "y": 364, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "marker-12": { + "x": 0, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "cinema-12": { + "x": 14, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "village-12": { + "x": 28, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "police-12": { + "x": 42, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "alcohol-shop-12": { + "x": 56, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "warehouse-12": { + "x": 70, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "farm-12": { + "x": 84, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "dog-park-12": { + "x": 98, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "waste-basket-12": { + "x": 112, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "polling-place-12": { + "x": 126, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "airport-12": { + "x": 140, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "water-12": { + "x": 154, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "marker-stroked-12": { + "x": 168, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "fast-food-12": { + "x": 182, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "post-12": { + "x": 196, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "wetland-12": { + "x": 210, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "hospital-12": { + "x": 224, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "airfield-12": { + "x": 238, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "zoo-12": { + "x": 252, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "chemist-12": { + "x": 266, + "y": 380, + "width": 12, + "height": 12, + "pixelRatio": 1, + "sdf": false + }, + "wave": { + "x": 280, + "y": 380, + "width": 16, + "height": 8, + "pixelRatio": 1, + "sdf": false + } +}
\ No newline at end of file diff --git a/styles/bright/img/sprite.png b/styles/bright/img/sprite.png Binary files differnew file mode 100644 index 0000000000..34ef6124b8 --- /dev/null +++ b/styles/bright/img/sprite.png diff --git a/styles/bright/img/sprite@2x.json b/styles/bright/img/sprite@2x.json new file mode 100644 index 0000000000..b15134a6a8 --- /dev/null +++ b/styles/bright/img/sprite@2x.json @@ -0,0 +1,2818 @@ +{ + "marker-24": { + "x": 0, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "zoo-24": { + "x": 50, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "-24": { + "x": 0, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "wetland-24": { + "x": 50, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "water-24": { + "x": 100, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "airfield-24": { + "x": 100, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "waste-basket-24": { + "x": 0, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "warehouse-24": { + "x": 50, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "airport-24": { + "x": 100, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "village-24": { + "x": 150, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "triangle-stroked-24": { + "x": 150, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "alcohol-shop-24": { + "x": 150, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "triangle-24": { + "x": 0, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "town-hall-24": { + "x": 50, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "america-football-24": { + "x": 100, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "town-24": { + "x": 150, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "toilets-24": { + "x": 200, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "art-gallery-24": { + "x": 200, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "theatre-24": { + "x": 200, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "tennis-24": { + "x": 200, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "bakery-24": { + "x": 0, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "telephone-24": { + "x": 50, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "swimming-24": { + "x": 100, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "bank-24": { + "x": 150, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "suitcase-24": { + "x": 200, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "star-stroked-24": { + "x": 250, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "bar-24": { + "x": 250, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "star-24": { + "x": 250, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "square-stroked-24": { + "x": 250, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "baseball-24": { + "x": 250, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "square-24": { + "x": 0, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "soccer-24": { + "x": 50, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "basketball-24": { + "x": 100, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "slaughterhouse-24": { + "x": 150, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "skiing-24": { + "x": 200, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "beer-24": { + "x": 250, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "shop-24": { + "x": 300, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "scooter-24": { + "x": 300, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "bicycle-24": { + "x": 300, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "school-24": { + "x": 300, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "rocket-24": { + "x": 300, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "building-24": { + "x": 300, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "roadblock-24": { + "x": 0, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "restaurant-24": { + "x": 50, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "bus-24": { + "x": 100, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "religious-muslim-24": { + "x": 150, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "religious-jewish-24": { + "x": 200, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "cafe-24": { + "x": 250, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "religious-christian-24": { + "x": 300, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "rail-underground-24": { + "x": 350, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "camera-24": { + "x": 350, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "rail-metro-24": { + "x": 350, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "rail-light-24": { + "x": 350, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "campsite-24": { + "x": 350, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "rail-above-24": { + "x": 350, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "rail-24": { + "x": 350, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "car-24": { + "x": 0, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "prison-24": { + "x": 50, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "post-24": { + "x": 100, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "cemetery-24": { + "x": 150, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "polling-place-24": { + "x": 200, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "police-24": { + "x": 250, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "chemist-24": { + "x": 300, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "playground-24": { + "x": 350, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "place-of-worship-24": { + "x": 400, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "cinema-24": { + "x": 400, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "pitch-24": { + "x": 400, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "pharmacy-24": { + "x": 400, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "circle-24": { + "x": 400, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "parking-garage-24": { + "x": 400, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "parking-24": { + "x": 400, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "circle-stroked-24": { + "x": 400, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "park2-24": { + "x": 0, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "park-24": { + "x": 50, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "city-24": { + "x": 100, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "oil-well-24": { + "x": 150, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "music-24": { + "x": 200, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "clothing-store-24": { + "x": 250, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "museum-24": { + "x": 300, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "monument-24": { + "x": 350, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "college-24": { + "x": 400, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "mobilephone-24": { + "x": 450, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "minefield-24": { + "x": 450, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "commercial-24": { + "x": 450, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "marker-stroked-24": { + "x": 450, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "london-underground-24": { + "x": 450, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "cricket-24": { + "x": 450, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "logging-24": { + "x": 450, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "lodging-24": { + "x": 450, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "cross-24": { + "x": 450, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "lighthouse-24": { + "x": 0, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "library-24": { + "x": 50, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "dam-24": { + "x": 100, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "laundry-24": { + "x": 150, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "land-use-24": { + "x": 200, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "danger-24": { + "x": 250, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "industrial-24": { + "x": 300, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "hospital-24": { + "x": 350, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "disability-24": { + "x": 400, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "heliport-24": { + "x": 450, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "heart-24": { + "x": 500, + "y": 0, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "dog-park-24": { + "x": 500, + "y": 50, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "harbor-24": { + "x": 500, + "y": 100, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "hairdresser-24": { + "x": 500, + "y": 150, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "embassy-24": { + "x": 500, + "y": 200, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "grocery-24": { + "x": 500, + "y": 250, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "golf-24": { + "x": 500, + "y": 300, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "emergency-telephone-24": { + "x": 500, + "y": 350, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "garden-24": { + "x": 500, + "y": 400, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "fuel-24": { + "x": 500, + "y": 450, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "entrance-24": { + "x": 0, + "y": 500, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "fire-station-24": { + "x": 50, + "y": 500, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "ferry-24": { + "x": 100, + "y": 500, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "farm-24": { + "x": 150, + "y": 500, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "fast-food-24": { + "x": 200, + "y": 500, + "width": 48, + "height": 48, + "pixelRatio": 2, + "sdf": false + }, + "police-18": { + "x": 250, + "y": 500, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "fast-food-18": { + "x": 288, + "y": 500, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "farm-18": { + "x": 326, + "y": 500, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "fire-station-18": { + "x": 364, + "y": 500, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "fuel-18": { + "x": 402, + "y": 500, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "entrance-18": { + "x": 440, + "y": 500, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "garden-18": { + "x": 478, + "y": 500, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "golf-18": { + "x": 550, + "y": 0, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "emergency-telephone-18": { + "x": 550, + "y": 38, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "grocery-18": { + "x": 550, + "y": 76, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "hairdresser-18": { + "x": 550, + "y": 114, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "embassy-18": { + "x": 550, + "y": 152, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "harbor-18": { + "x": 550, + "y": 190, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "heart-18": { + "x": 550, + "y": 228, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "dog-park-18": { + "x": 550, + "y": 266, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "heliport-18": { + "x": 550, + "y": 304, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "hospital-18": { + "x": 550, + "y": 342, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "disability-18": { + "x": 550, + "y": 380, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "industrial-18": { + "x": 550, + "y": 418, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "land-use-18": { + "x": 550, + "y": 456, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "danger-18": { + "x": 550, + "y": 494, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "laundry-18": { + "x": 0, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "library-18": { + "x": 38, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "dam-18": { + "x": 76, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "lighthouse-18": { + "x": 114, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "lodging-18": { + "x": 152, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "cross-18": { + "x": 190, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "logging-18": { + "x": 228, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "london-underground-18": { + "x": 266, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "cricket-18": { + "x": 304, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "marker-18": { + "x": 342, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "-18": { + "x": 380, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "marker-stroked-18": { + "x": 418, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "minefield-18": { + "x": 456, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "commercial-18": { + "x": 494, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "mobilephone-18": { + "x": 532, + "y": 550, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "monument-18": { + "x": 588, + "y": 0, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "college-18": { + "x": 588, + "y": 38, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "motorway_1": { + "x": 0, + "y": 588, + "width": 38, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "motorway_2": { + "x": 40, + "y": 588, + "width": 48, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "motorway_3": { + "x": 90, + "y": 588, + "width": 58, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "motorway_4": { + "x": 150, + "y": 588, + "width": 68, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "motorway_5": { + "x": 220, + "y": 588, + "width": 78, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "motorway_6": { + "x": 300, + "y": 588, + "width": 88, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "museum-18": { + "x": 588, + "y": 76, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "music-18": { + "x": 588, + "y": 114, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "clothing-store-18": { + "x": 588, + "y": 152, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "oil-well-18": { + "x": 588, + "y": 190, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "park-18": { + "x": 588, + "y": 228, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "city-18": { + "x": 588, + "y": 266, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "park2-18": { + "x": 588, + "y": 304, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "parking-18": { + "x": 588, + "y": 342, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "circle-stroked-18": { + "x": 588, + "y": 380, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "parking-garage-18": { + "x": 588, + "y": 418, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "pharmacy-18": { + "x": 588, + "y": 456, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "circle-18": { + "x": 588, + "y": 494, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "pitch-18": { + "x": 588, + "y": 532, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "place-of-worship-18": { + "x": 390, + "y": 588, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "cinema-18": { + "x": 428, + "y": 588, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "playground-18": { + "x": 466, + "y": 588, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "ferry-18": { + "x": 504, + "y": 588, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "chemist-18": { + "x": 542, + "y": 588, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "polling-place-18": { + "x": 580, + "y": 588, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "post-18": { + "x": 626, + "y": 0, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "cemetery-18": { + "x": 626, + "y": 38, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "prison-18": { + "x": 626, + "y": 76, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "rail-18": { + "x": 626, + "y": 114, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "car-18": { + "x": 626, + "y": 152, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "rail-above-18": { + "x": 626, + "y": 190, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "rail-light-18": { + "x": 626, + "y": 228, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "campsite-18": { + "x": 626, + "y": 266, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "rail-metro-18": { + "x": 626, + "y": 304, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "rail-underground-18": { + "x": 626, + "y": 342, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "camera-18": { + "x": 626, + "y": 380, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "religious-christian-18": { + "x": 626, + "y": 418, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "religious-jewish-18": { + "x": 626, + "y": 456, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "cafe-18": { + "x": 626, + "y": 494, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "religious-muslim-18": { + "x": 626, + "y": 532, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "restaurant-18": { + "x": 626, + "y": 570, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "bus-18": { + "x": 0, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "roadblock-18": { + "x": 38, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "rocket-18": { + "x": 76, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "building-18": { + "x": 114, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "school-18": { + "x": 152, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "scooter-18": { + "x": 190, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "bicycle-18": { + "x": 228, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "shop-18": { + "x": 266, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "skiing-18": { + "x": 304, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "beer-18": { + "x": 342, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "slaughterhouse-18": { + "x": 380, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "soccer-18": { + "x": 418, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "basketball-18": { + "x": 456, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "square-18": { + "x": 494, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "square-stroked-18": { + "x": 532, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "baseball-18": { + "x": 570, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "star-18": { + "x": 608, + "y": 626, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "star-stroked-18": { + "x": 664, + "y": 0, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "bar-18": { + "x": 664, + "y": 38, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "suitcase-18": { + "x": 664, + "y": 76, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "swimming-18": { + "x": 664, + "y": 114, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "bank-18": { + "x": 664, + "y": 152, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "telephone-18": { + "x": 664, + "y": 190, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "tennis-18": { + "x": 664, + "y": 228, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "bakery-18": { + "x": 664, + "y": 266, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "theatre-18": { + "x": 664, + "y": 304, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "toilets-18": { + "x": 664, + "y": 342, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "art-gallery-18": { + "x": 664, + "y": 380, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "town-18": { + "x": 664, + "y": 418, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "town-hall-18": { + "x": 664, + "y": 456, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "america-football-18": { + "x": 664, + "y": 494, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "triangle-18": { + "x": 664, + "y": 532, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "triangle-stroked-18": { + "x": 664, + "y": 570, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "alcohol-shop-18": { + "x": 664, + "y": 608, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "village-18": { + "x": 0, + "y": 664, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "warehouse-18": { + "x": 38, + "y": 664, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "airport-18": { + "x": 76, + "y": 664, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "waste-basket-18": { + "x": 114, + "y": 664, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "water-18": { + "x": 152, + "y": 664, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "airfield-18": { + "x": 190, + "y": 664, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "zoo-18": { + "x": 228, + "y": 664, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "wetland-18": { + "x": 266, + "y": 664, + "width": 36, + "height": 36, + "pixelRatio": 2, + "sdf": false + }, + "cricket-12": { + "x": 516, + "y": 500, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "prison-12": { + "x": 304, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "minefield-12": { + "x": 330, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "cemetery-12": { + "x": 356, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "rail-12": { + "x": 382, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "fuel-12": { + "x": 408, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "grocery-12": { + "x": 434, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "rail-above-12": { + "x": 460, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "mobilephone-12": { + "x": 486, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "car-12": { + "x": 512, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "rail-light-12": { + "x": 538, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "industrial-12": { + "x": 564, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "commercial-12": { + "x": 590, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "rail-metro-12": { + "x": 616, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "monument-12": { + "x": 642, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "campsite-12": { + "x": 668, + "y": 664, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "rail-underground-12": { + "x": 702, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "ferry-12": { + "x": 702, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "disability-12": { + "x": 702, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "religious-christian-12": { + "x": 702, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "land-use-12": { + "x": 702, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "camera-12": { + "x": 702, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "religious-jewish-12": { + "x": 702, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "emergency-telephone-12": { + "x": 702, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "hairdresser-12": { + "x": 702, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "religious-muslim-12": { + "x": 702, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "laundry-12": { + "x": 702, + "y": 260, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "cafe-12": { + "x": 702, + "y": 286, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "restaurant-12": { + "x": 702, + "y": 312, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "fire-station-12": { + "x": 702, + "y": 338, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "danger-12": { + "x": 702, + "y": 364, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "roadblock-12": { + "x": 702, + "y": 390, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "museum-12": { + "x": 702, + "y": 416, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "bus-12": { + "x": 702, + "y": 442, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "rocket-12": { + "x": 702, + "y": 468, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "library-12": { + "x": 702, + "y": 494, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "college-12": { + "x": 702, + "y": 520, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "school-12": { + "x": 702, + "y": 546, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "music-12": { + "x": 702, + "y": 572, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "building-12": { + "x": 702, + "y": 598, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "scooter-12": { + "x": 702, + "y": 624, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "garden-12": { + "x": 702, + "y": 650, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "harbor-12": { + "x": 702, + "y": 676, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "shop-12": { + "x": 0, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "oil-well-12": { + "x": 26, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "bicycle-12": { + "x": 52, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "skiing-12": { + "x": 78, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "lighthouse-12": { + "x": 104, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "clothing-store-12": { + "x": 130, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "slaughterhouse-12": { + "x": 156, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "park-12": { + "x": 182, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "beer-12": { + "x": 208, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "soccer-12": { + "x": 234, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "-12": { + "x": 260, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "dam-12": { + "x": 286, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "square-12": { + "x": 312, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "park2-12": { + "x": 338, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "basketball-12": { + "x": 364, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "square-stroked-12": { + "x": 390, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "lodging-12": { + "x": 416, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "city-12": { + "x": 442, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "star-12": { + "x": 468, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "parking-12": { + "x": 494, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "baseball-12": { + "x": 520, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "star-stroked-12": { + "x": 546, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "embassy-12": { + "x": 572, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "heart-12": { + "x": 598, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "suitcase-12": { + "x": 624, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "parking-garage-12": { + "x": 650, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "bar-12": { + "x": 676, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "swimming-12": { + "x": 702, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "logging-12": { + "x": 728, + "y": 0, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "circle-stroked-12": { + "x": 728, + "y": 26, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "telephone-12": { + "x": 728, + "y": 52, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "pharmacy-12": { + "x": 728, + "y": 78, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "bank-12": { + "x": 728, + "y": 104, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "tennis-12": { + "x": 728, + "y": 130, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "entrance-12": { + "x": 728, + "y": 156, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "cross-12": { + "x": 728, + "y": 182, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "theatre-12": { + "x": 728, + "y": 208, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "pitch-12": { + "x": 728, + "y": 234, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "bakery-12": { + "x": 728, + "y": 260, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "toilets-12": { + "x": 728, + "y": 286, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "london-underground-12": { + "x": 728, + "y": 312, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "circle-12": { + "x": 728, + "y": 338, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "town-12": { + "x": 728, + "y": 364, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "place-of-worship-12": { + "x": 728, + "y": 390, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "art-gallery-12": { + "x": 728, + "y": 416, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "town-hall-12": { + "x": 728, + "y": 442, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "golf-12": { + "x": 728, + "y": 468, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "heliport-12": { + "x": 728, + "y": 494, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "triangle-12": { + "x": 728, + "y": 520, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "playground-12": { + "x": 728, + "y": 546, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "america-football-12": { + "x": 728, + "y": 572, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "triangle-stroked-12": { + "x": 728, + "y": 598, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "marker-12": { + "x": 728, + "y": 624, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "cinema-12": { + "x": 728, + "y": 650, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "village-12": { + "x": 728, + "y": 676, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "police-12": { + "x": 728, + "y": 702, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "alcohol-shop-12": { + "x": 0, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "warehouse-12": { + "x": 26, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "farm-12": { + "x": 52, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "dog-park-12": { + "x": 78, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "waste-basket-12": { + "x": 104, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "polling-place-12": { + "x": 130, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "airport-12": { + "x": 156, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "water-12": { + "x": 182, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "marker-stroked-12": { + "x": 208, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "fast-food-12": { + "x": 234, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "post-12": { + "x": 260, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "wetland-12": { + "x": 286, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "hospital-12": { + "x": 312, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "airfield-12": { + "x": 338, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "zoo-12": { + "x": 364, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "chemist-12": { + "x": 390, + "y": 728, + "width": 24, + "height": 24, + "pixelRatio": 2, + "sdf": false + }, + "wave": { + "x": 664, + "y": 646, + "width": 32, + "height": 16, + "pixelRatio": 2, + "sdf": false + } +}
\ No newline at end of file diff --git a/styles/bright/img/sprite@2x.png b/styles/bright/img/sprite@2x.png Binary files differnew file mode 100644 index 0000000000..ef7c1f45d1 --- /dev/null +++ b/styles/bright/img/sprite@2x.png diff --git a/styles/bright/style.json b/styles/bright/style.json new file mode 100644 index 0000000000..1ee81a499b --- /dev/null +++ b/styles/bright/style.json @@ -0,0 +1,1336 @@ +{ + "version": 4, + "sprite": "img/sprite", + "glyphs": "https://mapbox.s3.amazonaws.com/gl-glyphs-256/{fontstack}/{range}.pbf", + "constants": { + "@name": "{name_en}", + "@sans": "Open Sans Regular, Arial Unicode MS Regular", + "@sans_it": "Open Sans Italic, Arial Unicode MS Regular", + "@sans_md": "Open Sans Semibold, Arial Unicode MS Bold", + "@sans_bd": "Open Sans Bold, Arial Unicode MS Bold", + "@land": "#f8f4f0", + "@water": "#a0c8f0", + "@admin": "#446", + "@park": "#d8e8c8", + "@cemetary": "#e0e4dd", + "@hospital": "#fde", + "@school": "#f0e8f8", + "@wood": "#6a4", + "@building": "#f2eae2", + "@building_shadow": "#dfdbd7", + "@building_color_transition": { + "base": 1, + "stops": [[15.5, "#f2eae2"], [16, "#dfdbd7"]] + }, + "@building_opacity": {"base": 1, "stops": [[15, 0], [16, 1]]}, + "@aeroway": "#f0ede9", + "@motorway": "#fc8", + "@motorway_casing": "#e9ac77", + "@motorway_tunnel": "#ffdaa6", + "@main": "#fea", + "@main_tunnel": "#fff4c6", + "@street": "#fff", + "@street_limited": "#f3f3f3", + "@street_casing": "#cfcdca", + "@path": "#cba", + "@rail": "#bbb", + "@text": "#334", + "@text_halo": "rgba(255,255,255,0.8)", + "@marine_text": "#74aee9", + "@marine_text_halo": "rgba(255,255,255,0.7)", + "@poi_text": "#666", + "@poi_text_halo": "#ffffff", + "@maki": "#666", + "@point_translate": [0, -30], + "@tunnel_line_dasharray": [{ + "base": 1.5, "stops": [[12, 6], [20, 9]] + }, { + "base": 1.5, "stops": [[12, 2], [20, 3]] + }], + "@motorway_width": {"base": 1.2, "stops": [[6.5, 0], [7, 0.5], [20, 18]]}, + "@motorway_casing_width": {"base": 1.2, "stops": [[5, 0.4], [6, 0.6], [7, 1.5], [20, 22]]}, + "@motorway_link_width": {"base": 1.2, "stops": [[12.5, 0], [13, 1.5], [20, 10]]}, + "@motorway_link_casing_width": {"base": 1.2, "stops": [[12, 1], [13, 3], [20, 13]]}, + "@main_width": {"base": 1.2, "stops": [[6.5, 0], [7, 0.5], [20, 14]]}, + "@main_casing_width": {"base": 1.2, "stops": [[5, 0.1], [6, 0.2], [7, 1.5], [20, 18]]}, + "@street_width": {"base": 1.2, "stops": [[13.5, 0], [14, 2.5], [20, 11.5]]}, + "@street_casing_width": {"base": 1.2, "stops": [[12, 0.5], [13, 1], [14, 4], [20, 15]]}, + "@street_casing_opacity": {"stops": [[12, 0], [12.5, 1]]}, + "@service_casing_width": {"base": 1.2, "stops": [[15, 1], [16, 4], [20, 11]]}, + "@service_width": {"base": 1.2, "stops": [[15.5, 0], [16, 2], [20, 7.5]]}, + "@path_width": {"base": 1.2, "stops": [[15, 1.2], [20, 4]]}, + "@path_line_dasharray": [{ + "base": 1.2, "stops": [[15, 2], [20, 4]] + },{ + "base": 1.2, "stops": [[15, 1], [20, 2]] + }], + "@rail_width": {"base": 1.4, "stops": [[14, 0.4], [15, 0.75], [20, 2]]}, + "@rail_hatch_width": {"base": 1.4, "stops": [[14.5, 0], [15, 3], [20, 8]]}, + "@rail_hatch_line_dasharray": [2, 30], + "@admin_level_3_width": { + "base": 1, + "stops": [[4, 0.4], [5, 1], [12, 3]] + }, + "@admin_level_2_width": { + "base": 1, + "stops": [[4, 1.4], [5, 2], [12, 8]] + } + }, + "sources": { + "mapbox": { + "type": "vector", + "url": "mapbox://mapbox.mapbox-streets-v6-dev", + "maxZoom": 15 + } + }, + "layers": [{ + "id": "background", + "style": { + "background-color": "@land" + }, + "type": "background" + }, { + "id": "landuse_park", + "source": "mapbox", + "source-layer": "landuse", + "filter": { "class": "park" }, + "style": { + "fill-color": "@park" + }, + "type": "fill" + }, { + "id": "landuse_cemetary", + "source": "mapbox", + "source-layer": "landuse", + "filter": { "class": "cemetary" }, + "style": { + "fill-color": "@cemetary" + }, + "type": "fill" + }, { + "id": "landuse_hospital", + "source": "mapbox", + "source-layer": "landuse", + "filter": { "class": "hospital" }, + "style": { + "fill-color": "@hospital" + }, + "type": "fill" + }, { + "id": "landuse_school", + "source": "mapbox", + "source-layer": "landuse", + "filter": { "class": "school" }, + "style": { + "fill-color": "@school" + }, + "type": "fill" + }, { + "id": "landuse_wood", + "source": "mapbox", + "source-layer": "landuse", + "filter": { "class": "wood" }, + "style": { + "fill-color": "@wood", + "fill-opacity": 0.1 + }, + "type": "fill" + }, { + "id": "waterway", + "source": "mapbox", + "source-layer": "waterway", + "filter": { + "class": { + "!=": ["river", "stream", "canal"] + } + }, + "render": { + "line-cap": "round" + }, + "style": { + "line-color": "@water", + "line-width": { + "base": 1.3, + "stops": [[13, 0.5], [20, 2]] + } + }, + "type": "line" + }, { + "id": "waterway_river", + "source": "mapbox", + "source-layer": "waterway", + "filter": { "class": "river" }, + "render": { + "line-cap": "round" + }, + "style": { + "line-color": "@water", + "line-width": { + "base": 1.2, + "stops": [[11, 0.5], [20, 6]] + } + }, + "type": "line" + }, { + "id": "waterway_stream_canal", + "source": "mapbox", + "source-layer": "waterway", + "filter": { "class": ["stream", "canal"] }, + "render": { + "line-cap": "round" + }, + "style": { + "line-color": "@water", + "line-width": { + "base": 1.3, + "stops": [[13, 0.5], [20, 6]] + } + }, + "type": "line" + }, { + "id": "water", + "source": "mapbox", + "source-layer": "water", + "style": { + "fill-color": "@water" + }, + "type": "fill" + }, { + "id": "water_offset", + "source": "mapbox", + "source-layer": "water", + "type": "fill", + "style": { + "fill-color": "white", + "fill-opacity": 0.3, + "fill-translate": [0, 2.5] + } + }, { + "id": "water_pattern", + "ref": "water", + "style": { + "fill-image": "wave", + "fill-translate": [0, 2.5] + } + }, { + "id": "aeroway_fill", + "source": "mapbox", + "source-layer": "aeroway", + "min-zoom": 11, + "filter": { + "$type": "Polygon" + }, + "style": { + "fill-color": "@aeroway", + "fill-opacity": 0.7 + }, + "type": "fill" + }, { + "id": "aeroway_runway", + "source": "mapbox", + "source-layer": "aeroway", + "min-zoom": 11, + "filter": { + "$type": "LineString", + "type": "runway" }, + "style": { + "line-color": "@aeroway", + "line-width": { + "base": 1.2, + "stops": [[11, 3], [20, 16]] + } + }, + "type": "line" + }, { + "id": "aeroway_taxiway", + "source": "mapbox", + "source-layer": "aeroway", + "min-zoom": 11, + "filter": { + "$type": "LineString", + "type": "taxiway" }, + "style": { + "line-color": "@aeroway", + "line-width": { + "base": 1.2, + "stops": [[11, 0.5], [20, 6]] + } + }, + "type": "line" + }, { + "id": "building", + "source": "mapbox", + "source-layer": "building", + "style": { + "fill-color": "@building_color_transition" + }, + "type": "fill" + }, { + "id": "building_top", + "ref": "building", + "style": { + "fill-color": "@building", + "fill-opacity": "@building_opacity", + "fill-translate": [{ + "base": 1, + "stops": [[15, 0], [16, -2] ] + }, { + "base": 1, + "stops": [[15, 0], [16, -2] ] + }], + "fill-outline-color": "@building_shadow" + } + }, { + "id": "tunnel_motorway_link_casing", + "source": "mapbox", + "source-layer": "tunnel", + "filter": { "class": "motorway_link" }, + "render": { + "line-cap": "round", + "line-join": "round" + }, + "style": { + "line-color": "@motorway_casing", + "line-dasharray": "@tunnel_line_dasharray", + "line-width": "@motorway_link_casing_width" + }, + "type": "line" + }, { + "id": "tunnel_service_casing", + "source": "mapbox", + "source-layer": "tunnel", + "filter": { "class": "service" }, + "render": { + "line-cap": "round", + "line-join": "round" + }, + "style": { + "line-color": "@street_casing", + "line-dasharray": "@tunnel_line_dasharray", + "line-width": "@service_casing_width" + }, + "type": "line" + }, { + "id": "tunnel_street_casing", + "source": "mapbox", + "source-layer": "tunnel", + "filter": { "class": ["street", "street_limited"] }, + "render": { + "line-join": "round" + }, + "style": { + "line-color": "@street_casing", + "line-dasharray": [7, 2], + "line-width": "@street_casing_width", + "line-opacity": "@street_casing_opacity" + }, + "type": "line" + }, { + "id": "tunnel_main_casing", + "source": "mapbox", + "source-layer": "tunnel", + "filter": { "class": "main" }, + "render": { + "line-cap": "round", + "line-join": "round" + }, + "style": { + "line-color": "@motorway_casing", + "line-dasharray": "@tunnel_line_dasharray", + "line-width": "@main_casing_width" + }, + "type": "line" + }, { + "id": "tunnel_motorway_casing", + "source": "mapbox", + "source-layer": "tunnel", + "filter": { "class": "motorway" }, + "render": { + "line-cap": "round", + "line-join": "round" + }, + "style": { + "line-color": "@motorway_casing", + "line-dasharray": "@tunnel_line_dasharray", + "line-width": "@motorway_casing_width" + }, + "type": "line" + }, { + "id": "tunnel_path", + "source": "mapbox", + "source-layer": "tunnel", + "filter": { "class": "path" }, + "type": "line", + "style": { + "line-color": "@path", + "line-dasharray": "@path_line_dasharray", + "line-width": "@path_width" + } + }, { + "id": "tunnel_motorway_link", + "ref": "tunnel_motorway_link_casing", + "style": { + "line-color": "@motorway", + "line-width": "@motorway_link_width" + } + }, { + "id": "tunnel_service", + "ref": "tunnel_service_casing", + "style": { + "line-color": "@street", + "line-width": "@service_width" + } + }, { + "id": "tunnel_street", + "ref": "tunnel_street_casing", + "style": { + "line-color": "@street", + "line-width": "@street_width" + } + }, { + "id": "tunnel_main", + "ref": "tunnel_main_casing", + "style": { + "line-color": "@main_tunnel", + "line-width": "@main_width" + } + }, { + "id": "tunnel_motorway", + "ref": "tunnel_motorway_casing", + "style": { + "line-color": "@motorway_tunnel", + "line-width": "@motorway_width" + } + }, { + "id": "tunnel_major_rail", + "source": "mapbox", + "source-layer": "tunnel", + "filter": { "class": "major_rail" }, + "style": { + "line-color": "@rail", + "line-width": "@rail_width" + }, + "type": "line" + }, { + "id": "tunnel_major_rail_hatching", + "ref": "tunnel_major_rail", + "style": { + "line-color": "@rail", + "line-dasharray": "@rail_hatch_line_dasharray", + "line-width": "@rail_hatch_width" + } + }, { + "id": "road_motorway_link_casing", + "source": "mapbox", + "source-layer": "road", + "min-zoom": 12, + "filter": { "class": "motorway_link" }, + "render": { + "line-cap": "round", + "line-join": "round" + }, + "style": { + "line-color": "@motorway_casing", + "line-width": "@motorway_link_casing_width" + }, + "type": "line" + }, { + "id": "road_service_casing", + "source": "mapbox", + "source-layer": "road", + "filter": { "class": "service" }, + "render": { + "line-cap": "round", + "line-join": "round" + }, + "style": { + "line-color": "@street_casing", + "line-width": "@service_casing_width" + }, + "type": "line" + }, { + "id": "road_street_casing", + "source": "mapbox", + "source-layer": "road", + "filter": { "class": ["street", "street_limited"], "$type": "LineString" }, + "render": { + "line-cap": "round", + "line-join": "round" + }, + "style": { + "line-color": "@street_casing", + "line-width": "@street_casing_width", + "line-opacity": "@street_casing_opacity" + }, + "type": "line" + }, { + "id": "road_main_casing", + "source": "mapbox", + "source-layer": "road", + "filter": { "class": "main" }, + "render": { + "line-cap": "round", + "line-join": "round" + }, + "style": { + "line-color": "@motorway_casing", + "line-width": "@main_casing_width" + }, + "type": "line" + }, { + "id": "road_motorway_casing", + "source": "mapbox", + "source-layer": "road", + "min-zoom": 5, + "filter": { "class": "motorway" }, + "render": { + "line-cap": "round", + "line-join": "round" + }, + "style": { + "line-color": "@motorway_casing", + "line-width": "@motorway_casing_width" + }, + "type": "line" + }, { + "id": "road_path", + "source": "mapbox", + "source-layer": "road", + "filter": { "class": "path" }, + "style": { + "line-color": "@path", + "line-dasharray": "@path_line_dasharray", + "line-width": "@path_width" + }, + "type": "line" + }, { + "id": "road_motorway_link", + "ref": "road_motorway_link_casing", + "style": { + "line-color": "@motorway", + "line-width": "@motorway_link_width" + } + }, { + "id": "road_service", + "ref": "road_service_casing", + "style": { + "line-color": "@street", + "line-width": "@service_width" + } + }, { + "id": "road_street", + "ref": "road_street_casing", + "style": { + "line-color": "@street", + "line-width": "@street_width" + } + }, { + "id": "road_main", + "ref": "road_main_casing", + "style": { + "line-color": "@main", + "line-width": "@main_width" + } + }, { + "id": "road_motorway", + "ref": "road_motorway_casing", + "style": { + "line-color": "@motorway", + "line-width": "@motorway_width" + }, + "type": "line" + }, { + "id": "road_major_rail", + "source": "mapbox", + "source-layer": "road", + "filter": { "class": "major_rail" }, + "style": { + "line-color": "@rail", + "line-width": "@rail_width" + }, + "type": "line" + }, { + "id": "road_major_rail_hatching", + "ref": "road_major_rail", + "style": { + "line-color": "@rail", + "line-dasharray": "@rail_hatch_line_dasharray", + "line-width": "@rail_hatch_width" + } + }, { + "id": "bridge_motorway_link_casing", + "source": "mapbox", + "source-layer": "bridge", + "filter": { "class": "motorway_link" }, + "render": { + "line-join": "round" + }, + "style": { + "line-color": "@motorway_casing", + "line-width": "@motorway_link_casing_width" + }, + "type": "line" + }, { + "id": "bridge_motorway_casing", + "source": "mapbox", + "source-layer": "bridge", + "filter": { "class": "motorway" }, + "render": { + "line-join": "round" + }, + "style": { + "line-color": "@motorway_casing", + "line-width": "@motorway_casing_width" + }, + "type": "line" + }, { + "id": "bridge_service_casing", + "source": "mapbox", + "source-layer": "bridge", + "filter": { "class": "service" }, + "render": { + "line-join": "round" + }, + "style": { + "line-color": "@street_casing", + "line-width": "@service_casing_width" + }, + "type": "line" + }, { + "id": "bridge_street_casing", + "source": "mapbox", + "source-layer": "bridge", + "filter": { "class": ["street", "street_limited"] }, + "render": { + "line-join": "round" + }, + "style": { + "line-color": "@street_casing", + "line-width": "@street_casing_width", + "line-opacity": "@street_casing_opacity" + }, + "type": "line" + }, { + "id": "bridge_main_casing", + "source": "mapbox", + "source-layer": "bridge", + "filter": { "class": "main" }, + "render": { + "line-join": "round" + }, + "style": { + "line-color": "@motorway_casing", + "line-width": "@main_casing_width" + }, + "type": "line" + }, { + "id": "bridge_path", + "source": "mapbox", + "source-layer": "bridge", + "filter": { "class": "path" }, + "style": { + "line-color": "@path", + "line-dasharray": "@path_line_dasharray", + "line-width": "@path_width" + }, + "type": "line" + }, { + "id": "bridge_motorway_link", + "ref": "bridge_motorway_link_casing", + "style": { + "line-color": "@motorway", + "line-width": "@motorway_link_width" + } + }, { + "id": "bridge_service", + "ref": "bridge_service_casing", + "style": { + "line-color": "@street", + "line-width": "@service_width" + } + }, { + "id": "bridge_street", + "ref": "bridge_street_casing", + "style": { + "line-color": "@street", + "line-width": "@street_width" + } + }, { + "id": "bridge_main", + "ref": "bridge_main_casing", + "style": { + "line-color": "@main", + "line-width": "@main_width" + } + }, { + "id": "bridge_motorway", + "ref": "bridge_motorway_casing", + "style": { + "line-color": "@motorway", + "line-width": "@motorway_width" + } + }, { + "id": "bridge_major_rail", + "source": "mapbox", + "source-layer": "bridge", + "filter": { "class": "major_rail" }, + "style": { + "line-color": "@rail", + "line-width": "@rail_width" + }, + "type": "line" + }, { + "id": "bridge_major_rail_hatching", + "ref": "bridge_major_rail", + "style": { + "line-color": "@rail", + "line-dasharray": "@rail_hatch_line_dasharray", + "line-width": "@rail_hatch_width" + } + }, { + "id": "admin", + "type": "composite", + "style": { + "composite-opacity": 0.5 + }, + "layers": [{ + "id": "admin_level_3", + "source": "mapbox", + "source-layer": "admin", + "filter": { + "admin_level": {">=": 3}, + "maritime": 0 + }, + "render": { + "line-join": "round" + }, + "style": { + "line-color": "@admin", + "line-dasharray": [10, 3], + "line-width": "@admin_level_3_width" + }, + "type": "line" + }, { + "id": "admin_level_2", + "source": "mapbox", + "source-layer": "admin", + "filter": { + "admin_level": 2, + "disputed": 0, + "maritime": 0 + }, + "render": { + "line-cap": "round" + }, + "style": { + "line-color": "@admin", + "line-width": "@admin_level_2_width" + }, + "type": "line" + }, { + "id": "admin_level_2_disputed", + "source": "mapbox", + "source-layer": "admin", + "filter": { + "admin_level": 2, + "disputed": 1, + "maritime": 0 + }, + "render": { + "line-cap": "round" + }, + "style": { + "line-color": "@admin", + "line-dasharray": [4, 4], + "line-width": "@admin_level_2_width" + }, + "type": "line" + }, { + "id": "admin_level_3_maritime", + "source": "mapbox", + "source-layer": "admin", + "filter": { + "admin_level": {">=": 3}, + "maritime": 1 + }, + "render": { + "line-join": "round" + }, + "style": { + "line-color": "@water", + "line-dasharray": [10, 3], + "line-width": "@admin_level_3_width" + }, + "type": "line" + }, { + "id": "admin_level_2_maritime", + "source": "mapbox", + "source-layer": "admin", + "filter": { + "admin_level": 2, + "maritime": 1 + }, + "render": { + "line-cap": "round" + }, + "style": { + "line-color": "@water", + "line-width": "@admin_level_2_width" + }, + "type": "line" + }] + }, { + "id": "country_label_line", + "source": "mapbox", + "source-layer": "country_label_line", + "style": { + "line-color": "@text", + "line-opacity": 0.5 + }, + "type": "line" + }, { + "id": "country_label_1", + "source": "mapbox", + "source-layer": "country_label", + "filter": { + "scalerank": 1 + }, + "render": { + "text-font": "@sans_bd", + "text-field": "@name", + "text-max-width": 6.25, + "text-transform": "uppercase", + "text-max-size": 17 + }, + "style": { + "text-color": "@text", + "text-halo-color": "@text_halo", + "text-halo-width": 2, + "text-halo-blur": 1, + "text-size": { + "stops": [[2, 13], [4, 17]] + } + }, + "type": "symbol" + }, { + "id": "country_label_2", + "source": "mapbox", + "source-layer": "country_label", + "filter": { + "scalerank": 2 + }, + "render": { + "text-font": "@sans_bd", + "text-field": "{name_en}", + "text-max-width": 6.25, + "text-transform": "uppercase", + "text-max-size": 17 + }, + "style": { + "text-color": "@text", + "text-halo-color": "@text_halo", + "text-halo-width": 2, + "text-halo-blur": 1, + "text-size": { + "stops": [[3, 13], [5, 17]] + } + }, + "type": "symbol" + }, { + "id": "country_label_3", + "source": "mapbox", + "source-layer": "country_label", + "filter": { + "scalerank": 3 + }, + "render": { + "text-font": "@sans_bd", + "text-field": "@name", + "text-max-width": 6.25, + "text-transform": "uppercase", + "text-max-size": 17 + }, + "style": { + "text-color": "@text", + "text-halo-color": "@text_halo", + "text-halo-width": 2, + "text-halo-blur": 1, + "text-size": { + "stops": [[4, 13], [7, 17]] + } + }, + "type": "symbol" + }, { + "id": "country_label_4", + "source": "mapbox", + "source-layer": "country_label", + "filter": { + "scalerank": {">=": 4} + }, + "render": { + "text-font": "@sans_bd", + "text-field": "@name", + "text-max-width": 6.25, + "text-transform": "uppercase", + "text-max-size": 15 + }, + "style": { + "text-color": "@text", + "text-halo-color": "@text_halo", + "text-halo-width": 2, + "text-halo-blur": 1, + "text-size": { + "stops": [[5, 13], [6, 15]] + } + }, + "type": "symbol" + }, { + "id": "marine_label_point_1", + "source": "mapbox", + "source-layer": "marine_label", + "filter": {"labelrank": 1, "$type": "Point"}, + "render": { + "text-font": "@sans_it", + "text-field": "@name", + "text-max-size": 22, + "text-max-width": 5, + "text-letter-spacing": 0.2, + "text-line-height": 1.6, + "symbol-placement": "point", + "text-offset": [0, 2.4] + }, + "style": { + "text-color": "@marine_text", + "text-halo-color": "@marine_text_halo", + "text-halo-width": 0.75, + "text-halo-blur": 0.75, + "text-size": { + "stops": [[3, 18], [4, 22]] + } + }, + "type": "symbol" + }, { + "id": "marine_label_line_1", + "source": "mapbox", + "source-layer": "marine_label", + "filter": {"labelrank": 1, "$type": "LineString"}, + "render": { + "text-font": "@sans_it", + "text-field": "@name", + "text-max-size": 14, + "text-letter-spacing": 0.2, + "symbol-placement": "line" + }, + "style": { + "text-color": "@marine_text", + "text-halo-color": "@marine_text_halo", + "text-halo-width": 0.75, + "text-halo-blur": 0.75, + "text-size": { + "stops": [[3, 18], [4, 22]] + } + }, + "type": "symbol" + }, { + "id": "marine_label_point_2", + "source": "mapbox", + "source-layer": "marine_label", + "filter": {"labelrank": 2, "$type": "Point"}, + "render": { + "text-font": "@sans_it", + "text-field": "@name", + "text-max-size": 16, + "text-max-width": 5, + "text-letter-spacing": 0.2, + "symbol-placement": "point" + }, + "style": { + "text-color": "@marine_text", + "text-halo-color": "@marine_text_halo", + "text-halo-width": 0.75, + "text-halo-blur": 0.75, + "text-size": { + "stops": [[3, 14], [4, 16]] + } + }, + "type": "symbol" + }, { + "id": "marine_label_line_2", + "source": "mapbox", + "source-layer": "marine_label", + "filter": {"labelrank": 2, "$type": "LineString"}, + "render": { + "text-font": "@sans_it", + "text-field": "@name", + "text-max-size": 16, + "text-letter-spacing": 0.2, + "symbol-placement": "line" + }, + "style": { + "text-color": "@marine_text", + "text-halo-color": "@marine_text_halo", + "text-halo-width": 0.75, + "text-halo-blur": 0.75, + "text-size": { + "stops": [[3, 14], [4, 16]] + } + }, + "type": "symbol" + }, { + "id": "marine_label_3", + "source": "mapbox", + "source-layer": "marine_label", + "filter": {"labelrank": 3, "$type": "Point"}, + "render": { + "text-font": "@sans_it", + "text-field": "@name", + "text-max-size": 14, + "text-max-width": 5, + "text-letter-spacing": 0.2, + "symbol-placement": "point" + }, + "style": { + "text-color": "@marine_text", + "text-halo-color": "@marine_text_halo", + "text-halo-width": 0.75, + "text-halo-blur": 0.75, + "text-size": { + "stops": [[3, 11], [4, 14]] + } + }, + "type": "symbol" + }, { + "id": "marine_label_line_3", + "source": "mapbox", + "source-layer": "marine_label", + "filter": {"labelrank": 3, "$type": "LineString"}, + "render": { + "text-font": "@sans_it", + "text-field": "@name", + "text-max-size": 14, + "text-letter-spacing": 0.2, + "symbol-placement": "line" + }, + "style": { + "text-color": "@marine_text", + "text-halo-color": "@marine_text_halo", + "text-halo-width": 0.75, + "text-halo-blur": 0.75, + "text-size": { + "stops": [[3, 11], [4, 14]] + } + }, + "type": "symbol" + }, { + "id": "marine_label_4", + "source": "mapbox", + "source-layer": "marine_label", + "filter": {"labelrank": {">=": 4}, "$type": "Point"}, + "render": { + "text-font": "@sans_it", + "text-field": "@name", + "text-max-size": 12, + "text-max-width": 6, + "text-letter-spacing": 0.2, + "symbol-placement": "point" + }, + "style": { + "text-color": "@marine_text", + "text-halo-color": "@marine_text_halo", + "text-halo-width": 0.75, + "text-halo-blur": 0.75, + "text-size": { + "stops": [[3, 11], [4, 12]] + } + }, + "type": "symbol" + }, { + "id": "marine_label_line_4", + "source": "mapbox", + "source-layer": "marine_label", + "filter": {"labelrank": {">=": 4}, "$type": "LineString"}, + "render": { + "text-font": "@sans_it", + "text-field": "@name", + "text-max-size": 12, + "text-letter-spacing": 0.2, + "symbol-placement": "line" + }, + "style": { + "text-color": "@marine_text", + "text-halo-color": "@marine_text_halo", + "text-halo-width": 0.75, + "text-halo-blur": 0.75, + "text-size": { + "stops": [[3, 11], [4, 12]] + } + }, + "type": "symbol" + }, { + "id": "place_label_city", + "source": "mapbox", + "source-layer": "place_label", + "filter": { "type": "city" }, + "render": { + "text-font": "@sans_md", + "text-field": "@name", + "text-max-size": 24, + "text-max-width": 8 + }, + "style": { + "text-color": "#333", + "text-halo-color": "@text_halo", + "text-halo-width": 1.2, + "text-size": { + "base": 1.2, + "stops": [[7, 14], [11, 24]] + } + }, + "type": "symbol" + }, { + "id": "place_label_town", + "source": "mapbox", + "source-layer": "place_label", + "filter": { "type": "town" }, + "render": { + "text-font": "@sans", + "text-field": "@name", + "text-max-size": 24, + "text-max-width": 8 + }, + "style": { + "text-color": "#333", + "text-halo-color": "@text_halo", + "text-halo-width": 1.2, + "text-size": { + "base": 1.2, + "stops": [[10, 14], [15, 24]] + } + }, + "type": "symbol" + }, { + "id": "place_label_village", + "source": "mapbox", + "source-layer": "place_label", + "filter": { "type": "village" }, + "render": { + "text-font": "@sans", + "text-field": "@name", + "text-max-size": 22, + "text-max-width": 8 + }, + "style": { + "text-color": "#333", + "text-halo-color": "@text_halo", + "text-halo-width": 1.2, + "text-size": { + "base": 1.2, + "stops": [[10, 12], [15, 22]] + } + }, + "type": "symbol" + }, { + "id": "road_label_highway_shields", + "source": "mapbox", + "source-layer": "road_label", + "filter": {"class": "motorway", "reflen": [1,2,3,4,5,6]}, + "render": { + "symbol-placement": "line", + "symbol-min-distance": 500, + "icon-image": "motorway_{reflen}", + "icon-max-size": 1, + "text-field": "{ref}", + "text-font": "@sans_bd", + "text-max-size": 11, + "text-rotation-alignment": "viewport", + "icon-rotation-alignment": "viewport" + }, + "style": { + "text-color": "#765", + "text-size": 11 + }, + "type": "symbol" + }, { + "id": "road_label", + "source": "mapbox", + "source-layer": "road_label", + "filter": { "$type": "LineString" }, + "render": { + "text-font": "@sans", + "text-field": "@name", + "text-max-size": 13, + "symbol-placement": "line" + }, + "style": { + "text-color": "#765", + "text-halo-color": "#fff", + "text-halo-width": 1, + "text-halo-blur": 0.5, + "text-size": { + "stops": [[13, 12], [14, 13]] + } + }, + "type": "symbol" + }, { + "id": "place_label_other", + "source": "mapbox", + "source-layer": "place_label", + "filter": { "type": ["hamlet", "suburb", "neighbourhood"] }, + "render": { + "text-font": "@sans_bd", + "text-transform": "uppercase", + "text-letter-spacing": 0.1, + "text-field": "@name", + "text-max-size": 14, + "text-max-width": 9 + }, + "style": { + "text-color": "#633", + "text-halo-color": "@text_halo", + "text-halo-width": 1.2, + "text-size": { + "base": 1.2, + "stops": [[12, 10], [15, 14]] + } + }, + "type": "symbol" + }, { + "id": "poi_label_1", + "source": "mapbox", + "source-layer": "poi_label", + "filter": { "$type": "Point", "scalerank": 1 }, + "render": { + "icon-image": "{maki}-12", + "text-font": "@sans_md", + "text-field": "@name", + "text-max-size": 12, + "text-max-width": 9, + "text-padding": 2, + "text-offset": [0, 0.6], + "text-vertical-align": "top" + }, + "style": { + "text-color": "@poi_text", + "text-size": 12, + "text-halo-color": "@poi_text_halo", + "text-halo-width": 1, + "text-halo-blur": 0.5 + }, + "min-zoom": 13, + "type": "symbol" + }, { + "id": "poi_label_2", + "source": "mapbox", + "source-layer": "poi_label", + "filter": { "$type": "Point", "scalerank": 2 }, + "render": { + "icon-image": "{maki}-12", + "text-font": "@sans_md", + "text-field": "@name", + "text-max-size": 12, + "text-max-width": 9, + "text-padding": 2, + "text-offset": [0, 0.6], + "text-vertical-align": "top" + }, + "style": { + "text-color": "@poi_text", + "text-size": 12, + "text-halo-color": "@poi_text_halo", + "text-halo-width": 1, + "text-halo-blur": 0.5 + }, + "min-zoom": 14, + "type": "symbol" + }, { + "id": "poi_label_3", + "source": "mapbox", + "source-layer": "poi_label", + "filter": { "$type": "Point", "scalerank": 3 }, + "render": { + "icon-image": "{maki}-12", + "text-font": "@sans_md", + "text-field": "@name", + "text-max-size": 12, + "text-max-width": 9, + "text-padding": 2, + "text-offset": [0, 0.6], + "text-vertical-align": "top" + }, + "style": { + "text-color": "@poi_text", + "text-size": 12, + "text-halo-color": "@poi_text_halo", + "text-halo-width": 1, + "text-halo-blur": 0.5 + }, + "min-zoom": 15, + "type": "symbol" + }, { + "id": "poi_label_4", + "source": "mapbox", + "source-layer": "poi_label", + "filter": { "$type": "Point", "scalerank": 4 }, + "render": { + "icon-image": "{maki}-12", + "text-font": "@sans_md", + "text-field": "@name", + "text-max-size": 12, + "text-max-width": 9, + "text-padding": 2, + "text-offset": [0, 0.6], + "text-vertical-align": "top" + }, + "style": { + "text-color": "@poi_text", + "text-size": 12, + "text-halo-color": "@poi_text_halo", + "text-halo-width": 1, + "text-halo-blur": 0.5 + }, + "min-zoom": 16, + "type": "symbol" + }, { + "id": "poi_label_other", + "source": "mapbox", + "source-layer": "poi_label", + "filter": { "$type": "Point", "scalerank": {">=": 5} }, + "render": { + "icon-image": "{maki}-12", + "text-font": "@sans_md", + "text-field": "@name", + "text-max-size": 12, + "text-max-width": 9, + "text-padding": 2, + "text-offset": [0, 0.6], + "text-vertical-align": "top" + }, + "style": { + "text-color": "@poi_text", + "text-size": 12, + "text-halo-color": "@poi_text_halo", + "text-halo-width": 1, + "text-halo-blur": 0.5 + }, + "min-zoom": 17, + "type": "symbol" + }, { + "id": "water_label", + "source": "mapbox", + "source-layer": "water_label", + "filter": { "$type": "Point" }, + "render": { + "text-font": "@sans_it", + "text-field": "@name", + "text-max-size": 12, + "text-max-width": 5 + }, + "style": { + "text-size": 12, + "text-color": "@marine_text", + "text-halo-width": 1.5, + "text-halo-color": "@marine_text_halo" + }, + "type": "symbol" + }] +}
\ No newline at end of file diff --git a/test/enums.cpp b/test/enums.cpp index e959994a57..f909631e3e 100644 --- a/test/enums.cpp +++ b/test/enums.cpp @@ -13,8 +13,7 @@ TEST(Enums, StyleLayerType) { ASSERT_EQ(StyleLayerType::Unknown, StyleLayerTypeClass("unknown")); ASSERT_EQ(StyleLayerType::Fill, StyleLayerTypeClass("fill")); ASSERT_EQ(StyleLayerType::Line, StyleLayerTypeClass("line")); - ASSERT_EQ(StyleLayerType::Icon, StyleLayerTypeClass("icon")); - ASSERT_EQ(StyleLayerType::Text, StyleLayerTypeClass("text")); + ASSERT_EQ(StyleLayerType::Symbol, StyleLayerTypeClass("symbol")); ASSERT_EQ(StyleLayerType::Raster, StyleLayerTypeClass("raster")); ASSERT_EQ(StyleLayerType::Composite, StyleLayerTypeClass("composite")); ASSERT_EQ(StyleLayerType::Background, StyleLayerTypeClass("background")); @@ -22,8 +21,7 @@ TEST(Enums, StyleLayerType) { ASSERT_EQ(StyleLayerType::Unknown, StyleLayerTypeClass(StyleLayerType::Unknown)); ASSERT_EQ(StyleLayerType::Fill, StyleLayerTypeClass(StyleLayerType::Fill)); ASSERT_EQ(StyleLayerType::Line, StyleLayerTypeClass(StyleLayerType::Line)); - ASSERT_EQ(StyleLayerType::Icon, StyleLayerTypeClass(StyleLayerType::Icon)); - ASSERT_EQ(StyleLayerType::Text, StyleLayerTypeClass(StyleLayerType::Text)); + ASSERT_EQ(StyleLayerType::Symbol, StyleLayerTypeClass(StyleLayerType::Symbol)); ASSERT_EQ(StyleLayerType::Raster, StyleLayerTypeClass(StyleLayerType::Raster)); ASSERT_EQ(StyleLayerType::Composite, StyleLayerTypeClass(StyleLayerType::Composite)); ASSERT_EQ(StyleLayerType::Background, StyleLayerTypeClass(StyleLayerType::Background)); @@ -31,8 +29,7 @@ TEST(Enums, StyleLayerType) { ASSERT_EQ(StyleLayerTypeClass(StyleLayerType::Unknown), StyleLayerTypeClass(StyleLayerType::Unknown)); ASSERT_EQ(StyleLayerTypeClass(StyleLayerType::Fill), StyleLayerTypeClass(StyleLayerType::Fill)); ASSERT_EQ(StyleLayerTypeClass(StyleLayerType::Line), StyleLayerTypeClass(StyleLayerType::Line)); - ASSERT_EQ(StyleLayerTypeClass(StyleLayerType::Icon), StyleLayerTypeClass(StyleLayerType::Icon)); - ASSERT_EQ(StyleLayerTypeClass(StyleLayerType::Text), StyleLayerTypeClass(StyleLayerType::Text)); + ASSERT_EQ(StyleLayerTypeClass(StyleLayerType::Symbol), StyleLayerTypeClass(StyleLayerType::Symbol)); ASSERT_EQ(StyleLayerTypeClass(StyleLayerType::Raster), StyleLayerTypeClass(StyleLayerType::Raster)); ASSERT_EQ(StyleLayerTypeClass(StyleLayerType::Composite), StyleLayerTypeClass(StyleLayerType::Composite)); ASSERT_EQ(StyleLayerTypeClass(StyleLayerType::Background), StyleLayerTypeClass(StyleLayerType::Background)); diff --git a/test/fixtures/glyphs/Open Sans Semibold, Arial Unicode MS Bold/0-255.pbf b/test/fixtures/glyphs/Open Sans Semibold, Arial Unicode MS Bold/0-255.pbf Binary files differdeleted file mode 100644 index cea1c094d6..0000000000 --- a/test/fixtures/glyphs/Open Sans Semibold, Arial Unicode MS Bold/0-255.pbf +++ /dev/null diff --git a/test/fixtures/sprites/outdoors.json b/test/fixtures/sprites/outdoors.json deleted file mode 100644 index b2a7ddf53a..0000000000 --- a/test/fixtures/sprites/outdoors.json +++ /dev/null @@ -1 +0,0 @@ -{"wetland_noveg_64":{"x":0,"y":0,"width":64,"height":32,"pixelRatio":1},"wetland_64":{"x":66,"y":0,"width":64,"height":32,"pixelRatio":1},"airport-24":{"x":0,"y":34,"width":24,"height":24,"pixelRatio":1},"wetland_32":{"x":26,"y":34,"width":32,"height":16,"pixelRatio":1},"wetland_noveg_32":{"x":60,"y":34,"width":32,"height":16,"pixelRatio":1},"night-library-12":{"x":94,"y":34,"width":12,"height":12,"pixelRatio":1},"bakery-12":{"x":108,"y":34,"width":12,"height":12,"pixelRatio":1},"bank-12":{"x":0,"y":60,"width":12,"height":12,"pixelRatio":1},"bar-12":{"x":14,"y":60,"width":12,"height":12,"pixelRatio":1},"baseball-12":{"x":28,"y":60,"width":12,"height":12,"pixelRatio":1},"basketball-12":{"x":42,"y":60,"width":12,"height":12,"pixelRatio":1},"beer-12":{"x":56,"y":60,"width":12,"height":12,"pixelRatio":1},"bicycle-12":{"x":70,"y":60,"width":12,"height":12,"pixelRatio":1},"building-12":{"x":84,"y":60,"width":12,"height":12,"pixelRatio":1},"bus-12":{"x":98,"y":60,"width":12,"height":12,"pixelRatio":1},"cafe-12":{"x":112,"y":60,"width":12,"height":12,"pixelRatio":1},"camera-12":{"x":0,"y":74,"width":12,"height":12,"pixelRatio":1},"campsite-12":{"x":14,"y":74,"width":12,"height":12,"pixelRatio":1},"car-12":{"x":28,"y":74,"width":12,"height":12,"pixelRatio":1},"cemetery-12":{"x":42,"y":74,"width":12,"height":12,"pixelRatio":1},"chemist-12":{"x":56,"y":74,"width":12,"height":12,"pixelRatio":1},"cinema-12":{"x":70,"y":74,"width":12,"height":12,"pixelRatio":1},"circle-12":{"x":84,"y":74,"width":12,"height":12,"pixelRatio":1},"circle-stroked-12":{"x":98,"y":74,"width":12,"height":12,"pixelRatio":1},"city-12":{"x":112,"y":74,"width":12,"height":12,"pixelRatio":1},"clothing-store-12":{"x":0,"y":88,"width":12,"height":12,"pixelRatio":1},"college-12":{"x":14,"y":88,"width":12,"height":12,"pixelRatio":1},"commercial-12":{"x":28,"y":88,"width":12,"height":12,"pixelRatio":1},"cricket-12":{"x":42,"y":88,"width":12,"height":12,"pixelRatio":1},"cross-12":{"x":56,"y":88,"width":12,"height":12,"pixelRatio":1},"dam-12":{"x":70,"y":88,"width":12,"height":12,"pixelRatio":1},"danger-12":{"x":84,"y":88,"width":12,"height":12,"pixelRatio":1},"disability-12":{"x":98,"y":88,"width":12,"height":12,"pixelRatio":1},"dog-park-12":{"x":112,"y":88,"width":12,"height":12,"pixelRatio":1},"embassy-12":{"x":0,"y":102,"width":12,"height":12,"pixelRatio":1},"emergency-telephone-12":{"x":14,"y":102,"width":12,"height":12,"pixelRatio":1},"entrance-12":{"x":28,"y":102,"width":12,"height":12,"pixelRatio":1},"farm-12":{"x":42,"y":102,"width":12,"height":12,"pixelRatio":1},"fast-food-12":{"x":56,"y":102,"width":12,"height":12,"pixelRatio":1},"ferry-12":{"x":70,"y":102,"width":12,"height":12,"pixelRatio":1},"fire-station-12":{"x":84,"y":102,"width":12,"height":12,"pixelRatio":1},"fuel-12":{"x":98,"y":102,"width":12,"height":12,"pixelRatio":1},"garden-12":{"x":112,"y":102,"width":12,"height":12,"pixelRatio":1},"golf-12":{"x":0,"y":116,"width":12,"height":12,"pixelRatio":1},"grocery-12":{"x":14,"y":116,"width":12,"height":12,"pixelRatio":1},"hairdresser-12":{"x":28,"y":116,"width":12,"height":12,"pixelRatio":1},"harbor-12":{"x":42,"y":116,"width":12,"height":12,"pixelRatio":1},"heart-12":{"x":56,"y":116,"width":12,"height":12,"pixelRatio":1},"heliport-12":{"x":70,"y":116,"width":12,"height":12,"pixelRatio":1},"hospital-12":{"x":84,"y":116,"width":12,"height":12,"pixelRatio":1},"industrial-12":{"x":98,"y":116,"width":12,"height":12,"pixelRatio":1},"land-use-12":{"x":112,"y":116,"width":12,"height":12,"pixelRatio":1},"laundry-12":{"x":132,"y":0,"width":12,"height":12,"pixelRatio":1},"library-12":{"x":132,"y":14,"width":12,"height":12,"pixelRatio":1},"lighthouse-12":{"x":132,"y":28,"width":12,"height":12,"pixelRatio":1},"lodging-12":{"x":132,"y":42,"width":12,"height":12,"pixelRatio":1},"logging-12":{"x":132,"y":56,"width":12,"height":12,"pixelRatio":1},"london-underground-12":{"x":132,"y":70,"width":12,"height":12,"pixelRatio":1},"marker-12":{"x":132,"y":84,"width":12,"height":12,"pixelRatio":1},"marker-stroked-12":{"x":132,"y":98,"width":12,"height":12,"pixelRatio":1},"minefield-12":{"x":132,"y":112,"width":12,"height":12,"pixelRatio":1},"mobilephone-12":{"x":0,"y":130,"width":12,"height":12,"pixelRatio":1},"monument-12":{"x":14,"y":130,"width":12,"height":12,"pixelRatio":1},"museum-12":{"x":28,"y":130,"width":12,"height":12,"pixelRatio":1},"music-12":{"x":42,"y":130,"width":12,"height":12,"pixelRatio":1},"night-airfield-12":{"x":56,"y":130,"width":12,"height":12,"pixelRatio":1},"night-airport-12":{"x":70,"y":130,"width":12,"height":12,"pixelRatio":1},"night-alcohol-shop-12":{"x":84,"y":130,"width":12,"height":12,"pixelRatio":1},"night-america-football-12":{"x":98,"y":130,"width":12,"height":12,"pixelRatio":1},"night-art-gallery-12":{"x":112,"y":130,"width":12,"height":12,"pixelRatio":1},"night-bakery-12":{"x":126,"y":130,"width":12,"height":12,"pixelRatio":1},"night-bank-12":{"x":146,"y":0,"width":12,"height":12,"pixelRatio":1},"night-bar-12":{"x":146,"y":14,"width":12,"height":12,"pixelRatio":1},"night-baseball-12":{"x":146,"y":28,"width":12,"height":12,"pixelRatio":1},"night-basketball-12":{"x":146,"y":42,"width":12,"height":12,"pixelRatio":1},"night-beer-12":{"x":146,"y":56,"width":12,"height":12,"pixelRatio":1},"night-bicycle-12":{"x":146,"y":70,"width":12,"height":12,"pixelRatio":1},"night-building-12":{"x":146,"y":84,"width":12,"height":12,"pixelRatio":1},"night-bus-12":{"x":146,"y":98,"width":12,"height":12,"pixelRatio":1},"night-cafe-12":{"x":146,"y":112,"width":12,"height":12,"pixelRatio":1},"night-camera-12":{"x":146,"y":126,"width":12,"height":12,"pixelRatio":1},"night-campsite-12":{"x":0,"y":144,"width":12,"height":12,"pixelRatio":1},"night-car-12":{"x":14,"y":144,"width":12,"height":12,"pixelRatio":1},"night-cemetery-12":{"x":28,"y":144,"width":12,"height":12,"pixelRatio":1},"night-chemist-12":{"x":42,"y":144,"width":12,"height":12,"pixelRatio":1},"night-cinema-12":{"x":56,"y":144,"width":12,"height":12,"pixelRatio":1},"night-circle-12":{"x":70,"y":144,"width":12,"height":12,"pixelRatio":1},"night-circle-stroked-12":{"x":84,"y":144,"width":12,"height":12,"pixelRatio":1},"night-city-12":{"x":98,"y":144,"width":12,"height":12,"pixelRatio":1},"night-clothing-store-12":{"x":112,"y":144,"width":12,"height":12,"pixelRatio":1},"night-college-12":{"x":126,"y":144,"width":12,"height":12,"pixelRatio":1},"night-commercial-12":{"x":140,"y":144,"width":12,"height":12,"pixelRatio":1},"night-cricket-12":{"x":160,"y":0,"width":12,"height":12,"pixelRatio":1},"night-cross-12":{"x":160,"y":14,"width":12,"height":12,"pixelRatio":1},"night-dam-12":{"x":160,"y":28,"width":12,"height":12,"pixelRatio":1},"night-danger-12":{"x":160,"y":42,"width":12,"height":12,"pixelRatio":1},"night-disability-12":{"x":160,"y":56,"width":12,"height":12,"pixelRatio":1},"night-dog-park-12":{"x":160,"y":70,"width":12,"height":12,"pixelRatio":1},"night-embassy-12":{"x":160,"y":84,"width":12,"height":12,"pixelRatio":1},"night-emergency-telephone-12":{"x":160,"y":98,"width":12,"height":12,"pixelRatio":1},"night-entrance-12":{"x":160,"y":112,"width":12,"height":12,"pixelRatio":1},"night-farm-12":{"x":160,"y":126,"width":12,"height":12,"pixelRatio":1},"night-fast-food-12":{"x":160,"y":140,"width":12,"height":12,"pixelRatio":1},"night-ferry-12":{"x":0,"y":158,"width":12,"height":12,"pixelRatio":1},"night-fire-station-12":{"x":14,"y":158,"width":12,"height":12,"pixelRatio":1},"night-fuel-12":{"x":28,"y":158,"width":12,"height":12,"pixelRatio":1},"night-garden-12":{"x":42,"y":158,"width":12,"height":12,"pixelRatio":1},"night-golf-12":{"x":56,"y":158,"width":12,"height":12,"pixelRatio":1},"night-grocery-12":{"x":70,"y":158,"width":12,"height":12,"pixelRatio":1},"night-hairdresser-12":{"x":84,"y":158,"width":12,"height":12,"pixelRatio":1},"night-harbor-12":{"x":98,"y":158,"width":12,"height":12,"pixelRatio":1},"night-heart-12":{"x":112,"y":158,"width":12,"height":12,"pixelRatio":1},"night-heliport-12":{"x":126,"y":158,"width":12,"height":12,"pixelRatio":1},"night-hospital-12":{"x":140,"y":158,"width":12,"height":12,"pixelRatio":1},"night-industrial-12":{"x":154,"y":158,"width":12,"height":12,"pixelRatio":1},"night-land-use-12":{"x":174,"y":0,"width":12,"height":12,"pixelRatio":1},"night-laundry-12":{"x":174,"y":14,"width":12,"height":12,"pixelRatio":1},"airport-12":{"x":174,"y":28,"width":12,"height":12,"pixelRatio":1},"night-lighthouse-12":{"x":174,"y":42,"width":12,"height":12,"pixelRatio":1},"night-lodging-12":{"x":174,"y":56,"width":12,"height":12,"pixelRatio":1},"night-logging-12":{"x":174,"y":70,"width":12,"height":12,"pixelRatio":1},"night-london-underground-12":{"x":174,"y":84,"width":12,"height":12,"pixelRatio":1},"night-marker-12":{"x":174,"y":98,"width":12,"height":12,"pixelRatio":1},"night-marker-stroked-12":{"x":174,"y":112,"width":12,"height":12,"pixelRatio":1},"night-minefield-12":{"x":174,"y":126,"width":12,"height":12,"pixelRatio":1},"night-mobilephone-12":{"x":174,"y":140,"width":12,"height":12,"pixelRatio":1},"night-monument-12":{"x":174,"y":154,"width":12,"height":12,"pixelRatio":1},"night-museum-12":{"x":0,"y":172,"width":12,"height":12,"pixelRatio":1},"night-music-12":{"x":14,"y":172,"width":12,"height":12,"pixelRatio":1},"night-oil-well-12":{"x":28,"y":172,"width":12,"height":12,"pixelRatio":1},"night-park-12":{"x":42,"y":172,"width":12,"height":12,"pixelRatio":1},"night-park2-12":{"x":56,"y":172,"width":12,"height":12,"pixelRatio":1},"night-parking-12":{"x":70,"y":172,"width":12,"height":12,"pixelRatio":1},"night-parking-garage-12":{"x":84,"y":172,"width":12,"height":12,"pixelRatio":1},"night-pharmacy-12":{"x":98,"y":172,"width":12,"height":12,"pixelRatio":1},"night-pitch-12":{"x":112,"y":172,"width":12,"height":12,"pixelRatio":1},"night-place-of-worship-12":{"x":126,"y":172,"width":12,"height":12,"pixelRatio":1},"night-playground-12":{"x":140,"y":172,"width":12,"height":12,"pixelRatio":1},"night-police-12":{"x":154,"y":172,"width":12,"height":12,"pixelRatio":1},"night-polling-place-12":{"x":168,"y":172,"width":12,"height":12,"pixelRatio":1},"night-post-12":{"x":188,"y":0,"width":12,"height":12,"pixelRatio":1},"night-prison-12":{"x":188,"y":14,"width":12,"height":12,"pixelRatio":1},"night-rail-12":{"x":188,"y":28,"width":12,"height":12,"pixelRatio":1},"night-rail-above-12":{"x":188,"y":42,"width":12,"height":12,"pixelRatio":1},"night-rail-light-12":{"x":188,"y":56,"width":12,"height":12,"pixelRatio":1},"night-rail-metro-12":{"x":188,"y":70,"width":12,"height":12,"pixelRatio":1},"night-rail-underground-12":{"x":188,"y":84,"width":12,"height":12,"pixelRatio":1},"night-religious-christian-12":{"x":188,"y":98,"width":12,"height":12,"pixelRatio":1},"night-religious-jewish-12":{"x":188,"y":112,"width":12,"height":12,"pixelRatio":1},"night-religious-muslim-12":{"x":188,"y":126,"width":12,"height":12,"pixelRatio":1},"night-restaurant-12":{"x":188,"y":140,"width":12,"height":12,"pixelRatio":1},"night-roadblock-12":{"x":188,"y":154,"width":12,"height":12,"pixelRatio":1},"night-rocket-12":{"x":188,"y":168,"width":12,"height":12,"pixelRatio":1},"night-school-12":{"x":0,"y":186,"width":12,"height":12,"pixelRatio":1},"night-scooter-12":{"x":14,"y":186,"width":12,"height":12,"pixelRatio":1},"night-shop-12":{"x":28,"y":186,"width":12,"height":12,"pixelRatio":1},"night-skiing-12":{"x":42,"y":186,"width":12,"height":12,"pixelRatio":1},"night-slaughterhouse-12":{"x":56,"y":186,"width":12,"height":12,"pixelRatio":1},"night-soccer-12":{"x":70,"y":186,"width":12,"height":12,"pixelRatio":1},"night-square-12":{"x":84,"y":186,"width":12,"height":12,"pixelRatio":1},"night-square-stroked-12":{"x":98,"y":186,"width":12,"height":12,"pixelRatio":1},"night-star-12":{"x":112,"y":186,"width":12,"height":12,"pixelRatio":1},"night-star-stroked-12":{"x":126,"y":186,"width":12,"height":12,"pixelRatio":1},"night-suitcase-12":{"x":140,"y":186,"width":12,"height":12,"pixelRatio":1},"night-swimming-12":{"x":154,"y":186,"width":12,"height":12,"pixelRatio":1},"night-telephone-12":{"x":168,"y":186,"width":12,"height":12,"pixelRatio":1},"night-tennis-12":{"x":182,"y":186,"width":12,"height":12,"pixelRatio":1},"night-theatre-12":{"x":202,"y":0,"width":12,"height":12,"pixelRatio":1},"night-toilets-12":{"x":202,"y":14,"width":12,"height":12,"pixelRatio":1},"night-town-12":{"x":202,"y":28,"width":12,"height":12,"pixelRatio":1},"night-town-hall-12":{"x":202,"y":42,"width":12,"height":12,"pixelRatio":1},"night-triangle-12":{"x":202,"y":56,"width":12,"height":12,"pixelRatio":1},"night-triangle-stroked-12":{"x":202,"y":70,"width":12,"height":12,"pixelRatio":1},"night-village-12":{"x":202,"y":84,"width":12,"height":12,"pixelRatio":1},"night-warehouse-12":{"x":202,"y":98,"width":12,"height":12,"pixelRatio":1},"night-waste-basket-12":{"x":202,"y":112,"width":12,"height":12,"pixelRatio":1},"night-water-12":{"x":202,"y":126,"width":12,"height":12,"pixelRatio":1},"night-wetland-12":{"x":202,"y":140,"width":12,"height":12,"pixelRatio":1},"night-zoo-12":{"x":202,"y":154,"width":12,"height":12,"pixelRatio":1},"oil-well-12":{"x":202,"y":168,"width":12,"height":12,"pixelRatio":1},"park-12":{"x":202,"y":182,"width":12,"height":12,"pixelRatio":1},"park2-12":{"x":0,"y":200,"width":12,"height":12,"pixelRatio":1},"parking-12":{"x":14,"y":200,"width":12,"height":12,"pixelRatio":1},"parking-garage-12":{"x":28,"y":200,"width":12,"height":12,"pixelRatio":1},"pharmacy-12":{"x":42,"y":200,"width":12,"height":12,"pixelRatio":1},"pitch-12":{"x":56,"y":200,"width":12,"height":12,"pixelRatio":1},"place-of-worship-12":{"x":70,"y":200,"width":12,"height":12,"pixelRatio":1},"playground-12":{"x":84,"y":200,"width":12,"height":12,"pixelRatio":1},"police-12":{"x":98,"y":200,"width":12,"height":12,"pixelRatio":1},"polling-place-12":{"x":112,"y":200,"width":12,"height":12,"pixelRatio":1},"post-12":{"x":126,"y":200,"width":12,"height":12,"pixelRatio":1},"prison-12":{"x":140,"y":200,"width":12,"height":12,"pixelRatio":1},"rail-12":{"x":154,"y":200,"width":12,"height":12,"pixelRatio":1},"rail-above-12":{"x":168,"y":200,"width":12,"height":12,"pixelRatio":1},"rail-light-12":{"x":182,"y":200,"width":12,"height":12,"pixelRatio":1},"rail-metro-12":{"x":196,"y":200,"width":12,"height":12,"pixelRatio":1},"rail-underground-12":{"x":216,"y":0,"width":12,"height":12,"pixelRatio":1},"religious-christian-12":{"x":216,"y":14,"width":12,"height":12,"pixelRatio":1},"religious-jewish-12":{"x":216,"y":28,"width":12,"height":12,"pixelRatio":1},"religious-muslim-12":{"x":216,"y":42,"width":12,"height":12,"pixelRatio":1},"restaurant-12":{"x":216,"y":56,"width":12,"height":12,"pixelRatio":1},"roadblock-12":{"x":216,"y":70,"width":12,"height":12,"pixelRatio":1},"rocket-12":{"x":216,"y":84,"width":12,"height":12,"pixelRatio":1},"school-12":{"x":216,"y":98,"width":12,"height":12,"pixelRatio":1},"scooter-12":{"x":216,"y":112,"width":12,"height":12,"pixelRatio":1},"shop-12":{"x":216,"y":126,"width":12,"height":12,"pixelRatio":1},"skiing-12":{"x":216,"y":140,"width":12,"height":12,"pixelRatio":1},"slaughterhouse-12":{"x":216,"y":154,"width":12,"height":12,"pixelRatio":1},"soccer-12":{"x":216,"y":168,"width":12,"height":12,"pixelRatio":1},"square-12":{"x":216,"y":182,"width":12,"height":12,"pixelRatio":1},"square-stroked-12":{"x":216,"y":196,"width":12,"height":12,"pixelRatio":1},"star-12":{"x":0,"y":214,"width":12,"height":12,"pixelRatio":1},"star-stroked-12":{"x":14,"y":214,"width":12,"height":12,"pixelRatio":1},"suitcase-12":{"x":28,"y":214,"width":12,"height":12,"pixelRatio":1},"swimming-12":{"x":42,"y":214,"width":12,"height":12,"pixelRatio":1},"telephone-12":{"x":56,"y":214,"width":12,"height":12,"pixelRatio":1},"tennis-12":{"x":70,"y":214,"width":12,"height":12,"pixelRatio":1},"theatre-12":{"x":84,"y":214,"width":12,"height":12,"pixelRatio":1},"toilets-12":{"x":98,"y":214,"width":12,"height":12,"pixelRatio":1},"town-12":{"x":112,"y":214,"width":12,"height":12,"pixelRatio":1},"town-hall-12":{"x":126,"y":214,"width":12,"height":12,"pixelRatio":1},"triangle-12":{"x":140,"y":214,"width":12,"height":12,"pixelRatio":1},"triangle-stroked-12":{"x":154,"y":214,"width":12,"height":12,"pixelRatio":1},"village-12":{"x":168,"y":214,"width":12,"height":12,"pixelRatio":1},"warehouse-12":{"x":182,"y":214,"width":12,"height":12,"pixelRatio":1},"waste-basket-12":{"x":196,"y":214,"width":12,"height":12,"pixelRatio":1},"water-12":{"x":210,"y":214,"width":12,"height":12,"pixelRatio":1},"wetland-12":{"x":230,"y":0,"width":12,"height":12,"pixelRatio":1},"airfield-12":{"x":230,"y":14,"width":12,"height":12,"pixelRatio":1},"alcohol-shop-12":{"x":230,"y":28,"width":12,"height":12,"pixelRatio":1},"america-football-12":{"x":230,"y":42,"width":12,"height":12,"pixelRatio":1},"art-gallery-12":{"x":230,"y":56,"width":12,"height":12,"pixelRatio":1},"zoo-12":{"x":230,"y":70,"width":12,"height":12,"pixelRatio":1},"wetland_noveg_16":{"x":0,"y":228,"width":16,"height":8,"pixelRatio":1},"wetland_16":{"x":18,"y":228,"width":16,"height":8,"pixelRatio":1}}
\ No newline at end of file diff --git a/test/fixtures/sprites/outdoors.png b/test/fixtures/sprites/outdoors.png Binary files differdeleted file mode 100644 index 61e99085f1..0000000000 --- a/test/fixtures/sprites/outdoors.png +++ /dev/null diff --git a/test/fixtures/styles/icons.info.json b/test/fixtures/styles/icons.info.json deleted file mode 100644 index e116913aa4..0000000000 --- a/test/fixtures/styles/icons.info.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "default": { - "zoom": 14, - "center": [52.499167, 13.418056], - "height": 256, - "log": [ - [1, "INFO", "HttpRequest", 200, "sprites/outdoors.json"], - [1, "INFO", "HttpRequest", 200, "sprites/outdoors.png"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5374.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5374.vector.pbf"], - [6, "INFO", "HttpRequest"], - - [1, "INFO", "Sprite", "loaded sprites/outdoors"] - ] - } -} diff --git a/test/fixtures/styles/icons.style.json b/test/fixtures/styles/icons.style.json deleted file mode 100644 index 46adfbd408..0000000000 --- a/test/fixtures/styles/icons.style.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "version": 3, - "sources": { - "mapbox": { - "type": "vector", - "url": "tiles/{z}-{x}-{y}.vector.pbf", - "tileSize": 512, - "maxZoom": 14 - } - }, - "sprite": "sprites/outdoors", - "layers": [ - { - "id": "background", - "type": "background", - "style": { - "background-color": "green" - } - }, - { - "id": "poi", - "source": "mapbox", - "source-layer": "poi_label", - "type": "icon", - "filter": { "maki": "restaurant" }, - "render": { - "icon-image": "{maki}-12", - "icon-size": 12, - "icon-allow-overlap": true - } - } - ] -} diff --git a/test/fixtures/styles/icons/default.expected.png b/test/fixtures/styles/icons/default.expected.png Binary files differdeleted file mode 100644 index 17e3252c88..0000000000 --- a/test/fixtures/styles/icons/default.expected.png +++ /dev/null diff --git a/test/fixtures/styles/line-color.info.json b/test/fixtures/styles/line-color.info.json deleted file mode 100644 index d9be88b102..0000000000 --- a/test/fixtures/styles/line-color.info.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "default": { - "zoom": 14, - "center": [52.499167, 13.418056], - "height": 256, - "log": [ - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5374.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5374.vector.pbf"], - [4, "INFO", "HttpRequest"] - ] - }, - "colored": { - "zoom": 14, - "center": [52.499167, 13.418056], - "height": 256, - "classes": ["colored"], - "log": [ - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5374.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5374.vector.pbf"], - [4, "INFO", "HttpRequest"] - ] - } -} diff --git a/test/fixtures/styles/line-color.style.json b/test/fixtures/styles/line-color.style.json deleted file mode 100644 index 10f5fb9cb7..0000000000 --- a/test/fixtures/styles/line-color.style.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "version": 3, - "sources": { - "mapbox": { - "type": "vector", - "url": "tiles/{z}-{x}-{y}.vector.pbf", - "tileSize": 512, - "maxZoom": 14 - } - }, - "layers": [ - { - "id": "background", - "type": "background", - "style": { - "background-color": "white" - } - }, - { - "id": "road", - "source": "mapbox", - "source-layer": "road", - "type": "line", - "style": { - "line-width": 2, - "line-color": "#3590db" - }, - "style.colored": { - "line-color": "#222222" - } - } - ] -} diff --git a/test/fixtures/styles/line-color/colored.expected.png b/test/fixtures/styles/line-color/colored.expected.png Binary files differdeleted file mode 100644 index ffd1ac3fb5..0000000000 --- a/test/fixtures/styles/line-color/colored.expected.png +++ /dev/null diff --git a/test/fixtures/styles/line-color/default.expected.png b/test/fixtures/styles/line-color/default.expected.png Binary files differdeleted file mode 100644 index 6369f5620b..0000000000 --- a/test/fixtures/styles/line-color/default.expected.png +++ /dev/null diff --git a/test/fixtures/styles/road-width.info.json b/test/fixtures/styles/road-width.info.json deleted file mode 100644 index 0094f715ca..0000000000 --- a/test/fixtures/styles/road-width.info.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "z13.9": { - "zoom": 13.9, - "center": [52.499167, 13.418056], - "height": 256, - "log": [ - [1, "INFO", "HttpRequest", 200, "tiles/13-4401-2687.vector.pbf"], - [1, "INFO", "HttpRequest"] - ] - }, - "z14.0": { - "zoom": 14, - "center": [52.499167, 13.418056], - "height": 256, - "log": [ - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5374.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5374.vector.pbf"], - [4, "INFO", "HttpRequest"] - ] - }, - "z14.1": { - "zoom": 14.1, - "center": [52.499167, 13.418056], - "height": 256, - "log": [ - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5375.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5374.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5374.vector.pbf"], - [4, "INFO", "HttpRequest"] - ] - }, - "z14.2": { - "zoom": 14.2, - "center": [52.499167, 13.418056], - "height": 256, - "log": [ - [1, "INFO", "HttpRequest", 200, "tiles/14-8803-5374.vector.pbf"], - [1, "INFO", "HttpRequest", 200, "tiles/14-8802-5374.vector.pbf"], - [2, "INFO", "HttpRequest"] - ] - } -} diff --git a/test/fixtures/styles/road-width.style.json b/test/fixtures/styles/road-width.style.json deleted file mode 100644 index 76d7b5258c..0000000000 --- a/test/fixtures/styles/road-width.style.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "version": 3, - "sources": { - "mapbox": { - "type": "vector", - "url": "tiles/{z}-{x}-{y}.vector.pbf", - "tileSize": 512, - "maxZoom": 14 - } - }, - "layers": [ - { - "id": "background", - "type": "background", - "style": { - "background-color": "white" - } - }, - { - "id": "road", - "source": "mapbox", - "source-layer": "road", - "type": "line", - "style": { - "line-width": { - "stops": [[13, 0], [13.999, 0], [14, 4], [14.1, 10], [14.2, 20]] - } - } - } - ] -} diff --git a/test/fixtures/styles/road-width/z13.9.expected.png b/test/fixtures/styles/road-width/z13.9.expected.png Binary files differdeleted file mode 100644 index e65d3339d6..0000000000 --- a/test/fixtures/styles/road-width/z13.9.expected.png +++ /dev/null diff --git a/test/fixtures/styles/road-width/z14.0.expected.png b/test/fixtures/styles/road-width/z14.0.expected.png Binary files differdeleted file mode 100644 index 01444c6f87..0000000000 --- a/test/fixtures/styles/road-width/z14.0.expected.png +++ /dev/null diff --git a/test/fixtures/styles/road-width/z14.1.expected.png b/test/fixtures/styles/road-width/z14.1.expected.png Binary files differdeleted file mode 100644 index 3a28f3c4de..0000000000 --- a/test/fixtures/styles/road-width/z14.1.expected.png +++ /dev/null diff --git a/test/fixtures/styles/road-width/z14.2.expected.png b/test/fixtures/styles/road-width/z14.2.expected.png Binary files differdeleted file mode 100644 index 8cd62453cb..0000000000 --- a/test/fixtures/styles/road-width/z14.2.expected.png +++ /dev/null diff --git a/test/fixtures/styles/world-aa.info.json b/test/fixtures/styles/world-aa.info.json deleted file mode 100644 index e5e123dcef..0000000000 --- a/test/fixtures/styles/world-aa.info.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plain": { - "center": [0, 0], - "zoom": 0, - "log": [ - [ 1, "INFO", "HttpRequest", 200, "tiles/0-0-0.vector.pbf" ] - ] - } -} diff --git a/test/fixtures/styles/world-aa.style.json b/test/fixtures/styles/world-aa.style.json deleted file mode 100644 index 3165ec5b9f..0000000000 --- a/test/fixtures/styles/world-aa.style.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "version": 3, - "sources": { - "mapbox": { - "type": "vector", - "url": "tiles/{z}-{x}-{y}.vector.pbf", - "tileSize": 512, - "maxZoom": 14 - } - }, - "layers": [ - { - "id": "background", - "type": "background", - "style": { - "background-color": "red" - } - }, - { - "id": "water", - "source": "mapbox", - "source-layer": "water", - "type": "fill", - "style": { - "fill-color": "blue" - } - } - ] -} diff --git a/test/fixtures/styles/world-aa/plain.expected.png b/test/fixtures/styles/world-aa/plain.expected.png Binary files differdeleted file mode 100644 index d34c46a6c9..0000000000 --- a/test/fixtures/styles/world-aa/plain.expected.png +++ /dev/null diff --git a/test/fixtures/styles/world-no-aa.info.json b/test/fixtures/styles/world-no-aa.info.json deleted file mode 100644 index f5bc0c1462..0000000000 --- a/test/fixtures/styles/world-no-aa.info.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "plain": { - "center": [0, 0], - "zoom": 0, - "log": [ - [1, "INFO", "HttpRequest", 200, "tiles/0-0-0.vector.pbf"], - [1, "INFO", "HttpRequest"] - ] - } -} diff --git a/test/fixtures/styles/world-no-aa.style.json b/test/fixtures/styles/world-no-aa.style.json deleted file mode 100644 index c37774b662..0000000000 --- a/test/fixtures/styles/world-no-aa.style.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "version": 3, - "sources": { - "mapbox": { - "type": "vector", - "url": "tiles/{z}-{x}-{y}.vector.pbf", - "tileSize": 512, - "maxZoom": 14 - } - }, - "layers": [ - { - "id": "background", - "type": "background", - "style": { - "background-color": "red" - } - }, - { - "id": "water", - "source": "mapbox", - "source-layer": "water", - "type": "fill", - "style": { - "fill-color": "blue", - "fill-antialias": false - } - } - ] -} diff --git a/test/fixtures/styles/world-no-aa/plain.expected.png b/test/fixtures/styles/world-no-aa/plain.expected.png Binary files differdeleted file mode 100644 index 34e984cce3..0000000000 --- a/test/fixtures/styles/world-no-aa/plain.expected.png +++ /dev/null diff --git a/test/fixtures/tiles/0-0-0.vector.pbf b/test/fixtures/tiles/0-0-0.vector.pbf Binary files differdeleted file mode 100644 index 5eee529357..0000000000 --- a/test/fixtures/tiles/0-0-0.vector.pbf +++ /dev/null diff --git a/test/fixtures/tiles/13-4401-2687.vector.pbf b/test/fixtures/tiles/13-4401-2687.vector.pbf Binary files differdeleted file mode 100644 index f2165b8fe5..0000000000 --- a/test/fixtures/tiles/13-4401-2687.vector.pbf +++ /dev/null diff --git a/test/fixtures/tiles/14-8802-5374.vector.pbf b/test/fixtures/tiles/14-8802-5374.vector.pbf Binary files differdeleted file mode 100644 index a2277ea1ff..0000000000 --- a/test/fixtures/tiles/14-8802-5374.vector.pbf +++ /dev/null diff --git a/test/fixtures/tiles/14-8802-5375.vector.pbf b/test/fixtures/tiles/14-8802-5375.vector.pbf Binary files differdeleted file mode 100644 index 0804e8736c..0000000000 --- a/test/fixtures/tiles/14-8802-5375.vector.pbf +++ /dev/null diff --git a/test/fixtures/tiles/14-8803-5374.vector.pbf b/test/fixtures/tiles/14-8803-5374.vector.pbf Binary files differdeleted file mode 100644 index 4e66bead09..0000000000 --- a/test/fixtures/tiles/14-8803-5374.vector.pbf +++ /dev/null diff --git a/test/fixtures/tiles/14-8803-5375.vector.pbf b/test/fixtures/tiles/14-8803-5375.vector.pbf Binary files differdeleted file mode 100644 index f940be5992..0000000000 --- a/test/fixtures/tiles/14-8803-5375.vector.pbf +++ /dev/null diff --git a/test/fixtures/tiles/2-1-1.vector.pbf b/test/fixtures/tiles/2-1-1.vector.pbf Binary files differdeleted file mode 100644 index 00a5435b7e..0000000000 --- a/test/fixtures/tiles/2-1-1.vector.pbf +++ /dev/null diff --git a/test/fixtures/tiles/2-1-2.vector.pbf b/test/fixtures/tiles/2-1-2.vector.pbf Binary files differdeleted file mode 100644 index 4d26226c57..0000000000 --- a/test/fixtures/tiles/2-1-2.vector.pbf +++ /dev/null diff --git a/test/fixtures/tiles/2-2-1.vector.pbf b/test/fixtures/tiles/2-2-1.vector.pbf Binary files differdeleted file mode 100644 index a92e85b60e..0000000000 --- a/test/fixtures/tiles/2-2-1.vector.pbf +++ /dev/null diff --git a/test/fixtures/tiles/2-2-2.vector.pbf b/test/fixtures/tiles/2-2-2.vector.pbf Binary files differdeleted file mode 100644 index 209014708f..0000000000 --- a/test/fixtures/tiles/2-2-2.vector.pbf +++ /dev/null diff --git a/test/headless.cpp b/test/headless.cpp index 18a17d6bdf..bd84e37681 100644 --- a/test/headless.cpp +++ b/test/headless.cpp @@ -6,6 +6,8 @@ #include <mbgl/util/std.hpp> #include <rapidjson/document.h> +#include <rapidjson/writer.h> +#include <rapidjson/stringbuffer.h> #include "../common/headless_view.hpp" @@ -16,24 +18,47 @@ const std::string base_directory = []{ std::string fn = __FILE__; fn.erase(fn.find_last_of("/")); - return fn + "/fixtures/styles"; + return fn + "/../node_modules/mapbox-gl-test-suite/"; }(); class HeadlessTest : public ::testing::TestWithParam<std::string> {}; +void ResolveLocalURL(rapidjson::Value& value, rapidjson::Document& doc) { + std::string str { value.GetString(), value.GetStringLength() }; + str.replace(0, 8, base_directory); // local:// + value.SetString(str.c_str(), str.length(), doc.GetAllocator()); +} + TEST_P(HeadlessTest, render) { using namespace mbgl; const std::string &base = GetParam(); - const std::string style = util::read_file(base_directory + "/" + base + ".style.json"); - const std::string info = util::read_file(base_directory + "/" + base + ".info.json"); + std::string style = util::read_file(base_directory + "tests/" + base + "/style.json"); + std::string info = util::read_file(base_directory + "tests/" + base + "/info.json"); + + // Parse style. + rapidjson::Document styleDoc; + styleDoc.Parse<0>((const char *const)style.c_str()); + ASSERT_EQ(false, styleDoc.HasParseError()); + ASSERT_EQ(true, styleDoc.IsObject()); + + if (styleDoc.HasMember("sprite")) { + ResolveLocalURL(styleDoc["sprite"], styleDoc); + } + + ResolveLocalURL(styleDoc["sources"]["mapbox"]["url"], styleDoc); + + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + styleDoc.Accept(writer); + style = buffer.GetString(); // Parse settings. - rapidjson::Document doc; - doc.Parse<0>((const char *const)info.c_str()); - ASSERT_EQ(false, doc.HasParseError()); - ASSERT_EQ(true, doc.IsObject()); + rapidjson::Document infoDoc; + infoDoc.Parse<0>((const char *const)info.c_str()); + ASSERT_EQ(false, infoDoc.HasParseError()); + ASSERT_EQ(true, infoDoc.IsObject()); Log::Set<FixtureLogBackend>(); @@ -41,15 +66,13 @@ TEST_P(HeadlessTest, render) { HeadlessView view; Map map(view); - for (auto it = doc.MemberBegin(), end = doc.MemberEnd(); it != end; it++) { - const FixtureLogBackend &log = Log::Set<FixtureLogBackend>(); - + for (auto it = infoDoc.MemberBegin(), end = infoDoc.MemberEnd(); it != end; it++) { const std::string name { it->name.GetString(), it->name.GetStringLength() }; const rapidjson::Value &value = it->value; ASSERT_EQ(true, value.IsObject()); if (value.HasMember("center")) ASSERT_EQ(true, value["center"].IsArray()); - const std::string actual_image = base_directory + "/" + base + "/" + name + ".actual.png"; + const std::string actual_image = base_directory + "tests/" + base + "/" + name + "/actual.png"; const double zoom = value.HasMember("zoom") ? value["zoom"].GetDouble() : 0; const double bearing = value.HasMember("bearing") ? value["bearing"].GetDouble() : 0; @@ -74,7 +97,7 @@ TEST_P(HeadlessTest, render) { view.resize(width, height); map.resize(width, height); map.setLonLatZoom(longitude, latitude, zoom); - map.setAngle(bearing); + map.setBearing(bearing); // Run the loop. It will terminate when we don't have any further listeners. map.run(); @@ -84,73 +107,22 @@ TEST_P(HeadlessTest, render) { const std::string image = util::compress_png(width, height, pixels.get(), true); util::write_file(actual_image, image); - - if (value.HasMember("log")) { - const rapidjson::Value &js_log = value["log"]; - ASSERT_EQ(true, js_log.IsArray()); - for (rapidjson::SizeType i = 0; i < js_log.Size(); i++) { - const rapidjson::Value &js_entry = js_log[i]; - ASSERT_EQ(true, js_entry.IsArray()); - if (js_entry.Size() == 5) { - const uint32_t count = js_entry[rapidjson::SizeType(0)].GetUint(); - const FixtureLogBackend::LogMessage message { - EventSeverityClass(js_entry[rapidjson::SizeType(1)].GetString()), - EventClass(js_entry[rapidjson::SizeType(2)].GetString()), - js_entry[rapidjson::SizeType(3)].GetInt64(), - js_entry[rapidjson::SizeType(4)].GetString() - }; - ASSERT_EQ(count, log.count(message)) << "Message: " << message << "Full Log: " << std::endl << log.messages; - } else if (js_entry.Size() == 4) { - const uint32_t count = js_entry[rapidjson::SizeType(0)].GetUint(); - if (js_entry[rapidjson::SizeType(3)].IsString()) { - const FixtureLogBackend::LogMessage message { - EventSeverityClass(js_entry[rapidjson::SizeType(1)].GetString()), - EventClass(js_entry[rapidjson::SizeType(2)].GetString()), - js_entry[rapidjson::SizeType(3)].GetString() - }; - ASSERT_EQ(count, log.count(message)) << "Message: " << message << "Full Log: " << std::endl << log.messages; - } else { - const FixtureLogBackend::LogMessage message { - EventSeverityClass(js_entry[rapidjson::SizeType(1)].GetString()), - EventClass(js_entry[rapidjson::SizeType(2)].GetString()), - js_entry[rapidjson::SizeType(3)].GetInt64() - }; - ASSERT_EQ(count, log.count(message)) << "Message: " << message << "Full Log: " << std::endl << log.messages; - } - } else if (js_entry.Size() == 3) { - const uint32_t count = js_entry[rapidjson::SizeType(0)].GetUint(); - const FixtureLogBackend::LogMessage message { - EventSeverityClass(js_entry[rapidjson::SizeType(1)].GetString()), - EventClass(js_entry[rapidjson::SizeType(2)].GetString()) - }; - ASSERT_EQ(count, log.count(message)) << "Message: " << message << "Full Log: " << std::endl << log.messages; - } else { - FAIL(); - } - } - } - - const auto &unchecked = log.unchecked(); - if (unchecked.size()) { - std::cerr << "Unchecked Log Messages (" << base << "/" << name << "): " << std::endl << unchecked; - } } } INSTANTIATE_TEST_CASE_P(Headless, HeadlessTest, ::testing::ValuesIn([] { std::vector<std::string> names; - const std::string ending = ".info.json"; - DIR *dir = opendir(base_directory.c_str()); + DIR *dir = opendir((base_directory + "tests").c_str()); if (dir == nullptr) { return names; } for (dirent *dp = nullptr; (dp = readdir(dir)) != nullptr;) { const std::string name = dp->d_name; - if (name.length() >= ending.length() && name.compare(name.length() - ending.length(), ending.length(), ending) == 0) { - names.push_back(name.substr(0, name.length() - ending.length())); + if (name != "." && name != ".." && name != "index.html") { + names.push_back(name); } } diff --git a/test/test.gyp b/test/test.gyp index f91f118080..75ab26a514 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -208,7 +208,7 @@ "dependencies": [ "../deps/gtest/gtest.gyp:gtest", "../mapboxgl.gyp:mapboxgl", - "../mapboxgl.gyp:copy_default_stylesheet", + '../mapboxgl.gyp:bundle_styles', "link_gl", ] }, |