diff --git a/.gitignore b/.gitignore index 08716f371..6cdd23455 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.xcodeproj *.o *.actual.png +*.diff.png /node_modules /mapnik-packaging /macosx/build diff --git a/.travis.yml b/.travis.yml index dddd096c8..62f185b2c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,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 @@ -38,7 +39,7 @@ notifications: rooms: secure: "SgMjGDp8MP+UTAY/aWJwSQH2hv1Q0vhEc1BdIde59+lEL3u6gNMSexUTSJoVxRlIQTFXDJBU7v58ISdgJX8cPa+UvIC8hhLsayM+cbm+J3jgy7V9E9wllq3RpmXN33TxY7nwKgWdIms2t9YKZco3y4dKKAUyHXrtMvw8NNPQM+4=" template: - - 'Build #%{build_number}: %{author} tested %{commit} in branch %{branch} of %{repository_name}: %{commit_message}
%{message} (%{duration})' + - 'Build #%{build_number}: %{author} tested %{commit} in branch %{branch} of %{repository_name}: %{commit_message}
%{message} (%{duration}) - Compare: 1 2 3 4' format: html git: diff --git a/bin/style.js b/bin/style.js index 21cca2af3..e760ea102 100644 --- a/bin/style.js +++ b/bin/style.js @@ -3,7 +3,7 @@ module.exports = { "version": 3, "sprite": "https://www.mapbox.com/mapbox-gl-styles/sprites/outdoors", - "glyphs": "https://mapbox.s3.amazonaws.com/gl-glyphs-256/{{fontstack}}/{{range}}.pbf", + "glyphs": "https://mapbox.s3.amazonaws.com/gl-glyphs-256/{fontstack}/{range}.pbf", "sources": { "mapbox": { "type": "vector", @@ -192,9 +192,7 @@ module.exports = { }, "layers": [{ "id": "background", - "render": { - "type": "background" - }, + "type": "background", "style": { "background-color": "@land" }, @@ -206,9 +204,7 @@ module.exports = { "source": "mapbox", "source-layer": "landcover", "filter": { "class": "snow" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@snow" }, @@ -220,9 +216,7 @@ module.exports = { "source": "mapbox", "source-layer": "landcover", "filter": { "class": "crop" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@crop" }, @@ -234,9 +228,7 @@ module.exports = { "source": "mapbox", "source-layer": "landcover", "filter": { "class": "grass" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@grass", "fill-opacity": { @@ -256,9 +248,7 @@ module.exports = { "source": "mapbox", "source-layer": "landcover", "filter": { "class": "scrub" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@scrub", "fill-opacity": { @@ -278,9 +268,7 @@ module.exports = { "source": "mapbox", "source-layer": "landcover", "filter": { "class": "wood" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@wood", "fill-opacity": { @@ -300,9 +288,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "wood" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@wood" }, @@ -315,9 +301,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "school" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@school" }, @@ -329,9 +313,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "sand" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@sand" }, @@ -344,9 +326,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "pitch" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "rgba(255,255,255,0.5)", "fill-outline-color": "@pitch" @@ -360,9 +340,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "park" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@park" }, @@ -374,9 +352,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "industrial" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "rgba(246,250,255,0.5)" }, @@ -388,9 +364,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "scrub" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@scrub" }, @@ -403,9 +377,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "grass" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@grass" }, @@ -418,9 +390,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "crop" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@crop" }, @@ -433,9 +403,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "rock" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@rock" }, @@ -448,9 +416,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "snow" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@snow" }, @@ -463,9 +429,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "hospital" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@hospital" }, @@ -477,9 +441,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse", "filter": { "class": "cemetery" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@cemetery" }, @@ -491,9 +453,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse_overlay", "filter": { "class": ["wetland", "wetland_noveg"] }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "rgba(210,225,225,0.2)", "fill-image": "wetland_noveg_64" @@ -507,9 +467,7 @@ module.exports = { "source": "mapbox", "source-layer": "landuse_overlay", "filter": { "class": ["breakwater", "pier"] }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@land" }, @@ -521,8 +479,8 @@ module.exports = { "source": "mapbox", "source-layer": "waterway", "filter": { "type": ["river", "canal"] }, + "type": "line", "render": { - "type": "line", "line-cap": "round" }, "style": { @@ -538,8 +496,8 @@ module.exports = { "source": "mapbox", "source-layer": "waterway", "filter": { "type": "stream" }, + "type": "line", "render": { - "type": "line", "line-cap": "round" }, "style": { @@ -554,9 +512,7 @@ module.exports = { "id": "building_shadow", "source": "mapbox", "source-layer": "building", - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "#d5d1c6", "fill-translate": [1, 1], @@ -608,9 +564,7 @@ module.exports = { "source": "mapbox", "source-layer": "hillshade", "filter": { "class": "full_highlight" }, - "render": { - "type": "fill" - }, + "type": "fill", "rasterize": "@hillshade_rasterize", "style": { "fill-color": "#fffff3", @@ -633,9 +587,7 @@ module.exports = { "source": "mapbox", "source-layer": "hillshade", "filter": { "class": "medium_highlight" }, - "render": { - "type": "fill" - }, + "type": "fill", "rasterize": "@hillshade_rasterize", "style": { "fill-color": "#ffd", @@ -658,9 +610,7 @@ module.exports = { "source": "mapbox", "source-layer": "hillshade", "filter": { "class": "medium_shadow" }, - "render": { - "type": "fill" - }, + "type": "fill", "rasterize": "@hillshade_rasterize", "style": { "fill-color": "#206", @@ -683,9 +633,7 @@ module.exports = { "source": "mapbox", "source-layer": "hillshade", "filter": { "class": "full_shadow" }, - "render": { - "type": "fill" - }, + "type": "fill", "rasterize": "@hillshade_rasterize", "style": { "fill-color": "#103", @@ -708,8 +656,8 @@ module.exports = { "source": "mapbox", "source-layer": "contour", "filter": { "index": 5 }, + "type": "line", "render": { - "type": "line", "line-join": "round" }, "style": { @@ -732,8 +680,8 @@ module.exports = { "id": "contour_line_regular", "source": "mapbox", "source-layer": "contour", + "type": "line", "render": { - "type": "line", "line-join": "round" }, "style": { @@ -757,9 +705,7 @@ module.exports = { "source": "mapbox", "source-layer": "barrier_line", "filter": { "class": "gate" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-width": 2.5, "line-color": "#aab" @@ -773,9 +719,7 @@ module.exports = { "source": "mapbox", "source-layer": "barrier_line", "filter": { "class": "fence" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#aeada3", "line-width": "@fence_width" @@ -789,9 +733,7 @@ module.exports = { "source": "mapbox", "source-layer": "barrier_line", "filter": { "class": "hedge" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#8de99b", "line-width": "@hedge_width" @@ -805,9 +747,7 @@ module.exports = { "source": "mapbox", "source-layer": "barrier_line", "filter": { "class": "land" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "@land", "line-width": "@barrier_line_land_width" @@ -821,9 +761,7 @@ module.exports = { "source": "mapbox", "source-layer": "barrier_line", "filter": { "class": "land" }, - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@land" }, @@ -835,9 +773,7 @@ module.exports = { "source": "mapbox", "source-layer": "barrier_line", "filter": { "class": "cliff" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#987", "line-width": 4 @@ -850,9 +786,7 @@ module.exports = { "id": "water", "source": "mapbox", "source-layer": "water", - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "@water", "fill-outline-color": "#a2bdc0" @@ -865,9 +799,7 @@ module.exports = { "id": "aeroway_fill", "source": "mapbox", "source-layer": "aeroway", - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "#ddd" }, @@ -879,9 +811,7 @@ module.exports = { "source": "mapbox", "source-layer": "aeroway", "filter": { "type": "runway" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#ddd", "line-width": "@runway_width" @@ -895,9 +825,7 @@ module.exports = { "source": "mapbox", "source-layer": "aeroway", "filter": { "type": "taxiway" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#ddd", "line-width": "@taxiway_width" @@ -911,9 +839,7 @@ module.exports = { "source": "mapbox", "source-layer": "tunnel", "filter": { "class": "motorway_link" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "@case", "line-dasharray": [6, 6], @@ -929,9 +855,7 @@ module.exports = { "source": "mapbox", "source-layer": "tunnel", "filter": { "class": "service" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#000", "line-opacity": 0.04, @@ -949,9 +873,7 @@ module.exports = { "source": "mapbox", "source-layer": "tunnel", "filter": { "class": "main" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "@case", "line-dasharray": [6, 6], @@ -975,9 +897,7 @@ module.exports = { "source": "mapbox", "source-layer": "tunnel", "filter": { "class": ["street", "street_limited"] }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#d9d5c6", "line-width": "@street_casing_width", @@ -1045,9 +965,7 @@ module.exports = { "source": "mapbox", "source-layer": "tunnel", "filter": { "class": "motorway" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "@case", "line-dasharray": [6, 6], @@ -1090,9 +1008,7 @@ module.exports = { "source": "mapbox", "source-layer": "road", "filter": { "class": "path" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#ffd", "line-opacity": 0.4, @@ -1110,9 +1026,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "type": "footway" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#bba", "line-dasharray": [10, 4], @@ -1128,9 +1042,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "type": "path" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#987", "line-dasharray": [10, 4], @@ -1154,9 +1066,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "type": "cycleway" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#488", "line-dasharray": [10, 4], @@ -1172,9 +1082,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "type": "mtb" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#488", "line-dasharray": [12, 4], @@ -1190,9 +1098,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "type": "piste" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#87b", "line-dasharray": [8, 4], @@ -1208,9 +1114,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "type": "steps" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#bba", "line-dasharray": [10, 4], @@ -1227,9 +1131,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "class": "major_rail" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#c8c4c0", "line-width": 0.8 @@ -1243,8 +1145,8 @@ module.exports = { "source": "mapbox", "source-layer": "road", "filter": { "class": "motorway_link" }, + "type": "line", "render": { - "type": "line", "line-join": "round", "line-cap": "round" }, @@ -1261,8 +1163,8 @@ module.exports = { "source": "mapbox", "source-layer": "road", "filter": { "class": "service" }, + "type": "line", "render": { - "type": "line", "line-join": "round", "line-cap": "round" }, @@ -1281,8 +1183,8 @@ module.exports = { "source": "mapbox", "source-layer": "road", "filter": { "class": "main" }, + "type": "line", "render": { - "type": "line", "line-join": "round", "line-cap": "round" }, @@ -1307,8 +1209,8 @@ module.exports = { "source": "mapbox", "source-layer": "road", "filter": { "class": ["street", "street_limited"] }, + "type": "line", "render": { - "type": "line", "line-join": "round", "line-cap": "round" }, @@ -1379,8 +1281,8 @@ module.exports = { "source": "mapbox", "source-layer": "road", "filter": { "class": "motorway" }, + "type": "line", "render": { - "type": "line", "line-join": "round", "line-cap": "round" }, @@ -1437,9 +1339,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "class": "motorway_link" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "@case", "line-width": "@motorway_link_casing_width" @@ -1453,9 +1353,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "class": "service" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#000", "line-opacity": 0.04, @@ -1471,9 +1369,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "class": "main" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "@case", "line-width": "@main_casing_width", @@ -1495,9 +1391,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "class": ["street", "street_limited"] }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "#d9d5c6", "line-width": "@street_casing_width", @@ -1565,9 +1459,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "class": "motorway" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "@case", "line-width": "@motorway_casing_width", @@ -1608,9 +1500,7 @@ module.exports = { "source": "mapbox", "source-layer": "bridge", "filter": { "class": "aerialway" }, - "render": { - "type": "line" - }, + "type": "line", "style": { "line-color": "white", "line-opacity": 0.5, @@ -1639,8 +1529,8 @@ module.exports = { "source": "mapbox", "source-layer": "admin", "filter": { "admin_level": [3, 4, 5] }, + "type": "line", "render": { - "type": "line", "line-join": "round" }, "style": { @@ -1666,8 +1556,8 @@ module.exports = { "source": "mapbox", "source-layer": "admin", "filter": { "admin_level": 2 }, + "type": "line", "render": { - "type": "line", "line-join": "round", "line-cap": "round" }, @@ -1684,8 +1574,8 @@ module.exports = { "source": "mapbox", "source-layer": "admin", "filter": { "maritime": 1 }, + "type": "line", "render": { - "type": "line", "line-join": "round", "line-cap": "round" }, @@ -1718,9 +1608,9 @@ module.exports = { "id": "country_label_line", "source": "mapbox", "source-layer": "country_label_line", + "type": "line", "render": { - "text-max-width": 5, - "type": "line" + "text-max-width": 5 }, "style": { "line-color": "@country_text", @@ -1736,10 +1626,10 @@ module.exports = { "id": "country_label", "source": "mapbox", "source-layer": "country_label", - "filter": { "$type": "point" }, + "filter": { "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 24, @@ -1761,10 +1651,10 @@ module.exports = { "id": "marine_label_line_1", "source": "mapbox", "source-layer": "marine_label", - "filter": { "$type": "line", "labelrank": 1 }, + "filter": { "$type": "LineString", "labelrank": 1 }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "curve", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 30, @@ -1791,10 +1681,10 @@ module.exports = { "id": "marine_label_line_2", "source": "mapbox", "source-layer": "marine_label", - "filter": { "$type": "line", "labelrank": 2 }, + "filter": { "$type": "LineString", "labelrank": 2 }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "curve", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 24, @@ -1820,10 +1710,10 @@ module.exports = { "id": "marine_label_line_3", "source": "mapbox", "source-layer": "marine_label", - "filter": { "$type": "line", "labelrank": 3 }, + "filter": { "$type": "LineString", "labelrank": 3 }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "curve", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 18, @@ -1849,10 +1739,10 @@ module.exports = { "id": "marine_label_line_other", "source": "mapbox", "source-layer": "marine_label", - "filter": { "$type": "line", "labelrank": [4, 5, 6] }, + "filter": { "$type": "LineString", "labelrank": [4, 5, 6] }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "curve", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 16, @@ -1878,10 +1768,10 @@ module.exports = { "id": "marine_label_point_1", "source": "mapbox", "source-layer": "marine_label", - "filter": { "$type": "point", "labelrank": 1 }, + "filter": { "$type": "Point", "labelrank": 1 }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 30, @@ -1909,10 +1799,10 @@ module.exports = { "id": "marine_label_point_2", "source": "mapbox", "source-layer": "marine_label", - "filter": { "$type": "point", "labelrank": 2 }, + "filter": { "$type": "Point", "labelrank": 2 }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 24, @@ -1940,10 +1830,10 @@ module.exports = { "id": "marine_label_point_3", "source": "mapbox", "source-layer": "marine_label", - "filter": { "$type": "point", "labelrank": 3 }, + "filter": { "$type": "Point", "labelrank": 3 }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 18, @@ -1971,10 +1861,10 @@ module.exports = { "id": "marine_label_point_other", "source": "mapbox", "source-layer": "marine_label", - "filter": { "$type": "point", "labelrank": [4, 5, 6] }, + "filter": { "$type": "Point", "labelrank": [4, 5, 6] }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 16, @@ -2002,10 +1892,10 @@ module.exports = { "id": "state_label", "source": "mapbox", "source-layer": "state_label", - "filter": { "$type": "point" }, + "filter": { "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Regular, Arial Unicode MS Regular", "text-max-size": 16, @@ -2033,10 +1923,10 @@ module.exports = { "id": "place_label_city", "source": "mapbox", "source-layer": "place_label", - "filter": { "type": "city", "$type": "point" }, + "filter": { "type": "city", "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 20, @@ -2064,10 +1954,10 @@ module.exports = { "id": "place_label_town", "source": "mapbox", "source-layer": "place_label", - "filter": { "type": "town", "$type": "point" }, + "filter": { "type": "town", "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 24, @@ -2095,10 +1985,10 @@ module.exports = { "id": "place_label_village", "source": "mapbox", "source-layer": "place_label", - "filter": { "type": "village", "$type": "point" }, + "filter": { "type": "village", "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 22, @@ -2126,10 +2016,10 @@ module.exports = { "id": "place_label_other", "source": "mapbox", "source-layer": "place_label", - "filter": { "type": ["hamlet", "suburb", "neighbourhood"], "$type": "point" }, + "filter": { "type": ["hamlet", "suburb", "neighbourhood"], "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold, Arial Unicode MS Bold", "text-max-size": 18, @@ -2156,10 +2046,10 @@ module.exports = { "id": "road_label_1", "source": "mapbox", "source-layer": "road_label", - "filter": { "class": ["motorway", "main"], "$type": "line" }, + "filter": { "class": ["motorway", "main"], "$type": "LineString" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "curve", "text-padding": 2, "text-font": "Open Sans Regular, Arial Unicode MS Regular", @@ -2182,10 +2072,10 @@ module.exports = { "id": "road_label_2", "source": "mapbox", "source-layer": "road_label", - "filter": { "class": ["street", "street_limited"], "$type": "line" }, + "filter": { "class": ["street", "street_limited"], "$type": "LineString" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "curve", "text-padding": 2, "text-font": "Open Sans Regular, Arial Unicode MS Regular", @@ -2208,10 +2098,10 @@ module.exports = { "id": "road_label_3", "source": "mapbox", "source-layer": "road_label", - "filter": { "class": ["service", "driveway", "path"], "$type": "line" }, + "filter": { "class": ["service", "driveway", "path"], "$type": "LineString" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "curve", "text-padding": 2, "text-font": "Open Sans Regular, Arial Unicode MS Regular", @@ -2234,11 +2124,11 @@ module.exports = { "id": "contour_label", "source": "mapbox", "source-layer": "contour", - "filter": { "index": [5, 10], "$type": "line" }, + "filter": { "index": [5, 10], "$type": "LineString" }, + "type": "text", "render": { - "type": "text", "text-path": "curve", - "text-field": "{{ele}} m", + "text-field": "{ele} m", "text-font": "Open Sans Regular, Arial Unicode MS Regular", "text-max-size": 10, "text-max-angle": 0.5 @@ -2259,10 +2149,10 @@ module.exports = { "id": "water_label", "source": "mapbox", "source-layer": "water_label", - "filter": { "$type": "point" }, + "filter": { "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 12, @@ -2280,10 +2170,10 @@ module.exports = { "id": "waterway_label", "source": "mapbox", "source-layer": "waterway_label", - "filter": { "$type": "line" }, + "filter": { "$type": "LineString" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "curve", "text-font": "Open Sans Semibold Italic, Arial Unicode MS Bold", "text-max-size": 12, @@ -2303,9 +2193,9 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "scalerank": [1, 2] }, + "type": "icon", "render": { - "type": "icon", - "icon-image": "{{maki}}-12", + "icon-image": "{maki}-12", "icon-size": 12 }, "style.night": {} @@ -2313,10 +2203,10 @@ module.exports = { "id": "poi_label_1-2", "source": "mapbox", "source-layer": "poi_label", - "filter": { "scalerank": [1, 2], "$type": "point" }, + "filter": { "scalerank": [1, 2], "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-padding": 2, "text-font": "Open Sans Semibold, Arial Unicode MS Bold", @@ -2341,9 +2231,9 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "scalerank": 3 }, + "type": "icon", "render": { - "type": "icon", - "icon-image": "{{maki}}-12", + "icon-image": "{maki}-12", "icon-size": 12 }, "style": { @@ -2362,10 +2252,10 @@ module.exports = { "id": "poi_label_3", "source": "mapbox", "source-layer": "poi_label", - "filter": { "scalerank": 3, "$type": "point" }, + "filter": { "scalerank": 3, "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-padding": 2, "text-font": "Open Sans Semibold, Arial Unicode MS Bold", @@ -2398,9 +2288,9 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "scalerank": 4 }, + "type": "icon", "render": { - "type": "icon", - "icon-image": "{{maki}}-12", + "icon-image": "{maki}-12", "icon-size": 12 }, "style": { @@ -2419,10 +2309,10 @@ module.exports = { "id": "poi_label_4", "source": "mapbox", "source-layer": "poi_label", - "filter": { "scalerank": 4, "$type": "point" }, + "filter": { "scalerank": 4, "$type": "Point" }, + "type": "text", "render": { - "type": "text", - "text-field": "{{name_en}}", + "text-field": "{name_en}", "text-path": "horizontal", "text-padding": 2, "text-font": "Open Sans Semibold, Arial Unicode MS Bold", @@ -2455,9 +2345,9 @@ module.exports = { "source": "mapbox", "source-layer": "poi_label", "filter": { "maki": "airport" }, + "type": "icon", "render": { - "type": "icon", - "icon-image": "{{maki}}-12", + "icon-image": "{maki}-12", "icon-size": 12 }, "style": { diff --git a/include/llmr/style/filter_expression.hpp b/include/llmr/style/filter_expression.hpp index 696aa04ae..5c7a400d8 100644 --- a/include/llmr/style/filter_expression.hpp +++ b/include/llmr/style/filter_expression.hpp @@ -85,7 +85,7 @@ public: enum class GeometryType : uint8_t { Any, Point, - Line, + LineString, Polygon }; diff --git a/include/llmr/style/style_bucket.hpp b/include/llmr/style/style_bucket.hpp index b963caba8..55ffeeb18 100644 --- a/include/llmr/style/style_bucket.hpp +++ b/include/llmr/style/style_bucket.hpp @@ -71,7 +71,8 @@ class StyleBucket { public: typedef std::shared_ptr Ptr; - BucketType type = BucketType::None; + StyleBucket(StyleLayerType type); + std::string name; std::shared_ptr source; std::string source_layer; diff --git a/include/llmr/style/style_layer.hpp b/include/llmr/style/style_layer.hpp index 5d8215cf0..4d6b8d1fa 100644 --- a/include/llmr/style/style_layer.hpp +++ b/include/llmr/style/style_layer.hpp @@ -61,6 +61,8 @@ public: // The name of this layer. const std::string id; + StyleLayerType type = StyleLayerType::Unknown; + // Bucket information, telling the renderer how to generate the geometries // for this layer (feature property filters, tessellation instructions, ...). std::shared_ptr bucket; diff --git a/include/llmr/style/types.hpp b/include/llmr/style/types.hpp index fd862c00d..fc0cc99ab 100644 --- a/include/llmr/style/types.hpp +++ b/include/llmr/style/types.hpp @@ -9,19 +9,20 @@ namespace llmr { // Stores a premultiplied color, with all four channels ranging from 0..1 typedef std::array Color; -enum class BucketType : uint8_t { - None, +enum class StyleLayerType : uint8_t { + Unknown, Fill, Line, Icon, Text, Raster, - Default = None + Composite, + Background }; -BucketType parseBucketType(const std::string &type); -const char *stringifyBucketType(BucketType type); -std::ostream& operator<<(std::ostream&, BucketType type); +StyleLayerType parseStyleLayerType(const std::string &type); +const char *stringifyStyleLayerType(StyleLayerType type); +std::ostream& operator<<(std::ostream&, StyleLayerType type); enum class WindingType : uint8_t { diff --git a/include/llmr/style/value.hpp b/include/llmr/style/value.hpp index bdcba4cad..fac316d3d 100644 --- a/include/llmr/style/value.hpp +++ b/include/llmr/style/value.hpp @@ -10,12 +10,6 @@ typedef util::variant Value; std::string toString(const Value &value); -inline std::string& operator+=(std::string& out, const Value& value) { - out += toString(value); - return out; -} - - Value parseValue(pbf data); namespace util { diff --git a/include/llmr/util/token.hpp b/include/llmr/util/token.hpp index db5b5fa0c..6795b97d1 100644 --- a/include/llmr/util/token.hpp +++ b/include/llmr/util/token.hpp @@ -9,40 +9,43 @@ namespace regex_impl = boost; namespace regex_impl = std; #endif +#include + namespace llmr { 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(); } -template -std::string replaceTokens(const std::string &source, const Tokens &tokens) { +template +std::string replaceTokens(const std::string &source, const Lookup &lookup) { std::string result; result.reserve(source.size()); bool token = false; for (auto token_it = regex_impl::sregex_token_iterator(source.begin(), source.end(), detail::tokenRegex, {-1, 1}); - token_it != detail::tokensEnd; ++token_it, token = !token) { + token_it != detail::tokensEnd; ++token_it, token = !token) { if (!token_it->matched) { continue; } - if (token) { - const auto it_prop = tokens.find(token_it->str()); - if (it_prop != tokens.end()) { - result += it_prop->second; - } - } else { - result += token_it->str(); - } + result += token ? lookup(token_it->str()) : token_it->str(); } return result; } +template +inline std::string replaceTokens(const std::string &source, const std::map &properties) { + return replaceTokens(source, [&properties](const std::string &token) -> std::string { + const auto it_prop = properties.find(token); + return it_prop != properties.end() ? toString(it_prop->second) : ""; + }); +} + } // end namespace util } // end namespace llmr diff --git a/scripts/compare_images.js b/scripts/compare_images.js new file mode 100755 index 000000000..258315756 --- /dev/null +++ b/scripts/compare_images.js @@ -0,0 +1,106 @@ +#!/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 = + '\n' + + '\n' + + '\n' + + ' \n' + + ' \n' + + ' \n' + + '\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 base = path.basename(name, '.info.json'); + + var actual = path.join(base_dir, base + '.actual.png'); + var expected = path.join(base_dir, base + '.expected.png'); + var diff = path.join(base_dir, base + '.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, info, error.trim(), Infinity); + exitCode = 2; + } else { + var difference = parseFloat(error.match(/^\d+(?:\.\d+)?\s+\(([^\)]+)\)\s*$/)[1]); + writeResult(base, info, '', difference); + + } + return processFiles(); + }); + compare.stdin.end(); +} + +function writeResult(base, 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 += + '\n' + + ' \n' + + ' \n' + + ' \n' + + '\n' + ; +} + +function done() { + html += "
ActualExpectedInfo
\n' + + '

' + base + '

\n' + + (error ? '

' + error + '

\n' : '') + + '
    \n' + + '
  • diff: ' + difference + '
  • \n' + + '
  • zoom: ' + (info.zoom || 0) + '
  • \n' + + '
  • latitude: ' + (info.latitude || 0) + '
  • \n' + + '
  • longitude: ' + (info.longitude || 0) + '
  • \n' + + '
  • angle: ' + (info.angle || 0) + '
  • \n' + + '
  • width: ' + (info.width || 0) + '
  • \n' + + '
  • height: ' + (info.height || 0) + '
  • \n' + + '
\n' + + '
\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/create_result_table.js b/scripts/create_result_table.js deleted file mode 100644 index ad604890f..000000000 --- a/scripts/create_result_table.js +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env node - -var fs = require('fs'); -var path = require('path'); - -var html = - '\n' + - '\n' + - '\n' + - ' \n' + - ' \n' + - ' \n' + - '\n'; - -var dir = fs.readdirSync('./test/fixtures/styles').filter(function(name) { - return name.match(/\.info\.json$/); -}).forEach(function(name) { - var info = require(path.join(path.resolve('.'), './test/fixtures/styles', name)); - var base = path.basename(name, '.info.json'); - - html += - '\n' + - ' \n' + - ' \n' + - ' \n' + - '\n' - ; -}); - -html += " layer_desc, RenderPass pass) { // 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->bucket->type) { - case BucketType::Fill: + switch (layer_desc->type) { + case StyleLayerType::Fill: if (!layer_desc->getProperties().isVisible()) return; break; - case BucketType::Line: + case StyleLayerType::Line: if (pass == Opaque) return; if (!layer_desc->getProperties().isVisible()) return; break; - case BucketType::Icon: + case StyleLayerType::Icon: if (pass == Opaque) return; if (!layer_desc->getProperties().isVisible()) return; break; - case BucketType::Text: + case StyleLayerType::Text: if (pass == Opaque) return; if (!layer_desc->getProperties().isVisible()) return; break; - case BucketType::Raster: + case StyleLayerType::Raster: if (pass == Translucent) return; if (!layer_desc->getProperties().isVisible()) return; break; @@ -634,7 +634,7 @@ void Map::renderLayer(std::shared_ptr layer_desc, RenderPass pass) { if (debug::renderTree) { std::cout << std::string(indent * 4, ' ') << "- " << layer_desc->id << " (" - << layer_desc->bucket->type << ")" << std::endl; + << layer_desc->type << ")" << std::endl; } layer_desc->bucket->source->render(painter, layer_desc); diff --git a/src/map/source.cpp b/src/map/source.cpp index da963272d..9736d5da5 100644 --- a/src/map/source.cpp +++ b/src/map/source.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,7 @@ std::string Source::normalizeSourceURL(const std::string &url, const std::string if (access_token.empty()) { throw std::runtime_error("You must provide a Mapbox API access token for Mapbox tile sources"); } - return "https://api.tiles.mapbox.com/v4/" + url.substr(t.length()) + "/%d/%d/%d.vector.pbf?access_token=" + access_token; + return std::string("https://api.tiles.mapbox.com/v4/") + url.substr(t.length()) + "/{z}/{x}/{y}.vector.pbf?access_token=" + access_token; } else { return url; } @@ -145,14 +146,17 @@ 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. - - std::string formed_url; + 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) { - formed_url = util::sprintf<256>(url, normalized_id.z, normalized_id.x, normalized_id.y); new_tile.data = std::make_shared(normalized_id, map, formed_url); } else if (type == SourceType::Raster) { - formed_url = util::sprintf<256>(url, normalized_id.z, normalized_id.x, normalized_id.y, (map.getState().getPixelRatio() > 1.0 ? "@2x" : "")); new_tile.data = std::make_shared(normalized_id, map, formed_url); } else { throw std::runtime_error("source type not implemented"); diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp index c420f8111..2208094cf 100644 --- a/src/map/tile_parser.cpp +++ b/src/map/tile_parser.cpp @@ -122,7 +122,7 @@ std::unique_ptr TileParser::createBucket(std::shared_ptr bu } else if (bucket_desc->render.is()) { return nullptr; } else { - fprintf(stderr, "[WARNING] unknown bucket render type for layer '%s'\n", bucket_desc->source_layer.c_str()); + fprintf(stderr, "[WARNING] unknown bucket render type for layer '%s' (source layer '%s')\n", bucket_desc->name.c_str(), bucket_desc->source_layer.c_str()); } } else { // The layer specified in the bucket does not exist. Do nothing. diff --git a/src/map/vector_tile.cpp b/src/map/vector_tile.cpp index fb4272668..a32db7fb3 100644 --- a/src/map/vector_tile.cpp +++ b/src/map/vector_tile.cpp @@ -272,7 +272,7 @@ void FilteredVectorTileLayer::iterator::operator++() { } else if (feature_pbf.tag == 3) { // geometry type switch (FeatureType(feature_pbf.varint())) { case FeatureType::Point: extractor.setType(FilterExpression::GeometryType::Point); break; - case FeatureType::LineString: extractor.setType(FilterExpression::GeometryType::Line); break; + case FeatureType::LineString: extractor.setType(FilterExpression::GeometryType::LineString); break; case FeatureType::Polygon: extractor.setType(FilterExpression::GeometryType::Polygon); break; default: break; } diff --git a/src/style/filter_expression.cpp b/src/style/filter_expression.cpp index 1a85b742e..58483acca 100644 --- a/src/style/filter_expression.cpp +++ b/src/style/filter_expression.cpp @@ -175,10 +175,10 @@ std::ostream& operator <<(std::ostream &s, FilterExpression::Operator op) { std::ostream& operator <<(std::ostream &s, FilterExpression::GeometryType type) { switch (type) { - case FilterExpression::GeometryType::Point: s << "point"; break; - case FilterExpression::GeometryType::Line: s << "line"; break; - case FilterExpression::GeometryType::Polygon: s << "polygon"; break; - case FilterExpression::GeometryType::Any: s << "any"; break; + case FilterExpression::GeometryType::Point: s << "Point"; break; + case FilterExpression::GeometryType::LineString: s << "LineString"; break; + case FilterExpression::GeometryType::Polygon: s << "Polygon"; break; + case FilterExpression::GeometryType::Any: s << ""; break; } return s; } @@ -265,9 +265,9 @@ void FilterExpression::setGeometryType(GeometryType g) { } FilterExpression::GeometryType parseGeometryType(const std::string &geometry) { - if (geometry == "point") return FilterExpression::GeometryType::Point; - if (geometry == "line") return FilterExpression::GeometryType::Line; - if (geometry == "polygon") return FilterExpression::GeometryType::Polygon; + if (geometry == "Point") return FilterExpression::GeometryType::Point; + if (geometry == "LineString") return FilterExpression::GeometryType::LineString; + if (geometry == "Polygon") return FilterExpression::GeometryType::Polygon; return FilterExpression::GeometryType::Any; } diff --git a/src/style/style_bucket.cpp b/src/style/style_bucket.cpp new file mode 100644 index 000000000..664d5a948 --- /dev/null +++ b/src/style/style_bucket.cpp @@ -0,0 +1,16 @@ +#include + +namespace llmr { + +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::Raster: render = StyleBucketRaster{}; break; + default: break; + } +} + +} \ No newline at end of file diff --git a/src/style/style_layer.cpp b/src/style/style_layer.cpp index 2646c363a..0c6287272 100644 --- a/src/style/style_layer.cpp +++ b/src/style/style_layer.cpp @@ -11,7 +11,7 @@ StyleLayer::StyleLayer(const std::string &id, std::map : id(id), styles(std::move(styles)), rasterize(std::move(rasterize)) {} bool StyleLayer::isBackground() const { - return id == "background"; + return type == StyleLayerType::Background && id == "background"; } void StyleLayer::setClasses(const std::vector &class_names, const timestamp now, @@ -257,19 +257,15 @@ void StyleLayer::updateProperties(float z, const timestamp now) { cleanupAppliedStyleProperties(now); - if (layers) { - applyStyleProperties(z, now); - } else if (bucket) { - switch (bucket->type) { - case BucketType::Fill: applyStyleProperties(z, now); break; - case BucketType::Line: applyStyleProperties(z, now); break; - case BucketType::Icon: applyStyleProperties(z, now); break; - case BucketType::Text: applyStyleProperties(z, now); break; - case BucketType::Raster: applyStyleProperties(z, now); break; - default: properties.set(); break; - } - } else { - return applyStyleProperties(z, now); + switch (type) { + case StyleLayerType::Fill: applyStyleProperties(z, now); break; + case StyleLayerType::Line: applyStyleProperties(z, now); break; + case StyleLayerType::Icon: applyStyleProperties(z, now); break; + case StyleLayerType::Text: applyStyleProperties(z, now); break; + case StyleLayerType::Raster: applyStyleProperties(z, now); break; + case StyleLayerType::Composite: applyStyleProperties(z, now); break; + case StyleLayerType::Background: applyStyleProperties(z, now); break; + default: properties.set(); break; } } diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp index e55c34808..9952a2286 100644 --- a/src/style/style_parser.cpp +++ b/src/style/style_parser.cpp @@ -553,6 +553,15 @@ void StyleParser::parseLayer(std::pair> &pair return; } + if (value.HasMember("type")) { + JSVal type = value["type"]; + if (!type.IsString()) { + fprintf(stderr, "[WARNING] layer type of '%s' must be a string\n", layer->id.c_str()); + } else { + layer->type = parseStyleLayerType({ type.GetString(), type.GetStringLength() }); + } + } + // Make sure we have not previously attempted to parse this layer. if (std::find(stack.begin(), stack.end(), layer.get()) != stack.end()) { fprintf(stderr, "[WARNING] layer reference of '%s' is circular\n", layer->id.c_str()); @@ -699,20 +708,24 @@ void StyleParser::parseReference(JSVal value, std::shared_ptr &layer parseLayer(it->second); stack.pop_front(); + std::shared_ptr reference = it->second.second; + + layer->type = reference->type; + if (reference->layers) { fprintf(stderr, "[WARNING] layer '%s' references composite layer\n", layer->id.c_str()); // We cannot parse this layer further. return; + } else { + layer->bucket = reference->bucket; } - - layer->bucket = reference->bucket; } #pragma mark - Parse Bucket void StyleParser::parseBucket(JSVal value, std::shared_ptr &layer) { - layer->bucket = std::make_shared(); + layer->bucket = std::make_shared(layer->type); // We name the buckets according to the layer that defined it. layer->bucket->name = layer->id; @@ -849,23 +862,14 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr &layer) { StyleBucket &bucket = *layer->bucket; - if (value.HasMember("type")) { - JSVal type = value["type"]; - if (type.IsString()) { - bucket.type = parseBucketType({ type.GetString(), type.GetStringLength() }); - } - } - - switch (bucket.type) { - case BucketType::Fill: { - bucket.render = StyleBucketFill{}; + switch (layer->type) { + case StyleLayerType::Fill: { StyleBucketFill &render = bucket.render.get(); parseRenderProperty(value, render.winding, "fill-winding", parseWindingType); } break; - case BucketType::Line: { - bucket.render = StyleBucketLine{}; + case StyleLayerType::Line: { StyleBucketLine &render = bucket.render.get(); parseRenderProperty(value, render.cap, "line-cap", parseCapType); @@ -874,8 +878,7 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr &layer) { parseRenderProperty(value, render.round_limit, "line-round-limit"); } break; - case BucketType::Icon: { - bucket.render = StyleBucketIcon{}; + case StyleLayerType::Icon: { StyleBucketIcon &render = bucket.render.get(); parseRenderProperty(value, render.size, "icon-size"); @@ -889,8 +892,7 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr &layer) { parseRenderProperty(value, render.translate_anchor, "icon-translate-anchor", parseTranslateAnchorType); } break; - case BucketType::Text: { - bucket.render = StyleBucketText{}; + case StyleLayerType::Text: { StyleBucketText &render = bucket.render.get(); parseRenderProperty(value, render.field, "text-field"); @@ -920,14 +922,8 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr &layer) { } parseRenderProperty(value, render.translate_anchor, "text-translate-anchor", parseTranslateAnchorType); } break; - - case BucketType::Raster: { - bucket.render = StyleBucketRaster{}; - } break; - default: - layer->bucket.reset(); - fprintf(stderr, "[WARNING] bucket type of layer '%s' is invalid\n", layer->id.c_str()); + // There are no render properties for these layer types. break; } } diff --git a/src/style/types.cpp b/src/style/types.cpp index 6cedbb1ab..41a17d0b1 100644 --- a/src/style/types.cpp +++ b/src/style/types.cpp @@ -4,28 +4,32 @@ namespace llmr { -BucketType parseBucketType(const std::string &type) { - if (type == "fill") return BucketType::Fill; - if (type == "line") return BucketType::Line; - if (type == "icon") return BucketType::Icon; - if (type == "text") return BucketType::Text; - if (type == "raster") return BucketType::Raster; - return BucketType::None; +StyleLayerType parseStyleLayerType(const std::string &type) { + if (type == "fill") return StyleLayerType::Fill; + if (type == "line") return StyleLayerType::Line; + if (type == "icon") return StyleLayerType::Icon; + if (type == "text") return StyleLayerType::Text; + if (type == "raster") return StyleLayerType::Raster; + if (type == "composite") return StyleLayerType::Composite; + if (type == "background") return StyleLayerType::Background; + return StyleLayerType::Unknown; } -const char *stringifyBucketType(BucketType type) { +const char *stringifyStyleLayerType(StyleLayerType type) { switch (type) { - case BucketType::Fill: return "fill"; - case BucketType::Line: return "line"; - case BucketType::Icon: return "icon"; - case BucketType::Text: return "text"; - case BucketType::Raster: return "raster"; - default: return "none"; + case StyleLayerType::Fill: return "fill"; + case StyleLayerType::Line: return "line"; + case StyleLayerType::Icon: return "icon"; + case StyleLayerType::Text: return "text"; + case StyleLayerType::Raster: return "raster"; + case StyleLayerType::Composite: return "composite"; + case StyleLayerType::Background: return "background"; + default: return "unknown"; } } -std::ostream& operator<<(std::ostream& os, BucketType type) { - return os << stringifyBucketType(type); +std::ostream& operator<<(std::ostream& os, StyleLayerType type) { + return os << stringifyStyleLayerType(type); } } diff --git a/src/text/glyph_store.cpp b/src/text/glyph_store.cpp index b79010670..f83d81089 100644 --- a/src/text/glyph_store.cpp +++ b/src/text/glyph_store.cpp @@ -120,12 +120,11 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl : future(promise.get_future().share()) { // Load the glyph set URL - - const std::map tokens {{ - { "fontstack", fontStack }, - { "range", std::to_string(glyphRange.first) + "-" + std::to_string(glyphRange.second) } - }}; - std::string url = util::replaceTokens(glyphURL, tokens); + std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string { + if (name == "fontstack") return fontStack; + if (name == "range") return std::to_string(glyphRange.first) + "-" + std::to_string(glyphRange.second); + return ""; + }); // TODO: Find more reliable URL normalization function std::replace(url.begin(), url.end(), ' ', '+'); diff --git a/test/fixtures/styles/0.style.json b/test/fixtures/styles/0.style.json index 06d7a1db1..c37774b66 100644 --- a/test/fixtures/styles/0.style.json +++ b/test/fixtures/styles/0.style.json @@ -3,7 +3,7 @@ "sources": { "mapbox": { "type": "vector", - "url": "tiles/%d-%d-%d.vector.pbf", + "url": "tiles/{z}-{x}-{y}.vector.pbf", "tileSize": 512, "maxZoom": 14 } @@ -11,9 +11,7 @@ "layers": [ { "id": "background", - "render": { - "type": "background" - }, + "type": "background", "style": { "background-color": "red" } @@ -22,9 +20,7 @@ "id": "water", "source": "mapbox", "source-layer": "water", - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "blue", "fill-antialias": false diff --git a/test/fixtures/styles/1.style.json b/test/fixtures/styles/1.style.json index d35c4bc0f..3165ec5b9 100644 --- a/test/fixtures/styles/1.style.json +++ b/test/fixtures/styles/1.style.json @@ -3,7 +3,7 @@ "sources": { "mapbox": { "type": "vector", - "url": "tiles/%d-%d-%d.vector.pbf", + "url": "tiles/{z}-{x}-{y}.vector.pbf", "tileSize": 512, "maxZoom": 14 } @@ -11,9 +11,7 @@ "layers": [ { "id": "background", - "render": { - "type": "background" - }, + "type": "background", "style": { "background-color": "red" } @@ -22,9 +20,7 @@ "id": "water", "source": "mapbox", "source-layer": "water", - "render": { - "type": "fill" - }, + "type": "fill", "style": { "fill-color": "blue" } diff --git a/test/headless.cpp b/test/headless.cpp index 0ed6e0a09..6f0219137 100644 --- a/test/headless.cpp +++ b/test/headless.cpp @@ -63,7 +63,6 @@ TEST_P(HeadlessTest, render) { const llmr::util::Image expected(expected_image_data, true); ASSERT_EQ(width, expected.getWidth()); ASSERT_EQ(height, expected.getHeight()); - ASSERT_EQ(0, std::memcmp(pixels.get(), expected.getData(), width * height * 4)); } INSTANTIATE_TEST_CASE_P(Headless, HeadlessTest,
ActualExpectedInfo
\n' + - '

' + base + '

\n' + - '
    \n' + - '
  • zoom: ' + (info.zoom || 0) + '
  • \n' + - '
  • latitude: ' + (info.latitude || 0) + '
  • \n' + - '
  • longitude: ' + (info.longitude || 0) + '
  • \n' + - '
  • angle: ' + (info.angle || 0) + '
  • \n' + - '
  • width: ' + (info.width || 0) + '
  • \n' + - '
  • height: ' + (info.height || 0) + '
  • \n' + - '
\n' + - '