diff --git a/app/src/main/assets/map_style_good_noshops.json b/app/src/main/assets/map_style_good_noshops.json index be46c83..c2de1bc 100644 --- a/app/src/main/assets/map_style_good_noshops.json +++ b/app/src/main/assets/map_style_good_noshops.json @@ -1,3107 +1,3107 @@ { "version": 8, "metadata": {"maputnik:renderer": "mlgljs"}, "sources": { "ne2_shaded": { "maxzoom": 6, "tileSize": 256, "tiles": [ "https://tiles.openfreemap.org/natural_earth/ne2sr/{z}/{x}/{y}.png" ], "type": "raster" }, "openmaptiles": { "type": "vector", "url": "https://tiles.openfreemap.org/planet" } }, "sprite": "https://tiles.openfreemap.org/sprites/ofm_f384/ofm", - "glyphs": "https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf", + "glyphs": "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf", "layers": [ { "id": "background", "type": "background", "paint": {"background-color": "#f8f4f0"} }, { "id": "landcover-glacier", "type": "fill", "source": "openmaptiles", "source-layer": "landcover", "filter": ["==", ["get", "subclass"], "glacier"], "paint": { "fill-color": "#fff", "fill-opacity": ["interpolate", ["linear"], ["zoom"], 0, 0.9, 10, 0.3] } }, { "id": "landuse-residential", "type": "fill", "source": "openmaptiles", "source-layer": "landuse", "filter": [ "match", ["get", "class"], ["neighbourhood", "residential"], true, false ], "paint": { "fill-color": [ "interpolate", ["linear"], ["zoom"], 12, "hsla(30,19%,90%,0.4)", 16, "hsla(30,19%,90%,0.2)" ] } }, { "id": "landuse-suburb", "type": "fill", "source": "openmaptiles", "source-layer": "landuse", "maxzoom": 10, "filter": ["==", ["get", "class"], "suburb"], "paint": { "fill-color": [ "interpolate", ["linear"], ["zoom"], 8, "hsla(30,19%,90%,0.4)", 10, "hsla(30,19%,90%,0.0)" ] } }, { "id": "landuse-commercial", "type": "fill", "source": "openmaptiles", "source-layer": "landuse", "filter": [ "all", ["match", ["geometry-type"], ["MultiPolygon", "Polygon"], true, false], ["==", ["get", "class"], "commercial"] ], "paint": {"fill-color": "hsla(0,60%,87%,0.23)"} }, { "id": "landuse-industrial", "type": "fill", "source": "openmaptiles", "source-layer": "landuse", "filter": [ "all", ["match", ["geometry-type"], ["MultiPolygon", "Polygon"], true, false], [ "match", ["get", "class"], ["dam", "garages", "industrial"], true, false ] ], "paint": {"fill-color": "hsla(49,100%,88%,0.34)"} }, { "id": "landuse-cemetery", "type": "fill", "source": "openmaptiles", "source-layer": "landuse", "filter": ["==", ["get", "class"], "cemetery"], "paint": {"fill-color": "#e0e4dd"} }, { "id": "landuse-hospital", "type": "fill", "source": "openmaptiles", "source-layer": "landuse", "filter": ["==", ["get", "class"], "hospital"], "paint": {"fill-color": "#fde"} }, { "id": "landuse-school", "type": "fill", "source": "openmaptiles", "source-layer": "landuse", "filter": ["==", ["get", "class"], "school"], "paint": {"fill-color": "#f0e8f8"} }, { "id": "landuse-railway", "type": "fill", "source": "openmaptiles", "source-layer": "landuse", "filter": ["==", ["get", "class"], "railway"], "paint": {"fill-color": "hsla(30,19%,90%,0.4)"} }, { "id": "park", "type": "fill", "source": "openmaptiles", "source-layer": "park", "filter": [ "match", ["geometry-type"], ["MultiPolygon", "Polygon"], true, false ], "paint": { "fill-color": "#d8e8c8", "fill-opacity": [ "interpolate", ["exponential", 1.8], ["zoom"], 9, 0.5, 12, 0.2 ] } }, { "id": "landcover-wood", "type": "fill", "source": "openmaptiles", "source-layer": "landcover", "filter": ["==", ["get", "class"], "wood"], "paint": { "fill-antialias": ["step", ["zoom"], false, 9, true], "fill-color": "#6a4", "fill-opacity": 0.1, "fill-outline-color": "hsla(0,0%,0%,0.03)" } }, { "id": "landcover-grass", "type": "fill", "source": "openmaptiles", "source-layer": "landcover", "filter": ["==", ["get", "class"], "grass"], "paint": {"fill-color": "#d8e8c8", "fill-opacity": 1} }, { "id": "landcover-grass-park", "type": "fill", "source": "openmaptiles", "source-layer": "park", "filter": ["==", ["get", "class"], "public_park"], "paint": {"fill-color": "#d8e8c8", "fill-opacity": 0.8} }, { "id": "waterway_tunnel", "type": "line", "source": "openmaptiles", "source-layer": "waterway", "minzoom": 14, "filter": [ "all", ["match", ["get", "class"], ["canal", "river", "stream"], true, false], ["==", ["get", "brunnel"], "tunnel"] ], "layout": {"line-cap": "round"}, "paint": { "line-color": "#a0c8f0", "line-dasharray": [2, 4], "line-width": [ "interpolate", ["exponential", 1.3], ["zoom"], 13, 0.5, 20, 6 ] } }, { "id": "waterway-other", "type": "line", "source": "openmaptiles", "source-layer": "waterway", "filter": [ "all", ["match", ["get", "class"], ["canal", "river", "stream"], false, true], ["==", ["get", "intermittent"], 0] ], "layout": {"line-cap": "round"}, "paint": { "line-color": "#a0c8f0", "line-width": [ "interpolate", ["exponential", 1.3], ["zoom"], 13, 0.5, 20, 2 ] } }, { "id": "waterway-other-intermittent", "type": "line", "source": "openmaptiles", "source-layer": "waterway", "filter": [ "all", ["match", ["get", "class"], ["canal", "river", "stream"], false, true], ["==", ["get", "intermittent"], 1] ], "layout": {"line-cap": "round"}, "paint": { "line-color": "#a0c8f0", "line-dasharray": [4, 3], "line-width": [ "interpolate", ["exponential", 1.3], ["zoom"], 13, 0.5, 20, 2 ] } }, { "id": "waterway-stream-canal", "type": "line", "source": "openmaptiles", "source-layer": "waterway", "filter": [ "all", ["match", ["get", "class"], ["canal", "stream"], true, false], ["!=", ["get", "brunnel"], "tunnel"], ["==", ["get", "intermittent"], 0] ], "layout": {"line-cap": "round"}, "paint": { "line-color": "#a0c8f0", "line-width": [ "interpolate", ["exponential", 1.3], ["zoom"], 13, 0.5, 20, 6 ] } }, { "id": "waterway-stream-canal-intermittent", "type": "line", "source": "openmaptiles", "source-layer": "waterway", "filter": [ "all", ["match", ["get", "class"], ["canal", "stream"], true, false], ["!=", ["get", "brunnel"], "tunnel"], ["==", ["get", "intermittent"], 1] ], "layout": {"line-cap": "round"}, "paint": { "line-color": "#a0c8f0", "line-dasharray": [4, 3], "line-width": [ "interpolate", ["exponential", 1.3], ["zoom"], 13, 0.5, 20, 6 ] } }, { "id": "waterway-river", "type": "line", "source": "openmaptiles", "source-layer": "waterway", "filter": [ "all", ["==", ["get", "class"], "river"], ["!=", ["get", "brunnel"], "tunnel"], ["!=", ["get", "intermittent"], 1] ], "layout": {"line-cap": "round"}, "paint": { "line-color": "#a0c8f0", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 10, 0.8, 20, 6 ] } }, { "id": "waterway-river-intermittent", "type": "line", "source": "openmaptiles", "source-layer": "waterway", "filter": [ "all", ["==", ["get", "class"], "river"], ["!=", ["get", "brunnel"], "tunnel"], ["==", ["get", "intermittent"], 1] ], "layout": {"line-cap": "round"}, "paint": { "line-color": "#a0c8f0", "line-dasharray": [3, 2.5], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 10, 0.8, 20, 6 ] } }, { "id": "water", "type": "fill", "source": "openmaptiles", "source-layer": "water", "filter": [ "all", ["!=", ["get", "intermittent"], 1], ["!=", ["get", "brunnel"], "tunnel"] ], "paint": {"fill-color": "#AECFE2"} }, { "id": "water-intermittent", "type": "fill", "source": "openmaptiles", "source-layer": "water", "filter": ["==", ["get", "intermittent"], 1], "paint": {"fill-color": "hsl(210,67%,85%)", "fill-opacity": 0.7} }, { "id": "landcover-ice-shelf", "type": "fill", "source": "openmaptiles", "source-layer": "landcover", "filter": ["==", ["get", "subclass"], "ice_shelf"], "paint": { "fill-color": "#fff", "fill-opacity": ["interpolate", ["linear"], ["zoom"], 0, 0.9, 10, 0.3] } }, { "id": "landcover-sand", "type": "fill", "source": "openmaptiles", "source-layer": "landcover", "filter": ["==", ["get", "class"], "sand"], "paint": {"fill-color": "rgba(245, 238, 188, 1)", "fill-opacity": 1} }, { "id": "building", "type": "fill", "source": "openmaptiles", "source-layer": "building", "paint": { "fill-antialias": true, "fill-color": [ "interpolate", ["linear"], ["zoom"], 15.5, "#f2eae2", 16, "#dfdbd7" ] } }, { "id": "building-top", "type": "fill", "source": "openmaptiles", "source-layer": "building", "paint": { "fill-color": "#f2eae2", "fill-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0, 16, 1], "fill-outline-color": "#dfdbd7", "fill-translate": [ "interpolate", ["linear"], ["zoom"], 14, ["literal", [0, 0]], 16, ["literal", [-2, -2]] ] } }, { "id": "tunnel-service-track-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["match", ["get", "class"], ["service", "track"], true, false] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#cfcdca", "line-dasharray": [0.5, 0.25], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 15, 1, 16, 4, 20, 11 ] } }, { "id": "tunnel-motorway-link-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "class"], "motorway"], ["==", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "rgba(200, 147, 102, 1)", "line-dasharray": [0.5, 0.25], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12, 1, 13, 3, 14, 4, 20, 15 ] } }, { "id": "tunnel-minor-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "class"], "minor"] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#cfcdca", "line-dasharray": [0.5, 0.25], "line-opacity": ["interpolate", ["linear"], ["zoom"], 12, 0, 12.5, 1], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12, 0.5, 13, 1, 14, 4, 20, 15 ] } }, { "id": "tunnel-link-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], [ "match", ["get", "class"], ["primary", "secondary", "tertiary", "trunk"], true, false ], ["==", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-dasharray": [0.5, 0.25], "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12, 1, 13, 3, 14, 4, 20, 15 ] } }, { "id": "tunnel-secondary-tertiary-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["match", ["get", "class"], ["secondary", "tertiary"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-dasharray": [0.5, 0.25], "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 8, 1.5, 20, 17 ] } }, { "id": "tunnel-trunk-primary-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["match", ["get", "class"], ["primary", "trunk"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 5, 0.4, 6, 0.6, 7, 1.5, 20, 22 ] } }, { "id": "tunnel-motorway-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "class"], "motorway"], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-dasharray": [0.5, 0.25], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 5, 0.4, 6, 0.6, 7, 1.5, 20, 22 ] } }, { "id": "tunnel-path", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "class"], "path"] ], "paint": { "line-color": "#cba", "line-dasharray": [1.5, 0.75], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 15, 1.2, 20, 4 ] } }, { "id": "tunnel-motorway-link", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "class"], "motorway"], ["==", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "rgba(244, 209, 158, 1)", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12.5, 0, 13, 1.5, 14, 2.5, 20, 11.5 ] } }, { "id": "tunnel-service-track", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["match", ["get", "class"], ["service", "track"], true, false] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fff", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 15.5, 0, 16, 2, 20, 7.5 ] } }, { "id": "tunnel-link", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], [ "match", ["get", "class"], ["primary", "secondary", "tertiary", "trunk"], true, false ], ["==", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fff4c6", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12.5, 0, 13, 1.5, 14, 2.5, 20, 11.5 ] } }, { "id": "tunnel-minor", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "class"], "minor"] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fff", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 13.5, 0, 14, 2.5, 20, 11.5 ] } }, { "id": "tunnel-secondary-tertiary", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["match", ["get", "class"], ["secondary", "tertiary"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fff4c6", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 6.5, 0, 7, 0.5, 20, 10 ] } }, { "id": "tunnel-trunk-primary", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["match", ["get", "class"], ["primary", "trunk"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fff4c6", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 6.5, 0, 7, 0.5, 20, 18 ] } }, { "id": "tunnel-motorway", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "class"], "motorway"], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#ffdaa6", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 6.5, 0, 7, 0.5, 20, 18 ] } }, { "id": "tunnel-railway", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "tunnel"], ["==", ["get", "class"], "rail"] ], "paint": { "line-color": "#bbb", "line-dasharray": [2, 2], "line-width": [ "interpolate", ["exponential", 1.4], ["zoom"], 14, 0.4, 15, 0.75, 20, 2 ] } }, { "id": "ferry", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": ["match", ["get", "class"], ["ferry"], true, false], "layout": {"line-join": "round"}, "paint": { "line-color": "rgba(108, 159, 182, 1)", "line-dasharray": [2, 2], "line-width": 1.1 } }, { "id": "aeroway-taxiway-casing", "type": "line", "source": "openmaptiles", "source-layer": "aeroway", "minzoom": 12, "filter": ["match", ["get", "class"], ["taxiway"], true, false], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "rgba(153, 153, 153, 1)", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.5], ["zoom"], 11, 2, 17, 12 ] } }, { "id": "aeroway-runway-casing", "type": "line", "source": "openmaptiles", "source-layer": "aeroway", "minzoom": 12, "filter": ["match", ["get", "class"], ["runway"], true, false], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "rgba(153, 153, 153, 1)", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.5], ["zoom"], 11, 5, 17, 55 ] } }, { "id": "aeroway-area", "type": "fill", "source": "openmaptiles", "source-layer": "aeroway", "minzoom": 4, "filter": [ "all", ["match", ["geometry-type"], ["MultiPolygon", "Polygon"], true, false], ["match", ["get", "class"], ["runway", "taxiway"], true, false] ], "paint": { "fill-color": "rgba(255, 255, 255, 1)", "fill-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0, 14, 1] } }, { "id": "aeroway-taxiway", "type": "line", "source": "openmaptiles", "source-layer": "aeroway", "minzoom": 4, "filter": [ "all", ["match", ["get", "class"], ["taxiway"], true, false], [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "rgba(255, 255, 255, 1)", "line-opacity": ["interpolate", ["linear"], ["zoom"], 11, 0, 12, 1], "line-width": [ "interpolate", ["exponential", 1.5], ["zoom"], 11, 1, 17, 10 ] } }, { "id": "aeroway-runway", "type": "line", "source": "openmaptiles", "source-layer": "aeroway", "minzoom": 4, "filter": [ "all", ["match", ["get", "class"], ["runway"], true, false], [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "rgba(255, 255, 255, 1)", "line-opacity": ["interpolate", ["linear"], ["zoom"], 11, 0, 12, 1], "line-width": [ "interpolate", ["exponential", 1.5], ["zoom"], 11, 4, 17, 50 ] } }, { "id": "road_area_pier", "type": "fill", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["match", ["geometry-type"], ["MultiPolygon", "Polygon"], true, false], ["==", ["get", "class"], "pier"] ], "paint": {"fill-antialias": true, "fill-color": "#f8f4f0"} }, { "id": "road_pier", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["match", ["get", "class"], ["pier"], true, false] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#f8f4f0", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 15, 1, 17, 4 ] } }, { "id": "highway-area", "type": "fill", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["match", ["geometry-type"], ["MultiPolygon", "Polygon"], true, false], ["match", ["get", "class"], ["pier"], false, true] ], "paint": { "fill-antialias": false, "fill-color": "hsla(0,0%,89%,0.56)", "fill-opacity": 0.9, "fill-outline-color": "#cfcdca" } }, { "id": "highway-motorway-link-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["==", ["get", "class"], "motorway"], ["==", ["get", "ramp"], 1] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12, 1, 13, 3, 14, 4, 20, 15 ] } }, { "id": "highway-link-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 13, "filter": [ "all", ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], [ "match", ["get", "class"], ["primary", "secondary", "tertiary", "trunk"], true, false ], ["==", ["get", "ramp"], 1] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12, 1, 13, 3, 14, 4, 20, 15 ] } }, { "id": "highway-minor-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["!=", ["get", "brunnel"], "tunnel"], ["match", ["get", "class"], ["minor", "service", "track"], true, false] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#cfcdca", "line-opacity": ["interpolate", ["linear"], ["zoom"], 12, 0, 12.5, 1], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12, 0.5, 13, 1, 14, 4, 20, 15 ] } }, { "id": "highway-secondary-tertiary-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["match", ["get", "class"], ["secondary", "tertiary"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-cap": "butt", "line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 8, 1.5, 20, 17 ] } }, { "id": "highway-primary-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 5, "filter": [ "all", ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["match", ["get", "class"], ["primary"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-cap": "butt", "line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-opacity": ["interpolate", ["linear"], ["zoom"], 7, 0, 8, 1], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 7, 0, 8, 0.6, 9, 1.5, 20, 22 ] } }, { "id": "highway-trunk-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 5, "filter": [ "all", ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["match", ["get", "class"], ["trunk"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-cap": "butt", "line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-opacity": ["interpolate", ["linear"], ["zoom"], 5, 0, 6, 1], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 5, 0, 6, 0.6, 7, 1.5, 20, 22 ] } }, { "id": "highway-motorway-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 4, "filter": [ "all", ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["==", ["get", "class"], "motorway"], ["!=", ["get", "ramp"], 1] ], "layout": {"line-cap": "butt", "line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-opacity": ["interpolate", ["linear"], ["zoom"], 4, 0, 5, 1], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 4, 0, 5, 0.4, 6, 0.6, 7, 1.5, 20, 22 ] } }, { "id": "highway-path", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["==", ["get", "class"], "path"] ], "paint": { "line-color": "#cba", "line-dasharray": [1.5, 0.75], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 15, 1.2, 20, 4 ] } }, { "id": "highway-motorway-link", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 12, "filter": [ "all", ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["==", ["get", "class"], "motorway"], ["==", ["get", "ramp"], 1] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#fc8", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12.5, 0, 13, 1.5, 14, 2.5, 20, 11.5 ] } }, { "id": "highway-link", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 13, "filter": [ "all", ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], [ "match", ["get", "class"], ["primary", "secondary", "tertiary", "trunk"], true, false ], ["==", ["get", "ramp"], 1] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#fea", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12.5, 0, 13, 1.5, 14, 2.5, 20, 11.5 ] } }, { "id": "highway-minor", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["!=", ["get", "brunnel"], "tunnel"], ["match", ["get", "class"], ["minor", "service", "track"], true, false] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#fff", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 13.5, 0, 14, 2.5, 20, 11.5 ] } }, { "id": "highway-secondary-tertiary", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["match", ["get", "class"], ["secondary", "tertiary"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#fea", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 6.5, 0, 8, 0.5, 20, 13 ] } }, { "id": "highway-primary", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["match", ["get", "class"], ["primary"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#fea", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 8.5, 0, 9, 0.5, 20, 18 ] } }, { "id": "highway-trunk", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["match", ["get", "class"], ["trunk"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#fea", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 6.5, 0, 7, 0.5, 20, 18 ] } }, { "id": "highway-motorway", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 5, "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["==", ["get", "class"], "motorway"], ["!=", ["get", "ramp"], 1] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#fc8", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 6.5, 0, 7, 0.5, 20, 18 ] } }, { "id": "railway-transit", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["==", ["get", "class"], "transit"], ["match", ["get", "brunnel"], ["tunnel"], false, true] ], "paint": { "line-color": "hsla(0,0%,73%,0.77)", "line-width": [ "interpolate", ["exponential", 1.4], ["zoom"], 14, 0.4, 20, 1 ] } }, { "id": "railway-transit-hatching", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["==", ["get", "class"], "transit"], ["match", ["get", "brunnel"], ["tunnel"], false, true] ], "paint": { "line-color": "hsla(0,0%,73%,0.68)", "line-dasharray": [0.2, 8], "line-width": [ "interpolate", ["exponential", 1.4], ["zoom"], 14.5, 0, 15, 2, 20, 6 ] } }, { "id": "railway-service", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["==", ["get", "class"], "rail"], ["has", "service"] ], "paint": { "line-color": "hsla(0,0%,73%,0.77)", "line-width": [ "interpolate", ["exponential", 1.4], ["zoom"], 14, 0.4, 20, 1 ] } }, { "id": "railway-service-hatching", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["==", ["get", "class"], "rail"], ["has", "service"] ], "paint": { "line-color": "hsla(0,0%,73%,0.68)", "line-dasharray": [0.2, 8], "line-width": [ "interpolate", ["exponential", 1.4], ["zoom"], 14.5, 0, 15, 2, 20, 6 ] } }, { "id": "railway", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["!", ["has", "service"]], ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["==", ["get", "class"], "rail"] ], "paint": { "line-color": "#bbb", "line-width": [ "interpolate", ["exponential", 1.4], ["zoom"], 14, 0.4, 15, 0.75, 20, 2 ] } }, { "id": "railway-hatching", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["!", ["has", "service"]], ["match", ["get", "brunnel"], ["bridge", "tunnel"], false, true], ["==", ["get", "class"], "rail"] ], "paint": { "line-color": "#bbb", "line-dasharray": [0.2, 8], "line-width": [ "interpolate", ["exponential", 1.4], ["zoom"], 14.5, 0, 15, 3, 20, 8 ] } }, { "id": "bridge-motorway-link-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["==", ["get", "class"], "motorway"], ["==", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12, 1, 13, 3, 14, 4, 20, 19 ] } }, { "id": "bridge-link-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], [ "match", ["get", "class"], ["primary", "secondary", "tertiary", "trunk"], true, false ], ["==", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12, 1, 13, 3, 14, 4, 20, 19 ] } }, { "id": "bridge-secondary-tertiary-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["match", ["get", "class"], ["secondary", "tertiary"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 5, 0.4, 7, 0.6, 8, 1.5, 20, 21 ] } }, { "id": "bridge-trunk-primary-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["match", ["get", "class"], ["primary", "trunk"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "hsl(28,76%,67%)", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 5, 0.4, 6, 0.6, 7, 1.5, 20, 26 ] } }, { "id": "bridge-motorway-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["==", ["get", "class"], "motorway"], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#e9ac77", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 5, 0.4, 6, 0.6, 7, 1.5, 20, 26 ] } }, { "id": "bridge-minor-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["==", ["get", "brunnel"], "bridge"], ["match", ["get", "class"], ["minor", "service", "track"], true, false] ], "layout": {"line-cap": "butt", "line-join": "round"}, "paint": { "line-color": "#cfcdca", "line-opacity": ["interpolate", ["linear"], ["zoom"], 12, 0, 12.5, 1], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12, 0.5, 13, 1, 14, 6, 20, 24 ] } }, { "id": "bridge-path-casing", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["==", ["get", "brunnel"], "bridge"], ["==", ["get", "class"], "path"] ], "paint": { "line-color": "#f8f4f0", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 15, 1.2, 20, 18 ] } }, { "id": "bridge-path", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["==", ["get", "brunnel"], "bridge"], ["==", ["get", "class"], "path"] ], "paint": { "line-color": "#cba", "line-dasharray": [1.5, 0.75], "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 15, 1.2, 20, 4 ] } }, { "id": "bridge-motorway-link", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["==", ["get", "class"], "motorway"], ["==", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fc8", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12.5, 0, 13, 1.5, 14, 2.5, 20, 11.5 ] } }, { "id": "bridge-link", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], [ "match", ["get", "class"], ["primary", "secondary", "tertiary", "trunk"], true, false ], ["==", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fea", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 12.5, 0, 13, 1.5, 14, 2.5, 20, 11.5 ] } }, { "id": "bridge-minor", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["==", ["get", "brunnel"], "bridge"], ["match", ["get", "class"], ["minor", "service", "track"], true, false] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "#fff", "line-opacity": 1, "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 13.5, 0, 14, 2.5, 20, 11.5 ] } }, { "id": "bridge-secondary-tertiary", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["match", ["get", "class"], ["secondary", "tertiary"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fea", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 6.5, 0, 8, 0.5, 20, 13 ] } }, { "id": "bridge-trunk-primary", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["match", ["get", "class"], ["primary", "trunk"], true, false], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fea", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 6.5, 0, 7, 0.5, 20, 18 ] } }, { "id": "bridge-motorway", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["==", ["get", "class"], "motorway"], ["!=", ["get", "ramp"], 1] ], "layout": {"line-join": "round"}, "paint": { "line-color": "#fc8", "line-width": [ "interpolate", ["exponential", 1.2], ["zoom"], 6.5, 0, 7, 0.5, 20, 18 ] } }, { "id": "bridge-railway", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["==", ["get", "class"], "rail"] ], "paint": { "line-color": "#bbb", "line-width": [ "interpolate", ["exponential", 1.4], ["zoom"], 14, 0.4, 15, 0.75, 20, 2 ] } }, { "id": "bridge-railway-hatching", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "filter": [ "all", ["==", ["get", "brunnel"], "bridge"], ["==", ["get", "class"], "rail"] ], "paint": { "line-color": "#bbb", "line-dasharray": [0.2, 8], "line-width": [ "interpolate", ["exponential", 1.4], ["zoom"], 14.5, 0, 15, 3, 20, 8 ] } }, { "id": "cablecar", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 13, "filter": ["==", ["get", "subclass"], "cable_car"], "layout": {"line-cap": "round"}, "paint": { "line-color": "hsl(0,0%,70%)", "line-width": ["interpolate", ["linear"], ["zoom"], 11, 1, 19, 2.5] } }, { "id": "cablecar-dash", "type": "line", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 13, "filter": ["==", ["get", "subclass"], "cable_car"], "layout": {"line-cap": "round"}, "paint": { "line-color": "hsl(0,0%,70%)", "line-dasharray": [2, 3], "line-width": ["interpolate", ["linear"], ["zoom"], 11, 3, 19, 5.5] } }, { "id": "boundary_3", "type": "line", "source": "openmaptiles", "source-layer": "boundary", "minzoom": 5, "filter": [ "all", [">=", ["get", "admin_level"], 3], ["<=", ["get", "admin_level"], 6], ["!=", ["get", "maritime"], 1], ["!=", ["get", "disputed"], 1], ["!", ["has", "claimed_by"]] ], "paint": { "line-color": "hsl(0,0%,70%)", "line-dasharray": [1, 1], "line-width": ["interpolate", ["linear", 1], ["zoom"], 7, 1, 11, 2] } }, { "id": "boundary_2", "type": "line", "source": "openmaptiles", "source-layer": "boundary", "filter": [ "all", ["==", ["get", "admin_level"], 2], ["!=", ["get", "maritime"], 1], ["!=", ["get", "disputed"], 1], ["!", ["has", "claimed_by"]] ], "layout": {"line-cap": "round", "line-join": "round"}, "paint": { "line-color": "hsl(248,7%,66%)", "line-opacity": ["interpolate", ["linear"], ["zoom"], 0, 0.4, 4, 1], "line-width": ["interpolate", ["linear"], ["zoom"], 3, 1, 5, 1.2, 12, 3] } }, { "id": "boundary_disputed", "type": "line", "source": "openmaptiles", "source-layer": "boundary", "filter": [ "all", ["!=", ["get", "maritime"], 1], ["==", ["get", "disputed"], 1] ], "paint": { "line-color": "hsl(248,7%,66%)", "line-dasharray": [1, 2], "line-width": ["interpolate", ["linear"], ["zoom"], 3, 1, 5, 1.2, 12, 3] } }, { "id": "road_oneway", "type": "symbol", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 15, "filter": [ "all", ["==", ["get", "oneway"], 1], [ "match", ["get", "class"], [ "minor", "motorway", "primary", "secondary", "service", "tertiary", "trunk" ], true, false ] ], "layout": { "icon-image": "oneway", "icon-padding": 2, "icon-rotate": 90, "icon-rotation-alignment": "map", "icon-size": ["interpolate", ["linear"], ["zoom"], 15, 0.5, 19, 1], "symbol-placement": "line", "symbol-spacing": 75 }, "paint": {"icon-opacity": 0.5} }, { "id": "road_oneway_opposite", "type": "symbol", "source": "openmaptiles", "source-layer": "transportation", "minzoom": 15, "filter": [ "all", ["==", ["get", "oneway"], -1], [ "match", ["get", "class"], [ "minor", "motorway", "primary", "secondary", "service", "tertiary", "trunk" ], true, false ] ], "layout": { "icon-image": "oneway", "icon-padding": 2, "icon-rotate": -90, "icon-rotation-alignment": "map", "icon-size": ["interpolate", ["linear"], ["zoom"], 15, 0.5, 19, 1], "symbol-placement": "line", "symbol-spacing": 75 }, "paint": {"icon-opacity": 0.5} }, { "id": "waterway_line_label", "type": "symbol", "source": "openmaptiles", "source-layer": "waterway", "minzoom": 10, "filter": [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], "layout": { "symbol-placement": "line", "symbol-spacing": 350, "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], " ", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Italic"], "text-letter-spacing": 0.2, "text-max-width": 5, "text-size": 14 }, "paint": { "text-color": "#74aee9", "text-halo-color": "rgba(255,255,255,0.7)", "text-halo-width": 1.5 } }, { "id": "water_name_point_label", "type": "symbol", "source": "openmaptiles", "source-layer": "water_name", "filter": [ "match", ["geometry-type"], ["MultiPoint", "Point"], true, false ], "layout": { "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Italic"], "text-letter-spacing": 0.2, "text-max-width": 5, "text-size": ["interpolate", ["linear"], ["zoom"], 0, 10, 8, 14] }, "paint": { "text-color": "#495e91", "text-halo-color": "rgba(255,255,255,0.7)", "text-halo-width": 1.5 } }, { "id": "water_name_line_label", "type": "symbol", "source": "openmaptiles", "source-layer": "water_name", "filter": [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], "layout": { "symbol-placement": "line", "symbol-spacing": 350, "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], " ", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Italic"], "text-letter-spacing": 0.2, "text-max-width": 5, "text-size": 14 }, "paint": { "text-color": "#495e91", "text-halo-color": "rgba(255,255,255,0.7)", "text-halo-width": 1.5 } }, { "id": "poi_r1", "type": "symbol", "source": "openmaptiles", "source-layer": "poi", "minzoom": 15, "filter": [ "all", ["match", ["geometry-type"], ["MultiPoint", "Point"], true, false], [">=", ["get", "rank"], 1], ["<=", ["get", "rank"], 6], ["!=", ["get", "subclass"], "bus_stop"], ["!=", ["get", "subclass"], "tram_stop"], ["!=", ["get", "class"], "shop"] ], "layout": { "icon-image": [ "match", ["get", "subclass"], ["florist", "furniture"], ["get", "subclass"], ["get", "class"] ], "text-anchor": "top", "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Italic"], "text-max-width": 9, "text-offset": [0, 0.6], "text-size": 12, "visibility": "visible" }, "paint": { "text-color": "#666", "text-halo-blur": 0.5, "text-halo-color": "#ffffff", "text-halo-width": 1 } }, { "id": "highway-name-path", "type": "symbol", "source": "openmaptiles", "source-layer": "transportation_name", "minzoom": 15.5, "filter": ["==", ["get", "class"], "path"], "layout": { "symbol-placement": "line", "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], " ", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Regular"], "text-rotation-alignment": "map", "text-size": ["interpolate", ["linear"], ["zoom"], 13, 12, 14, 13] }, "paint": { "text-color": "hsl(30,23%,62%)", "text-halo-color": "#f8f4f0", "text-halo-width": 0.5 } }, { "id": "highway-name-minor", "type": "symbol", "source": "openmaptiles", "source-layer": "transportation_name", "minzoom": 15, "filter": [ "all", [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["match", ["get", "class"], ["minor", "service", "track"], true, false] ], "layout": { "symbol-placement": "line", "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], " ", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Regular"], "text-rotation-alignment": "map", "text-size": ["interpolate", ["linear"], ["zoom"], 13, 12, 14, 13] }, "paint": { "text-color": "#666", "text-halo-blur": 0.5, "text-halo-width": 1 } }, { "id": "highway-name-major", "type": "symbol", "source": "openmaptiles", "source-layer": "transportation_name", "minzoom": 12.2, "filter": [ "match", ["get", "class"], ["primary", "secondary", "tertiary", "trunk"], true, false ], "layout": { "symbol-placement": "line", "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], " ", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Regular"], "text-rotation-alignment": "map", "text-size": ["interpolate", ["linear"], ["zoom"], 13, 12, 14, 13] }, "paint": { "text-color": "#666", "text-halo-blur": 0.5, "text-halo-width": 1 } }, { "id": "highway-shield-non-us", "type": "symbol", "source": "openmaptiles", "source-layer": "transportation_name", "minzoom": 8, "filter": [ "all", ["<=", ["get", "ref_length"], 6], [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], [ "match", ["get", "network"], ["us-highway", "us-interstate", "us-state"], false, true ] ], "layout": { "icon-image": ["concat", "road_", ["get", "ref_length"]], "icon-rotation-alignment": "viewport", "icon-size": 1, "symbol-placement": ["step", ["zoom"], "point", 11, "line"], "symbol-spacing": 200, "text-field": ["to-string", ["get", "ref"]], "text-font": ["Noto Sans Regular"], "text-rotation-alignment": "viewport", "text-size": 10 } }, { "id": "highway-shield-us-interstate", "type": "symbol", "source": "openmaptiles", "source-layer": "transportation_name", "minzoom": 7, "filter": [ "all", ["<=", ["get", "ref_length"], 6], [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["match", ["get", "network"], ["us-interstate"], true, false] ], "layout": { "icon-image": [ "concat", ["get", "network"], "_", ["get", "ref_length"] ], "icon-rotation-alignment": "viewport", "icon-size": 1, "symbol-placement": ["step", ["zoom"], "point", 7, "line", 8, "line"], "symbol-spacing": 200, "text-field": ["to-string", ["get", "ref"]], "text-font": ["Noto Sans Regular"], "text-rotation-alignment": "viewport", "text-size": 10 } }, { "id": "road_shield_us", "type": "symbol", "source": "openmaptiles", "source-layer": "transportation_name", "minzoom": 9, "filter": [ "all", ["<=", ["get", "ref_length"], 6], [ "match", ["geometry-type"], ["LineString", "MultiLineString"], true, false ], ["match", ["get", "network"], ["us-highway", "us-state"], true, false] ], "layout": { "icon-image": [ "concat", ["get", "network"], "_", ["get", "ref_length"] ], "icon-rotation-alignment": "viewport", "icon-size": 1, "symbol-placement": ["step", ["zoom"], "point", 11, "line"], "symbol-spacing": 200, "text-field": ["to-string", ["get", "ref"]], "text-font": ["Noto Sans Regular"], "text-rotation-alignment": "viewport", "text-size": 10 } }, { "id": "airport", "type": "symbol", "source": "openmaptiles", "source-layer": "aerodrome_label", "minzoom": 10, "filter": ["all", ["has", "iata"]], "layout": { "icon-image": "airport_11", "icon-size": 1, "text-anchor": "top", "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Regular"], "text-max-width": 9, "text-offset": [0, 0.6], "text-optional": true, "text-padding": 2, "text-size": 12 }, "paint": { "text-color": "#666", "text-halo-blur": 0.5, "text-halo-color": "#ffffff", "text-halo-width": 1 } }, { "id": "label_other", "type": "symbol", "source": "openmaptiles", "source-layer": "place", "minzoom": 8, "filter": [ "match", ["get", "class"], ["city", "continent", "country", "state", "town", "village"], false, true ], "layout": { "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Italic"], "text-letter-spacing": 0.1, "text-max-width": 9, "text-size": ["interpolate", ["linear"], ["zoom"], 8, 9, 12, 10], "text-transform": "uppercase" }, "paint": { "text-color": "#333", "text-halo-blur": 1, "text-halo-color": "#fff", "text-halo-width": 1 } }, { "id": "label_village", "type": "symbol", "source": "openmaptiles", "source-layer": "place", "minzoom": 9, "filter": ["==", ["get", "class"], "village"], "layout": { "icon-allow-overlap": true, "icon-image": ["step", ["zoom"], "circle_11_black", 10, ""], "icon-optional": false, "icon-size": 0.2, "text-anchor": "bottom", "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Regular"], "text-max-width": 8, "text-size": [ "interpolate", ["exponential", 1.2], ["zoom"], 7, 10, 11, 12 ] }, "paint": { "text-color": "#000", "text-halo-blur": 1, "text-halo-color": "#fff", "text-halo-width": 1 } }, { "id": "label_town", "type": "symbol", "source": "openmaptiles", "source-layer": "place", "minzoom": 6, "filter": ["==", ["get", "class"], "town"], "layout": { "icon-allow-overlap": true, "icon-image": ["step", ["zoom"], "circle_11_black", 10, ""], "icon-optional": false, "icon-size": 0.2, "text-anchor": "bottom", "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Regular"], "text-max-width": 8, "text-size": [ "interpolate", ["exponential", 1.2], ["zoom"], 7, 12, 11, 14 ] }, "paint": { "text-color": "#000", "text-halo-blur": 1, "text-halo-color": "#fff", "text-halo-width": 1 } }, { "id": "label_state", "type": "symbol", "source": "openmaptiles", "source-layer": "place", "minzoom": 5, "maxzoom": 8, "filter": ["==", ["get", "class"], "state"], "layout": { "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Italic"], "text-letter-spacing": 0.2, "text-max-width": 9, "text-size": ["interpolate", ["linear"], ["zoom"], 5, 10, 8, 14], "text-transform": "uppercase" }, "paint": { "text-color": "#333", "text-halo-blur": 1, "text-halo-color": "#fff", "text-halo-width": 1 } }, { "id": "label_city", "type": "symbol", "source": "openmaptiles", "source-layer": "place", "minzoom": 3, "filter": [ "all", ["==", ["get", "class"], "city"], ["!=", ["get", "capital"], 2] ], "layout": { "icon-allow-overlap": true, "icon-image": ["step", ["zoom"], "circle_11_black", 9, ""], "icon-optional": false, "icon-size": 0.4, "text-anchor": "bottom", "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Regular"], "text-max-width": 8, "text-offset": [0, -0.1], "text-size": [ "interpolate", ["exponential", 1.2], ["zoom"], 4, 11, 7, 13, 11, 18 ] }, "paint": { "text-color": "#000", "text-halo-blur": 1, "text-halo-color": "#fff", "text-halo-width": 1 } }, { "id": "label_city_capital", "type": "symbol", "source": "openmaptiles", "source-layer": "place", "minzoom": 3, "filter": [ "all", ["==", ["get", "class"], "city"], ["==", ["get", "capital"], 2] ], "layout": { "icon-allow-overlap": true, "icon-image": ["step", ["zoom"], "circle_11_black", 9, ""], "icon-optional": false, "icon-size": 0.5, "text-anchor": "bottom", "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Bold"], "text-max-width": 8, "text-offset": [0, -0.2], "text-size": [ "interpolate", ["exponential", 1.2], ["zoom"], 4, 12, 7, 14, 11, 20 ] }, "paint": { "text-color": "#000", "text-halo-blur": 1, "text-halo-color": "#fff", "text-halo-width": 1 } }, { "id": "label_country_3", "type": "symbol", "source": "openmaptiles", "source-layer": "place", "minzoom": 2, "maxzoom": 9, "filter": [ "all", ["==", ["get", "class"], "country"], [">=", ["get", "rank"], 3] ], "layout": { "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Bold"], "text-max-width": 6.25, "text-size": ["interpolate", ["linear"], ["zoom"], 3, 9, 7, 17] }, "paint": { "text-color": "#000", "text-halo-blur": 1, "text-halo-color": "#fff", "text-halo-width": 1 } }, { "id": "label_country_2", "type": "symbol", "source": "openmaptiles", "source-layer": "place", "maxzoom": 9, "filter": [ "all", ["==", ["get", "class"], "country"], ["==", ["get", "rank"], 2] ], "layout": { "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Bold"], "text-max-width": 6.25, "text-size": ["interpolate", ["linear"], ["zoom"], 2, 9, 5, 17] }, "paint": { "text-color": "#000", "text-halo-blur": 1, "text-halo-color": "#fff", "text-halo-width": 1 } }, { "id": "label_country_1", "type": "symbol", "source": "openmaptiles", "source-layer": "place", "maxzoom": 9, "filter": [ "all", ["==", ["get", "class"], "country"], ["==", ["get", "rank"], 1] ], "layout": { "text-field": [ "case", ["has", "name:nonlatin"], ["concat", ["get", "name:latin"], "\n", ["get", "name:nonlatin"]], ["coalesce", ["get", "name_en"], ["get", "name"]] ], "text-font": ["Noto Sans Bold"], "text-max-width": 6.25, "text-size": ["interpolate", ["linear"], ["zoom"], 1, 9, 4, 17] }, "paint": { "text-color": "#000", "text-halo-blur": 1, "text-halo-color": "#fff", "text-halo-width": 1 } } ], "id": "94huyrm" } \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt index 9c6fee2..20e2148 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt @@ -1,958 +1,963 @@ package it.reyboz.bustorino.fragments import android.Manifest import android.animation.ValueAnimator import android.annotation.SuppressLint import android.content.Context import android.content.pm.PackageManager import android.graphics.Color import android.location.Location import android.location.LocationListener import android.location.LocationManager import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.LinearInterpolator import android.widget.ImageButton import android.widget.RelativeLayout import android.widget.TextView import android.widget.Toast import androidx.activity.result.ActivityResultCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.cardview.widget.CardView import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.preference.PreferenceManager import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.gson.Gson import com.google.gson.JsonObject import it.reyboz.bustorino.R import it.reyboz.bustorino.backend.Stop import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate import it.reyboz.bustorino.backend.mato.MQTTMatoClient import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops import it.reyboz.bustorino.fragments.SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE import it.reyboz.bustorino.map.MapUtils import it.reyboz.bustorino.map.Styles import it.reyboz.bustorino.util.Permissions import it.reyboz.bustorino.viewmodels.LivePositionsViewModel import it.reyboz.bustorino.viewmodels.StopsMapViewModel import org.maplibre.android.MapLibre import org.maplibre.android.camera.CameraPosition import org.maplibre.android.camera.CameraUpdateFactory import org.maplibre.android.geometry.LatLng import org.maplibre.android.geometry.LatLngBounds import org.maplibre.android.location.LocationComponent import org.maplibre.android.location.LocationComponentActivationOptions import org.maplibre.android.location.LocationComponentOptions import org.maplibre.android.location.engine.LocationEngineRequest import org.maplibre.android.location.modes.CameraMode import org.maplibre.android.maps.MapLibreMap import org.maplibre.android.maps.MapView import org.maplibre.android.maps.OnMapReadyCallback import org.maplibre.android.maps.Style import org.maplibre.android.plugins.annotation.Symbol import org.maplibre.android.plugins.annotation.SymbolManager import org.maplibre.android.plugins.annotation.SymbolOptions import org.maplibre.android.style.expressions.Expression import org.maplibre.android.style.layers.Property.* import org.maplibre.android.style.layers.PropertyFactory import org.maplibre.android.style.layers.SymbolLayer import org.maplibre.android.style.sources.GeoJsonSource import org.maplibre.geojson.Feature import org.maplibre.geojson.FeatureCollection import org.maplibre.geojson.Point // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER private const val ARG_PARAM1 = "param1" private const val ARG_PARAM2 = "param2" /** * A simple [Fragment] subclass. * Use the [MapLibreFragment.newInstance] factory method to * create an instance of this fragment. */ class MapLibreFragment : Fragment(), OnMapReadyCallback { //private var param1: String? = null //private var param2: String? = null // Declare a variable for MapView private lateinit var mapView: MapView private lateinit var locationComponent: LocationComponent private var lastLocation: Location? = null private val stopsViewModel: StopsMapViewModel by viewModels() private val gson = Gson() private var stopsShowing = ArrayList(0) private var isBottomSheetShowing = false private lateinit var symbolManager: SymbolManager protected var map: MapLibreMap? = null // Sources for stops and buses private lateinit var stopsSource: GeoJsonSource private lateinit var busesSource: GeoJsonSource private var isStopsLayerStarted = false private var lastStopsSizeShown = 0 private var lastBBox = LatLngBounds.from(2.0, 2.0, 1.0,1.0) private lateinit var mapStyle: Style private var mapInitCompleted =false //bottom Sheet behavior private lateinit var bottomSheetBehavior: BottomSheetBehavior private var bottomLayout: RelativeLayout? = null private lateinit var stopTitleTextView: TextView private lateinit var stopNumberTextView: TextView private lateinit var linesPassingTextView: TextView private lateinit var arrivalsCard: CardView private lateinit var directionsCard: CardView private var stopActiveSymbol: Symbol? = null // Location stuff private lateinit var locationManager: LocationManager private lateinit var showUserPositionButton: ImageButton private lateinit var centerUserButton: ImageButton private lateinit var followUserButton: ImageButton private var followingUserLocation = false private var ignoreCameraMovementForFollowing = true private var enablingPositionFromClick = false private val positionRequestLauncher = registerForActivityResult, Map>( ActivityResultContracts.RequestMultiplePermissions(), ActivityResultCallback { result -> if (result == null) { Log.w(DEBUG_TAG, "Got asked permission but request is null, doing nothing?") } else if (java.lang.Boolean.TRUE == result[Manifest.permission.ACCESS_COARSE_LOCATION] && java.lang.Boolean.TRUE == result[Manifest.permission.ACCESS_FINE_LOCATION]) { // We can use the position, restart location overlay Log.d(DEBUG_TAG, "HAVE THE PERMISSIONS") if (context == null || requireContext().getSystemService(Context.LOCATION_SERVICE) == null) return@ActivityResultCallback ///@registerForActivityResult val locationManager = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager @SuppressLint("MissingPermission") val userLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) if (userLocation != null) { if(LatLng(userLocation.latitude, userLocation.longitude).distanceTo(DEFAULT_LATLNG) >= MAX_DIST_KM*1000){ setMapLocationEnabled(true, true, false) } } else requestInitialUserLocation() } else{ Toast.makeText(requireContext(),"User location disabled", Toast.LENGTH_SHORT).show() Log.w(DEBUG_TAG, "No location permission") } }) private val showUserPositionRequestLauncher = registerForActivityResult, Map>( ActivityResultContracts.RequestMultiplePermissions(), ActivityResultCallback { result -> if (result == null) { Log.w(DEBUG_TAG, "Got asked permission but request is null, doing nothing?") } else if (java.lang.Boolean.TRUE == result[Manifest.permission.ACCESS_COARSE_LOCATION] && java.lang.Boolean.TRUE == result[Manifest.permission.ACCESS_FINE_LOCATION]) { // We can use the position, restart location overlay if (context == null || requireContext().getSystemService(Context.LOCATION_SERVICE) == null) return@ActivityResultCallback ///@registerForActivityResult setMapLocationEnabled(true, true, enablingPositionFromClick) } else Log.w(DEBUG_TAG, "No location permission") }) //BUS POSITIONS private var useMQTTViewModel = true private val livePositionsViewModel : LivePositionsViewModel by viewModels() private val positionsByVehDict = HashMap(5) private val animatorsByVeh = HashMap() private var lastUpdateTime : Long = -1 private lateinit var vehiclesLabelsSource: GeoJsonSource override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) /*arguments?.let { param1 = it.getString(ARG_PARAM1) param2 = it.getString(ARG_PARAM2) } */ MapLibre.getInstance(requireContext()) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment val rootView = inflater.inflate(R.layout.fragment_map_libre, container, false) // Init layout view // Init the MapView mapView = rootView.findViewById(R.id.libreMapView) mapView.getMapAsync(this) //{ //map -> //map.setStyle("https://demotiles.maplibre.org/style.json") } //init bottom sheet val bottomSheet = rootView.findViewById(R.id.bottom_sheet) bottomLayout = bottomSheet stopTitleTextView = bottomSheet.findViewById(R.id.stopTitleTextView) stopNumberTextView = bottomSheet.findViewById(R.id.stopNumberTextView) linesPassingTextView = bottomSheet.findViewById(R.id.linesPassingTextView) arrivalsCard = bottomSheet.findViewById(R.id.arrivalsCardButton) directionsCard = bottomSheet.findViewById(R.id.directionsCardButton) showUserPositionButton = rootView.findViewById(R.id.locationEnableIcon) showUserPositionButton.setOnClickListener(this::switchUserLocationStatus) followUserButton = rootView.findViewById(R.id.followUserImageButton) centerUserButton = rootView.findViewById(R.id.centerMapImageButton) bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN arrivalsCard.setOnClickListener { if(context!=null){ Toast.makeText(context,"ARRIVALS", Toast.LENGTH_SHORT).show() } } centerUserButton.setOnClickListener { if(context!=null && locationComponent.isLocationComponentEnabled) { val location = locationComponent.lastKnownLocation location?.let { mapView.getMapAsync { map -> map.animateCamera(CameraUpdateFactory.newCameraPosition( CameraPosition.Builder().target(LatLng(location.latitude, location.longitude)).build()), 500) } } } } followUserButton.setOnClickListener { if(context!=null && locationComponent.isLocationComponentEnabled){ if(followingUserLocation) locationComponent.cameraMode = CameraMode.NONE else locationComponent.cameraMode = CameraMode.TRACKING setFollowingUser(!followingUserLocation) } } locationManager = requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager if (haveLocationPermissions()) { requestInitialUserLocation() } else{ if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) { //TODO: show dialog for permission rationale Toast.makeText(activity, R.string.enable_position_message_map, Toast.LENGTH_SHORT) .show() } positionRequestLauncher.launch(Permissions.LOCATION_PERMISSIONS) } // Setup close button rootView.findViewById(R.id.btnClose).setOnClickListener { hideStopBottomSheet() } return rootView } /** * This method sets up the map and the layers */ override fun onMapReady(mapReady: MapLibreMap) { this.map = mapReady //TODO: Check if we have the user last position and start the map there mapReady.cameraPosition = CameraPosition.Builder().target(LatLng(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON)).zoom( 15.0).build() - val mjson = Styles.getJsonStyleFromAsset(requireContext(), "map_style_good_noshops.json")//ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json") + val mjson = Styles.getJsonStyleFromAsset(requireContext(), "map_style_good_noshops.json") + //ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json") + + activity?.run { + val builder = Style.Builder().fromJson(mjson!!) + + mapReady.setStyle(builder) { style -> - mapReady.setStyle(Style.Builder().fromJson(mjson!!)) { style -> mapStyle = style //setupLayers(style) symbolManager = SymbolManager(mapView,mapReady,style) symbolManager.iconAllowOverlap = true symbolManager.textAllowOverlap = true symbolManager.addClickListener{ _ -> if (stopActiveSymbol!=null){ hideStopBottomSheet() return@addClickListener true } else return@addClickListener false } // Start observing data observeViewModels() initMapLocation(style, mapReady, requireContext()) initStopsLayer(style, FeatureCollection.fromFeatures(ArrayList())) setupBusLayer(style) } mapReady.addOnCameraIdleListener { map?.let { val newBbox = it.projection.visibleRegion.latLngBounds if ((newBbox.center==lastBBox.center) && (newBbox.latitudeSpan==lastBBox.latitudeSpan) && (newBbox.longitudeSpan==lastBBox.latitudeSpan)){ //do nothing } else { stopsViewModel.loadStopsInLatLngBounds(newBbox) lastBBox = newBbox //if we are moving away from the position, disable it /* */ } } } mapReady.addOnCameraMoveStartedListener { if (ignoreCameraMovementForFollowing){ ignoreCameraMovementForFollowing = false } else if (followingUserLocation){ setFollowingUser(false) } } mapReady.addOnMapClickListener { point -> val screenPoint = mapReady.projection.toScreenLocation(point) val features = mapReady.queryRenderedFeatures(screenPoint, STOPS_LAYER_ID) if (features.isNotEmpty()) { val feature = features[0] val id = feature.getStringProperty("id") val name = feature.getStringProperty("name") //Toast.makeText(requireContext(), "Clicked on $name ($id)", Toast.LENGTH_SHORT).show() val stop = stopsViewModel.getStopByID(id) stop?.let { if (isBottomSheetShowing){ hideStopBottomSheet() } showStopInBottomSheet(it) bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED isBottomSheetShowing = true //move camera if(it.latitude!=null && it.longitude!=null) //mapReady.cameraPosition = CameraPosition.Builder().target(LatLng(it.latitude!!, it.longitude!!)).build() mapReady.animateCamera(CameraUpdateFactory.newLatLng(LatLng(it.latitude!!,it.longitude!!)),750) } return@addOnMapClickListener true } false } mapInitCompleted = true // we start requesting the bus positions now startRequestingPositions() } } private fun initStopsLayer(style: Style, features:FeatureCollection){ stopsSource = GeoJsonSource(STOPS_SOURCE_ID,features) style.addSource(stopsSource) // add icon style.addImage(STOP_IMAGE_ID, ResourcesCompat.getDrawable(resources,R.drawable.bus_stop_new, activity?.theme)!!) style.addImage(STOP_ACTIVE_IMG, ResourcesCompat.getDrawable(resources, R.drawable.bus_stop_new_highlight, activity?.theme)!!) // Stops layer val stopsLayer = SymbolLayer(STOPS_LAYER_ID, STOPS_SOURCE_ID) stopsLayer.withProperties( PropertyFactory.iconImage(STOP_IMAGE_ID), PropertyFactory.iconAllowOverlap(true), PropertyFactory.iconIgnorePlacement(true) ) style.addLayerBelow(stopsLayer, "label_country_1") isStopsLayerStarted = true } /** * Setup the Map Layers */ private fun setupBusLayer(style: Style) { // Buses source busesSource = GeoJsonSource(BUSES_SOURCE_ID) style.addSource(busesSource) style.addImage("bus_symbol",ResourcesCompat.getDrawable(resources, R.drawable.map_bus_position_icon, activity?.theme)!!) // Buses layer val busesLayer = SymbolLayer(BUSES_LAYER_ID, BUSES_SOURCE_ID).apply { withProperties( PropertyFactory.iconImage("bus_symbol"), //PropertyFactory.iconSize(1.0f), PropertyFactory.iconAllowOverlap(true), PropertyFactory.iconIgnorePlacement(true), PropertyFactory.iconRotate(Expression.get("bearing")), /*PropertyFactory.textField("label"), PropertyFactory.textSize(12f), PropertyFactory.textColor(Color.BLACK), //PropertyFactory.textHaloColor(Color.BLACK), //PropertyFactory.textHaloWidth(1f), PropertyFactory.textAnchor(TEXT_ANCHOR_CENTER), PropertyFactory.textAllowOverlap(true), PropertyFactory.textRotationAlignment(TEXT_ROTATION_ALIGNMENT_VIEWPORT) */ ) } style.addLayerAbove(busesLayer, STOPS_LAYER_ID) //Line names layer vehiclesLabelsSource = GeoJsonSource(LABELS_SOURCE) style.addSource(vehiclesLabelsSource) val textLayer = SymbolLayer(LABELS_LAYER_ID, LABELS_SOURCE).apply { withProperties( PropertyFactory.textField("label"), - PropertyFactory.textSize(50f), + PropertyFactory.textSize(30f), PropertyFactory.textColor(Color.BLACK), //PropertyFactory.textHaloColor(Color.BLACK), //PropertyFactory.textHaloWidth(1f), PropertyFactory.textAnchor(TEXT_ANCHOR_CENTER), PropertyFactory.textAllowOverlap(true), PropertyFactory.textRotationAlignment(TEXT_ROTATION_ALIGNMENT_VIEWPORT) ) } style.addLayerAbove(textLayer, BUSES_LAYER_ID) } /** * Update the bottom sheet with the stop information */ private fun showStopInBottomSheet(stop: Stop?){ if (stop==null) return bottomLayout?.let { //lay.findViewById(R.id.stopTitleTextView).text ="${stop.ID} - ${stop.stopDefaultName}" stopTitleTextView.text = stop.stopDefaultName stopNumberTextView.text = stop.ID val string_show = if (stop.numRoutesStopping==0) "" else if (stop.numRoutesStopping <= 1) requireContext().getString(R.string.line_fill, stop.routesThatStopHereToString()) else requireContext().getString(R.string.lines_fill, stop.routesThatStopHereToString()) linesPassingTextView.text = string_show } //add stop marker if (stop.latitude!=null && stop.longitude!=null) { /*val marker = map?.addMarker( MarkerOptions() .position(LatLng(stop.latitude!!, stop.longitude!!)) // example coords .icon( //IconFactory.getInstance(requireContext()).fromBitmap( getIconFromVectorDrawable(requireContext(), R.drawable.bus_stop_new_highlight) //R.drawable.bus_stop_new_highlight) //IconFactory.getInstance(requireContext()) //.fromResource(R.drawable.bus_stop_new_highlight) ) .title(stop.stopDefaultName) ) */ stopActiveSymbol = symbolManager.create( SymbolOptions() .withLatLng(LatLng(stop.latitude!!, stop.longitude!!)) .withIconImage(STOP_ACTIVE_IMG) .withIconAnchor(ICON_ANCHOR_CENTER) ) } } override fun onStart() { super.onStart() mapView.onStart() } override fun onResume() { super.onResume() mapView.onResume() val keySourcePositions = getString(R.string.pref_positions_source) useMQTTViewModel = PreferenceManager.getDefaultSharedPreferences(requireContext()) .getString(keySourcePositions, LIVE_POSITIONS_PREF_MQTT_VALUE) .contentEquals(LIVE_POSITIONS_PREF_MQTT_VALUE) if (useMQTTViewModel) livePositionsViewModel.requestMatoPosUpdates(MQTTMatoClient.LINES_ALL) else livePositionsViewModel.requestGTFSUpdates() //mapViewModel.testCascade(); livePositionsViewModel.isLastWorkResultGood.observe(this) { d: Boolean -> Log.d( DEBUG_TAG, "Last trip download result is $d" ) } livePositionsViewModel.tripsGtfsIDsToQuery.observe(this) { dat: List -> Log.i(DEBUG_TAG, "Have these trips IDs missing from the DB, to be queried: $dat") livePositionsViewModel.downloadTripsFromMato(dat) } } override fun onPause() { super.onPause() mapView.onPause() } override fun onStop() { super.onStop() mapView.onStop() } override fun onLowMemory() { super.onLowMemory() mapView.onLowMemory() } override fun onDestroy() { super.onDestroy() mapView.onDestroy() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) mapView.onSaveInstanceState(outState) } private fun observeViewModels() { // Observe stops stopsViewModel.stopsToShow.observe(viewLifecycleOwner) { stops -> stopsShowing = ArrayList(stops) displayStops(stopsShowing) } } /** * Add the stops to the layers */ private fun displayStops(stops: List?) { if (stops.isNullOrEmpty()) return if (stops.size==lastStopsSizeShown){ Log.d(DEBUG_TAG, "Not updating, we have the same stop (can only increase!)") return } val features = ArrayList()//stops.mapNotNull { stop -> //stop.latitude?.let { lat -> // stop.longitude?.let { lon -> for (s in stops){ if (s.latitude!=null && s.longitude!=null) features.add( Feature.fromGeometry( Point.fromLngLat(s.longitude!!, s.latitude!!), JsonObject().apply { addProperty("id", s.ID) addProperty("name", s.stopDefaultName) addProperty("routes", s.routesThatStopHereToString()) // Add routes array to JSON object } ) ) } Log.d(DEBUG_TAG,"Have put ${features.size} stops to display") // if the layer is already started, substitute the stops inside, otherwise start it if (isStopsLayerStarted) { stopsSource.setGeoJson(FeatureCollection.fromFeatures(features)) lastStopsSizeShown = features.size } else map?.let { initStopsLayer(mapStyle, FeatureCollection.fromFeatures(features)) Log.d(DEBUG_TAG,"Started stops layer on map") lastStopsSizeShown = features.size } } // Hide the bottom sheet and remove extra symbol private fun hideStopBottomSheet(){ if (stopActiveSymbol!=null){ symbolManager.delete(stopActiveSymbol) stopActiveSymbol = null } bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN isBottomSheetShowing = false } // --------------- BUS LOCATIONS STUFF -------------------------- /** * Start requesting position updates */ private fun startRequestingPositions() { livePositionsViewModel.updatesWithTripAndPatterns.observe(viewLifecycleOwner) { data: HashMap> -> Log.d( DEBUG_TAG, "Have " + data.size + " trip updates, has Map start finished: " + mapInitCompleted ) if (mapInitCompleted) updateBusPositionsInMap(data) if (!isDetached && !useMQTTViewModel) livePositionsViewModel.requestDelayedGTFSUpdates( 3000 ) } } private fun isInsideVisibleRegion(latitude: Double, longitude: Double, nullValue: Boolean): Boolean{ var isInside = nullValue val visibleRegion = map?.projection?.visibleRegion visibleRegion?.let { val bounds = it.latLngBounds isInside = bounds.contains(LatLng(latitude, longitude)) } return isInside } private fun updateBusPositionsInMap(incomingData: HashMap>){ val vehsNew = HashSet(incomingData.values.map { up -> up.first.vehicle }) val vehsOld = HashSet(positionsByVehDict.keys) for (upsWithTrp in incomingData.values){ val pos = upsWithTrp.first val vehID = pos.vehicle var animate = false if (vehsOld.contains(vehID)){ //update position only if the starting or the stopping position of the animation are in the view val isPositionInBounds = isInsideVisibleRegion(pos.latitude, pos.longitude, true ) || positionsByVehDict[vehID]?.let { isInsideVisibleRegion(it.latitude, it.longitude, true) } ?: false if (isPositionInBounds) animate = true } if (animate){ moveVehicleToNewPosition(pos) } else{ // update it simply positionsByVehDict[vehID] = pos } } //remove old positions vehsOld.removeAll(vehsNew) //now vehsOld contains the vehicles id for those that have NOT been updated val currentTimeStamp = System.currentTimeMillis() /1000 for(vehID in vehsOld){ //remove after 2 minutes of inactivity if (positionsByVehDict[vehID]!!.timestamp - currentTimeStamp > 2*60){ positionsByVehDict.remove(vehID) } } //update UI updatePositionsIcons() } /** * This is the tricky part, animating the transitions * Basically, we need to set the new positions with the data and redraw them all */ private fun moveVehicleToNewPosition(positionUpdate: LivePositionUpdate){ if (positionUpdate.vehicle !in positionsByVehDict.keys) return val vehID = positionUpdate.vehicle val currentUpdate = positionsByVehDict[positionUpdate.vehicle] currentUpdate?.let { //cancel current animation on vehicle animatorsByVeh[vehID]?.cancel() val currentPos = LatLng(it.latitude, it.longitude) val newPos = LatLng(positionUpdate.latitude, positionUpdate.longitude) val valueAnimator = ValueAnimator.ofObject(MapUtils.LatLngEvaluator(), currentPos, newPos) valueAnimator.addUpdateListener(object : ValueAnimator.AnimatorUpdateListener { private var latLng: LatLng? = null override fun onAnimationUpdate(animation: ValueAnimator) { latLng = animation.animatedValue as LatLng //update position on animation val update = positionsByVehDict[positionUpdate.vehicle]!! latLng?.let { ll-> update.latitude = ll.latitude update.longitude = ll.longitude updatePositionsIcons() } } }) /*valueAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator) { super.onAnimationStart(animation) val update = positionsByVehDict[positionUpdate.vehicle]!! update } })*/ //set the new position as the current one but with the old lat and lng positionUpdate.latitude = currentUpdate.latitude positionUpdate.longitude = currentUpdate.longitude positionsByVehDict[vehID] = positionUpdate valueAnimator.duration = 500 valueAnimator.interpolator = LinearInterpolator() valueAnimator.start() animatorsByVeh[vehID] = valueAnimator } ?: { Log.e(DEBUG_TAG, "Have to run animation for veh ${positionUpdate.vehicle} but not in the dict, adding") positionsByVehDict[positionUpdate.vehicle] = positionUpdate } } /** * Update the bus positions displayed on the map, from the existing data */ private fun updatePositionsIcons(){ //avoid frequent updates val currentTime = System.currentTimeMillis() if(currentTime - lastUpdateTime < 60){ //DO NOT UPDATE THE MAP return } val features = ArrayList()//stops.mapNotNull { stop -> //stop.latitude?.let { lat -> // stop.longitude?.let { lon -> val busLinesFeatures = ArrayList() for (pos in positionsByVehDict.values){ //if (s.latitude!=null && s.longitude!=null) val point = Point.fromLngLat(pos.longitude, pos.latitude) features.add( Feature.fromGeometry( point, JsonObject().apply { addProperty("veh", pos.vehicle) addProperty("trip", pos.tripID) addProperty("bearing", pos.bearing?.let { it }?:0.0f) addProperty("label", pos.routeID) // Add routes array to JSON object } ) ) busLinesFeatures.add( Feature.fromGeometry(point, JsonObject().apply { - addProperty("label", pos.routeID) + addProperty("textField", pos.routeID) }) ) } busesSource.setGeoJson(FeatureCollection.fromFeatures(features)) vehiclesLabelsSource.setGeoJson(FeatureCollection.fromFeatures(busLinesFeatures)) lastUpdateTime = System.currentTimeMillis() } // ------ LOCATION STUFF ----- /*private fun checkAndRequestInitialUserLocation() { if (ContextCompat.checkSelfPermission( requireContext(), Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) { requestInitialUserLocation() } else { requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE) } } */ @SuppressLint("MissingPermission") private fun requestInitialUserLocation() { val provider :String? = LocationManager.GPS_PROVIDER//getBestLocationProvider() provider?.let { setLocationIconEnabled(true) Toast.makeText(requireContext(), R.string.position_searching_message, Toast.LENGTH_SHORT).show() locationManager.requestSingleUpdate(it, object : LocationListener { override fun onLocationChanged(location: Location) { val userLatLng = LatLng(location.latitude, location.longitude) val distanceToTarget = userLatLng.distanceTo(DEFAULT_LATLNG) if (distanceToTarget <= MAX_DIST_KM*1000.0) { map?.let{ //initMapLocation(mapStyle,map!!,requireContext()) setMapLocationEnabled(true, true, false) } } else { Toast.makeText(context, "You are too far, not showing the position", Toast.LENGTH_SHORT).show() } } override fun onProviderDisabled(provider: String) {} override fun onProviderEnabled(provider: String) {} @Deprecated("Deprecated in Java") override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {} }, null) } ?: run { Toast.makeText(context, "No suitable location provider found.", Toast.LENGTH_SHORT).show() } } /** * Initialize the map location, but do not enable the component */ @SuppressLint("MissingPermission") private fun initMapLocation(style: Style, map: MapLibreMap, context: Context){ locationComponent = map.locationComponent val locationComponentOptions = LocationComponentOptions.builder(context) .pulseEnabled(true) .build() val locationComponentActivationOptions = buildLocationComponentActivationOptions(style, locationComponentOptions, context) locationComponent.activateLocationComponent(locationComponentActivationOptions) locationComponent.isLocationComponentEnabled = false lastLocation?.let { if (it.accuracy < 200) locationComponent.forceLocationUpdate(it) } } private fun buildLocationComponentActivationOptions( style: Style, locationComponentOptions: LocationComponentOptions, context: Context ): LocationComponentActivationOptions { return LocationComponentActivationOptions .builder(context, style) .locationComponentOptions(locationComponentOptions) .useDefaultLocationEngine(true) .locationEngineRequest( LocationEngineRequest.Builder(750) .setFastestInterval(750) .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) .build() ) .build() } private fun haveLocationPermissions(): Boolean{ return !(ActivityCompat.checkSelfPermission( requireContext(),Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(requireContext(),Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) } /** * Handles logic of enabling the user location on the map */ @SuppressLint("MissingPermission") private fun setMapLocationEnabled(enabled: Boolean, assumePermissions: Boolean, fromClick: Boolean) { if (enabled) { val permissionOk = assumePermissions || haveLocationPermissions() if (permissionOk) { Log.d(DEBUG_TAG, "Permission OK, starting location component, assumed: $assumePermissions") locationComponent.isLocationComponentEnabled = true locationComponent.cameraMode = CameraMode.TRACKING //CameraMode.TRACKING setFollowingUser(true) showUserPositionButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.location_circlew_red)) if (fromClick) Toast.makeText(context, R.string.location_enabled, Toast.LENGTH_SHORT).show() } else { if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) { //TODO: show dialog for permission rationale Toast.makeText(activity, R.string.enable_position_message_map, Toast.LENGTH_SHORT).show() } Log.d(DEBUG_TAG, "Requesting permission to show user location") enablingPositionFromClick = fromClick showUserPositionRequestLauncher.launch(Permissions.LOCATION_PERMISSIONS) } } else{ locationComponent.isLocationComponentEnabled = false setFollowingUser(false) showUserPositionButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.location_circlew_grey)) if (fromClick) Toast.makeText(requireContext(), R.string.location_disabled, Toast.LENGTH_SHORT).show() } } private fun setLocationIconEnabled(enabled: Boolean){ if (enabled) showUserPositionButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.location_circlew_red)) else showUserPositionButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.location_circlew_grey)) } /** * Helper method for GUI */ private fun updateFollowingIcon(enabled: Boolean){ if(enabled) followUserButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_follow_me_on)) else followUserButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_follow_me)) } private fun setFollowingUser(following: Boolean){ updateFollowingIcon(following) followingUserLocation = following if(following) ignoreCameraMovementForFollowing = true } private fun switchUserLocationStatus(view: View?){ if(locationComponent.isLocationComponentEnabled) setMapLocationEnabled(false, false, true) else{ Log.d(DEBUG_TAG, "Request enable location") setMapLocationEnabled(true, false, true) } } companion object { private const val STOPS_SOURCE_ID = "stops-source" private const val STOPS_LAYER_ID = "stops-layer" private const val STOPS_LAYER_SEL_ID ="stops-layer-selected" private const val BUSES_SOURCE_ID = "buses-source" private const val BUSES_LAYER_ID = "buses-layer" private const val LABELS_LAYER_ID = "bus-labels-layer" private const val LABELS_SOURCE = "labels-source" private const val STOP_IMAGE_ID ="bus-stop-icon" private const val DEFAULT_CENTER_LAT = 45.0708 private const val DEFAULT_CENTER_LON = 7.6858 private val DEFAULT_LATLNG = LatLng(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON) private const val POSITION_FOUND_ZOOM = 16.5 private const val NO_POSITION_ZOOM = 17.1 private const val MAX_DIST_KM = 90.0 private const val ACCESS_TOKEN="KxO8lF4U3kiO63m0c7lzqDCDrMUVg1OA2JVzRXxxmYSyjugr1xpe4W4Db5rFNvbQ" private const val MAPLIBRE_URL = "https://api.jawg.io/styles/" private const val DEBUG_TAG = "BusTO-MapLibreFrag" private const val STOP_ACTIVE_IMG = "Stop-active" private const val LOCATION_PERMISSION_REQUEST_CODE = 981202 /** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @param param1 Parameter 1. * @param param2 Parameter 2. * @return A new instance of fragment MapLibreFragment. */ // TODO: Rename and change types and number of parameters @JvmStatic fun newInstance(param1: String, param2: String) = MapLibreFragment().apply { arguments = Bundle().apply { putString(ARG_PARAM1, param1) putString(ARG_PARAM2, param2) } } private fun makeStyleUrl(style: String = "jawg-streets") = "${MAPLIBRE_URL+ style}.json?access-token=${ACCESS_TOKEN}" private fun makeStyleMapBoxUrl(dark: Boolean) = if(dark) "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json" else //"https://basemaps.cartocdn.com/gl/positron-gl-style/style.json" "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json" const val OPENFREEMAP_LIBERY = "https://tiles.openfreemap.org/styles/liberty" const val OPENFREEMAP_BRIGHT = "https://tiles.openfreemap.org/styles/bright" } } \ No newline at end of file