diff --git a/app/javascript/maplibre/basemaps.js b/app/javascript/maplibre/basemaps.js
index 98a09b71..51329b73 100644
--- a/app/javascript/maplibre/basemaps.js
+++ b/app/javascript/maplibre/basemaps.js
@@ -1,6 +1,11 @@
-// Maptiler SDK shortcuts: https://docs.maptiler.com/sdk-js/api/map-styles/#mapstylelist
-
+// Default glyphs for Raster maps
const openmaptilesGlyphs = 'https://fonts.openmaptiles.org/{fontstack}/{range}.pbf'
+// fonts must be available via glyphs:
+// openmaptiles: https://github.com/openmaptiles/fonts/tree/gh-pages
+// maptiler: https://docs.maptiler.com/gl-style-specification/glyphs/
+// versatiles: https://github.com/versatiles-org/versatiles-fonts/tree/main/fonts
+// Emojis are not in the character range: https://github.com/maplibre/maplibre-gl-js/issues/2307
+export const defaultFont = 'Klokantech Noto Sans Bold' // avail from openmaptiles + maplibre
const defaultRasterLayer = [
{
id: 'simple-tiles',
@@ -15,137 +20,151 @@ const host = new URL(window.location.href).origin
export const basemaps = {
// static test tile
test: {
- version: 8,
- sources: {
- 'raster-tiles': {
- type: 'raster',
- tiles: ['/layers/test_tile.png'],
- tileSize: 1024
- }
- },
- layers: defaultRasterLayer,
- glyphs: openmaptilesGlyphs
+ style: {
+ version: 8,
+ sources: {
+ 'raster-tiles': {
+ type: 'raster',
+ tiles: ['/layers/test_tile.png'],
+ tileSize: 1024
+ }
+ },
+ layers: defaultRasterLayer,
+ glyphs: openmaptilesGlyphs
+ }
},
// Stadia maps
stamenWatercolorTiles: {
- version: 8,
- sources: {
- 'raster-tiles': {
- type: 'raster',
- tiles: [
+ style: {
+ version: 8,
+ sources: {
+ 'raster-tiles': {
+ type: 'raster',
+ tiles: [
// NOTE: Layers from Stadia Maps do not require an API key for localhost development or most production
// web deployments. See https://docs.stadiamaps.com/authentication/ for details.
- 'https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg'
- ],
- tileSize: 256,
- attribution: 'Map tiles by Stamen Design; Hosting by Stadia Maps. Data © OpenStreetMap contributors'
- }
- },
- layers: defaultRasterLayer,
- glyphs: openmaptilesGlyphs
+ 'https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg'
+ ],
+ tileSize: 256,
+ attribution: 'Map tiles by Stamen Design; Hosting by Stadia Maps. Data © OpenStreetMap contributors'
+ }
+ },
+ layers: defaultRasterLayer,
+ glyphs: openmaptilesGlyphs
+ }
},
stamenTonerTiles: {
- version: 8,
- sources: {
- 'raster-tiles': {
- type: 'raster',
- tiles: [
+ style: {
+ version: 8,
+ sources: {
+ 'raster-tiles': {
+ type: 'raster',
+ tiles: [
// NOTE: Layers from Stadia Maps do not require an API key for localhost development or most production
// web deployments. See https://docs.stadiamaps.com/authentication/ for details.
- 'https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}.jpg'
- ],
- tileSize: 256,
- attribution: 'Map tiles by Stamen Design; Hosting by Stadia Maps. Data © OpenStreetMap contributors'
- }
- },
- layers: defaultRasterLayer,
- glyphs: openmaptilesGlyphs
+ 'https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}.jpg'
+ ],
+ tileSize: 256,
+ attribution: 'Map tiles by Stamen Design; Hosting by Stadia Maps. Data © OpenStreetMap contributors'
+ }
+ },
+ layers: defaultRasterLayer,
+ glyphs: openmaptilesGlyphs
+ }
},
// free maps
openTopoTiles: {
- version: 8,
- sources: {
- 'raster-tiles': {
- type: 'raster',
- tiles: [
- // https://opentopomap.org/about#verwendung
- 'https://a.tile.opentopomap.org/{z}/{x}/{y}.png'
- ],
- tileSize: 256,
- attribution: 'Kartendaten: © ' +
- 'OpenStreetMap-Mitwirkende ' +
- 'SRTM | Kartendarstellung: © ' +
- 'OpenTopoMap ' +
- '(CC-BY-SA)'
- }
- },
- layers: defaultRasterLayer,
- glyphs: openmaptilesGlyphs
+ style: {
+ version: 8,
+ sources: {
+ 'raster-tiles': {
+ type: 'raster',
+ tiles: [
+ // https://opentopomap.org/about#verwendung
+ 'https://a.tile.opentopomap.org/{z}/{x}/{y}.png'
+ ],
+ tileSize: 256,
+ attribution: 'Kartendaten: © ' +
+ 'OpenStreetMap-Mitwirkende ' +
+ 'SRTM | Kartendarstellung: © ' +
+ 'OpenTopoMap ' +
+ '(CC-BY-SA)'
+ }
+ },
+ layers: defaultRasterLayer,
+ glyphs: openmaptilesGlyphs
+ }
},
satelliteTiles: {
- version: 8,
- projection: { type: 'globe' },
- sources: {
- 'raster-tiles': {
- type: 'raster',
- tiles: [
- 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
- ],
- tileSize: 256,
- attribution: 'Powered by Esri, ' +
+ style: {
+ version: 8,
+ projection: { type: 'globe' },
+ sources: {
+ 'raster-tiles': {
+ type: 'raster',
+ tiles: [
+ 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
+ ],
+ tileSize: 256,
+ attribution: 'Powered by Esri, ' +
'Sources: Esri, DigitalGlobe, GeoEye, i-cubed, USDA FSA, USGS, AEX, Getmapping, Aerogrid, IGN, IGP, swisstopo, and the GIS User Community'
- }
- },
- sky: {
- 'atmosphere-blend': [
- 'interpolate',
- ['linear'],
- ['zoom'],
- 0, 1,
- 5, 1,
- 7, 0
- ]
- },
- layers: defaultRasterLayer,
- glyphs: openmaptilesGlyphs
+ }
+ },
+ sky: {
+ 'atmosphere-blend': [
+ 'interpolate',
+ ['linear'],
+ ['zoom'],
+ 0, 1,
+ 5, 1,
+ 7, 0
+ ]
+ },
+ layers: defaultRasterLayer,
+ glyphs: openmaptilesGlyphs
+ }
},
osmRasterTiles: {
- version: 8,
- sources: {
- 'raster-tiles': {
- type: 'raster',
- tiles: [
- 'https://c.tile.openstreetmap.org/{z}/{x}/{y}.png'
- ],
- tileSize: 256,
- attribution: '© OpenStreetMap Contributors'
- }
- },
- layers: defaultRasterLayer,
- glyphs: openmaptilesGlyphs
+ style: {
+ version: 8,
+ sources: {
+ 'raster-tiles': {
+ type: 'raster',
+ tiles: [
+ 'https://c.tile.openstreetmap.org/{z}/{x}/{y}.png'
+ ],
+ tileSize: 256,
+ attribution: '© OpenStreetMap Contributors'
+ }
+ },
+ layers: defaultRasterLayer,
+ glyphs: openmaptilesGlyphs
+ }
},
// openfreemap.org
- openfreemapPositron: 'https://tiles.openfreemap.org/styles/positron',
- openfreemapBright: 'https://tiles.openfreemap.org/styles/bright',
- openfreemapLiberty: 'https://tiles.openfreemap.org/styles/liberty',
+ openfreemapPositron: { style: 'https://tiles.openfreemap.org/styles/positron', font: 'Noto Sans Regular' },
+ openfreemapBright: { style: 'https://tiles.openfreemap.org/styles/bright', font: 'Noto Sans Regular' },
+ openfreemapLiberty: { style: 'https://tiles.openfreemap.org/styles/liberty', font: 'Noto Sans Regular' },
// https://github.com/versatiles-org/versatiles-style
- versatilesColorful: 'https://tiles.versatiles.org/assets/styles/colorful.json',
- versatilesGraybeard: 'https://tiles.versatiles.org/assets/styles/graybeard.json',
- versatilesNeutrino: 'https://tiles.versatiles.org/assets/styles/neutrino.json',
+ // fonts: https://github.com/versatiles-org/versatiles-fonts
+ versatilesColorful: { style: 'https://tiles.versatiles.org/assets/styles/colorful.json', font: 'noto_sans_regular' },
+ versatilesGraybeard: { style: 'https://tiles.versatiles.org/assets/styles/graybeard.json', font: 'noto_sans_regular' },
+ versatilesNeutrino: { style: 'https://tiles.versatiles.org/assets/styles/neutrino.json', font: 'noto_sans_regular' },
+ // Maptiler maps: https://docs.maptiler.com/sdk-js/api/map-styles/#mapstylelist
// 3D Houses
- maptilerBasic: 'https://api.maptiler.com/maps/basic-v2/style.json?key=' + window.gon.map_keys.maptiler,
- maptilerOpenStreetmap: 'https://api.maptiler.com/maps/openstreetmap/style.json?key=' + window.gon.map_keys.maptiler,
- maptilerBuildings: 'https://api.maptiler.com/maps/streets-v2/style.json?key=' + window.gon.map_keys.maptiler,
- maptilerDataviz: 'https://api.maptiler.com/maps/dataviz/style.json?key=' + window.gon.map_keys.maptiler,
- maptilerStreets: host + '/layers/streets.json?key=' + window.gon.map_keys.maptiler,
- maptilerNoStreets: host + '/layers/nostreets.json?key=' + window.gon.map_keys.maptiler,
- maptilerSatellite: 'https://api.maptiler.com/maps/satellite/style.json?key=' + window.gon.map_keys.maptiler,
- maptilerWinter: 'https://api.maptiler.com/maps/winter-v2/style.json?key=' + window.gon.map_keys.maptiler,
- maptilerBike: 'https://api.maptiler.com/maps/64d03850-97e0-4aaa-bd1d-8287a9792de1/style.json?key=' + window.gon.map_keys.maptiler,
- maptilerHybrid: 'https://api.maptiler.com/maps/hybrid/style.json?key=' + window.gon.map_keys.maptiler
+ maptilerBasic: { style: 'https://api.maptiler.com/maps/basic-v2/style.json?key=' + window.gon.map_keys.maptiler },
+ maptilerOpenStreetmap: { style: 'https://api.maptiler.com/maps/openstreetmap/style.json?key=' + window.gon.map_keys.maptiler },
+ maptilerBuildings: { style: 'https://api.maptiler.com/maps/streets-v2/style.json?key=' + window.gon.map_keys.maptiler },
+ maptilerDataviz: { style: 'https://api.maptiler.com/maps/dataviz/style.json?key=' + window.gon.map_keys.maptiler },
+ maptilerStreets: { style: host + '/layers/streets.json?key=' + window.gon.map_keys.maptiler },
+ maptilerNoStreets: { style: host + '/layers/nostreets.json?key=' + window.gon.map_keys.maptiler },
+ maptilerSatellite: { style: 'https://api.maptiler.com/maps/satellite/style.json?key=' + window.gon.map_keys.maptiler },
+ maptilerWinter: { style: 'https://api.maptiler.com/maps/winter-v2/style.json?key=' + window.gon.map_keys.maptiler },
+ maptilerBike: { style: 'https://api.maptiler.com/maps/64d03850-97e0-4aaa-bd1d-8287a9792de1/style.json?key=' + window.gon.map_keys.maptiler },
+ maptilerHybrid: { style: 'https://api.maptiler.com/maps/hybrid/style.json?key=' + window.gon.map_keys.maptiler }
}
diff --git a/app/javascript/maplibre/edit.js b/app/javascript/maplibre/edit.js
index dc4b0802..85450973 100644
--- a/app/javascript/maplibre/edit.js
+++ b/app/javascript/maplibre/edit.js
@@ -58,7 +58,7 @@ export function initializeEditMode () {
combine_features: false
// uncombine_features
},
- styles: editStyles,
+ styles: editStyles(),
clickBuffer: 5,
touchBuffer: 25, // default 25
// user properties are available, prefixed with 'user_'
diff --git a/app/javascript/maplibre/edit_styles.js b/app/javascript/maplibre/edit_styles.js
index 51807d20..117571c5 100644
--- a/app/javascript/maplibre/edit_styles.js
+++ b/app/javascript/maplibre/edit_styles.js
@@ -5,10 +5,10 @@ export function initializeEditStyles () {
// MapboxDraw cannot render symbol+text styles.
// Adding those as extra layers to the map.
// render the extrusion layer from "source: 'geojson-source' without having it available for edit in draw
- map.addLayer(styles['polygon-layer-extrusion'])
- map.addLayer(styles['symbols-border-layer'])
- map.addLayer(styles['symbols-layer'])
- map.addLayer(styles['text-layer'])
+ map.addLayer(styles()['polygon-layer-extrusion'])
+ map.addLayer(styles()['symbols-border-layer'])
+ map.addLayer(styles()['symbols-layer'])
+ map.addLayer(styles()['text-layer'])
sortLayers()
console.log('Edit styles added')
@@ -27,135 +27,137 @@ export function initializeEditStyles () {
const highlightColor = '#fbb03b'
-export const editStyles = [
+export function editStyles () {
+ return [
- removeSource(styles['polygon-layer']), // gl-draw-polygon-fill-inactive
- removeSource(styles['line-layer-outline']),
- removeSource(styles['line-layer']), // 'gl-draw-line-inactive', 'gl-draw-polygon-stroke-inactive',
+ removeSource(styles()['polygon-layer']), // gl-draw-polygon-fill-inactive
+ removeSource(styles()['line-layer-outline']),
+ removeSource(styles()['line-layer']), // 'gl-draw-line-inactive', 'gl-draw-polygon-stroke-inactive',
- // active polygon outline
- {
- id: 'gl-draw-polygon-stroke-active',
- type: 'line',
- filter: ['all',
- ['==', 'active', 'true'],
- ['==', '$type', 'Polygon']],
- layout: {
- 'line-cap': 'round',
- 'line-join': 'round'
+ // active polygon outline
+ {
+ id: 'gl-draw-polygon-stroke-active',
+ type: 'line',
+ filter: ['all',
+ ['==', 'active', 'true'],
+ ['==', '$type', 'Polygon']],
+ layout: {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ paint: {
+ 'line-color': highlightColor,
+ 'line-dasharray': [0.2, 2],
+ 'line-width': 5
+ }
},
- paint: {
- 'line-color': highlightColor,
- 'line-dasharray': [0.2, 2],
- 'line-width': 5
- }
- },
- // active linestring
- {
- id: 'gl-draw-line-active',
- type: 'line',
- filter: ['all',
- ['==', '$type', 'LineString'],
- ['==', 'active', 'true']
- ],
- layout: {
- 'line-cap': 'round',
- 'line-join': 'round'
+ // active linestring
+ {
+ id: 'gl-draw-line-active',
+ type: 'line',
+ filter: ['all',
+ ['==', '$type', 'LineString'],
+ ['==', 'active', 'true']
+ ],
+ layout: {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ },
+ paint: {
+ 'line-color': highlightColor,
+ 'line-dasharray': [0.2, 2],
+ 'line-width': 5
+ }
+ },
+ // midpoints to extend lines/polygons
+ {
+ id: 'gl-draw-polygon-midpoint',
+ type: 'circle',
+ filter: ['all',
+ ['==', '$type', 'Point'],
+ ['==', 'meta', 'midpoint']],
+ paint: {
+ 'circle-radius': pointSize,
+ 'circle-color': 'grey',
+ 'circle-opacity': 0.8,
+ 'circle-stroke-color': '#ffffff',
+ 'circle-stroke-width': 1
+ }
+ },
+ // default point behind symbols, transparent points etc.
+ {
+ id: 'gl-draw-point-point-stroke-inactive',
+ type: 'circle',
+ filter: ['all',
+ ['==', 'active', 'false'],
+ ['==', '$type', 'Point'],
+ ['==', 'meta', 'feature'],
+ ['!=', 'mode', 'static']
+ ],
+ paint: {
+ 'circle-radius': pointSize,
+ 'circle-opacity': 0.2,
+ 'circle-color': '#ffffff',
+ 'circle-stroke-color': '#c0c0c0',
+ 'circle-stroke-width': 1
+ }
},
- paint: {
- 'line-color': highlightColor,
- 'line-dasharray': [0.2, 2],
- 'line-width': 5
- }
- },
- // midpoints to extend lines/polygons
- {
- id: 'gl-draw-polygon-midpoint',
- type: 'circle',
- filter: ['all',
- ['==', '$type', 'Point'],
- ['==', 'meta', 'midpoint']],
- paint: {
- 'circle-radius': pointSize,
- 'circle-color': 'grey',
- 'circle-opacity': 0.8,
- 'circle-stroke-color': '#ffffff',
- 'circle-stroke-width': 1
- }
- },
- // default point behind symbols, transparent points etc.
- {
- id: 'gl-draw-point-point-stroke-inactive',
- type: 'circle',
- filter: ['all',
- ['==', 'active', 'false'],
- ['==', '$type', 'Point'],
- ['==', 'meta', 'feature'],
- ['!=', 'mode', 'static']
- ],
- paint: {
- 'circle-radius': pointSize,
- 'circle-opacity': 0.2,
- 'circle-color': '#ffffff',
- 'circle-stroke-color': '#c0c0c0',
- 'circle-stroke-width': 1
- }
- },
- // active point, either single or on a line / polygon
- {
- id: 'gl-draw-point-stroke-active',
- type: 'circle',
- filter: ['all',
- ['==', '$type', 'Point'],
- ['==', 'active', 'true'],
- ['!=', 'meta', 'midpoint']
- ],
- paint: {
- 'circle-radius': ['*', pointSizeMax, 2],
- 'circle-color': '#ffffff',
- 'circle-opacity': 0.2,
- 'circle-stroke-color': highlightColor,
- 'circle-stroke-width': 3
- }
- },
- // inactive single point features
- removeSource(styles['points-border-layer']),
- removeSource(styles['points-layer']),
+ // active point, either single or on a line / polygon
+ {
+ id: 'gl-draw-point-stroke-active',
+ type: 'circle',
+ filter: ['all',
+ ['==', '$type', 'Point'],
+ ['==', 'active', 'true'],
+ ['!=', 'meta', 'midpoint']
+ ],
+ paint: {
+ 'circle-radius': ['*', pointSizeMax, 2],
+ 'circle-color': '#ffffff',
+ 'circle-opacity': 0.2,
+ 'circle-stroke-color': highlightColor,
+ 'circle-stroke-width': 3
+ }
+ },
+ // inactive single point features
+ removeSource(styles()['points-border-layer']),
+ removeSource(styles()['points-layer']),
- // inactive vertex points on lines + polygons, outline
- // renderingoutline seperately to generate nicer overlay effect
- {
- id: 'gl-draw-polygon-and-line-vertex-outline-inactive',
- type: 'circle',
- filter: ['all',
- ['==', 'meta', 'vertex'],
- ['==', '$type', 'Point'],
- ['!=', 'mode', 'static']
- ],
- paint: {
- 'circle-radius': pointSize,
- 'circle-opacity': 0,
- 'circle-stroke-color': '#444',
- 'circle-stroke-width': pointOutlineSize,
- 'circle-stroke-opacity': 1
- }
- },
- // inactive vertex points on lines + polygons
- {
- id: 'gl-draw-polygon-and-line-vertex-inactive',
- type: 'circle',
- filter: ['all',
- ['==', 'meta', 'vertex'],
- ['==', '$type', 'Point'],
- ['!=', 'mode', 'static']
- ],
- paint: {
- 'circle-radius': pointSize,
- 'circle-color': highlightColor
+ // inactive vertex points on lines + polygons, outline
+ // renderingoutline seperately to generate nicer overlay effect
+ {
+ id: 'gl-draw-polygon-and-line-vertex-outline-inactive',
+ type: 'circle',
+ filter: ['all',
+ ['==', 'meta', 'vertex'],
+ ['==', '$type', 'Point'],
+ ['!=', 'mode', 'static']
+ ],
+ paint: {
+ 'circle-radius': pointSize,
+ 'circle-opacity': 0,
+ 'circle-stroke-color': '#444',
+ 'circle-stroke-width': pointOutlineSize,
+ 'circle-stroke-opacity': 1
+ }
+ },
+ // inactive vertex points on lines + polygons
+ {
+ id: 'gl-draw-polygon-and-line-vertex-inactive',
+ type: 'circle',
+ filter: ['all',
+ ['==', 'meta', 'vertex'],
+ ['==', '$type', 'Point'],
+ ['!=', 'mode', 'static']
+ ],
+ paint: {
+ 'circle-radius': pointSize,
+ 'circle-color': highlightColor
+ }
}
- }
-]
+ ]
+}
function removeSource (style) {
const { source, ...filteredStyle } = style
diff --git a/app/javascript/maplibre/map.js b/app/javascript/maplibre/map.js
index 5b3c5a54..66af3a5c 100644
--- a/app/javascript/maplibre/map.js
+++ b/app/javascript/maplibre/map.js
@@ -1,7 +1,7 @@
-import { basemaps } from 'maplibre/basemaps'
+import { basemaps, defaultFont } from 'maplibre/basemaps'
import { draw } from 'maplibre/edit'
import { resetControls, initSettingsModal, geocoderConfig, initCtrlTooltips } from 'maplibre/controls'
-import { initializeViewStyles } from 'maplibre/styles'
+import { initializeViewStyles, setStyleDefaultFont } from 'maplibre/styles'
import { highlightFeature, resetHighlightedFeature } from 'maplibre/feature'
import { AnimatePointAnimation } from 'maplibre/animations'
import * as functions from 'helpers/functions'
@@ -16,8 +16,9 @@ export let geojsonData //= { type: 'FeatureCollection', features: [] }
export let mapProperties
export let lastMousePosition
export let highlightedFeature
+export let backgroundMapLayer
+
let mapInteracted
-let backgroundMapLayer
let backgroundTerrain
// workflow of event based map loading:
@@ -349,12 +350,13 @@ export function setBackgroundMapLayer (mapName = mapProperties.base_map, force =
if (backgroundMapLayer === mapName && backgroundTerrain === mapProperties.terrain && !force) { return }
if (basemaps[mapName]) {
map.once('style.load', () => { status('Loaded base map ' + mapName) })
- map.setStyle(basemaps[mapName],
+ backgroundMapLayer = mapName
+ backgroundTerrain = mapProperties.terrain
+ setStyleDefaultFont(basemaps[mapName].font || defaultFont)
+ map.setStyle(basemaps[mapName].style,
// adding 'diff: false' so that 'style.load' gets triggered (https://github.com/maplibre/maplibre-gl-js/issues/2587)
// which will trigger loadGeoJsonData()
{ diff: false, strictMode: true })
- backgroundMapLayer = mapName
- backgroundTerrain = mapProperties.terrain
} else {
console.error('Base map ' + mapName + ' not available!')
}
diff --git a/app/javascript/maplibre/styles.js b/app/javascript/maplibre/styles.js
index a77b4b44..b1179347 100644
--- a/app/javascript/maplibre/styles.js
+++ b/app/javascript/maplibre/styles.js
@@ -18,9 +18,11 @@ export const viewStyleNames = [
'polygon-layer-extrusion'
]
+export function setStyleDefaultFont (font) { labelFont = [font] }
+
export function initializeViewStyles () {
viewStyleNames.forEach(styleName => {
- map.addLayer(styles[styleName])
+ map.addLayer(styles()[styleName])
})
sortLayers()
console.log('View styles added')
@@ -173,260 +175,257 @@ const iconSizeFactor = ['/', pointSizeMax, 6]
const iconSize = ['*', 1 / 8, iconSizeFactor]
// const iconSizeActive = ['*', 1.1, iconSize] // icon-size is not a paint property
const labelSize = ['to-number', ['coalesce', ['get', 'user_label-size'], ['get', 'label-size'], 16]]
+// default font is set in basemap def basemaps[backgroundMapLayer]['font']
+let labelFont
-// font must be available via glyphs:
-// openmaptiles: https://github.com/openmaptiles/fonts/tree/gh-pages
-// maptiler: https://docs.maptiler.com/gl-style-specification/glyphs/
-// versatiles: https://github.com/versatiles-org/versatiles-fonts/tree/main/fonts
-// Emojis are not in the character range: https://github.com/maplibre/maplibre-gl-js/issues/2307
-const labelFont = ['Klokantech Noto Sans Bold']
-
-export const styles = {
- 'polygon-layer': {
- id: 'polygon-layer',
- type: 'fill',
- source: 'geojson-source',
- filter: ['all',
- ['in', '$type', 'Polygon']],
- paint: {
- 'fill-color': fillColor,
- 'fill-opacity': [
- 'case',
- ['boolean', ['feature-state', 'active'], false],
- fillOpacityActive,
- fillOpacity
- ]
- }
- },
- 'polygon-layer-extrusion': {
- id: 'polygon-layer-extrusion',
- type: 'fill-extrusion',
- source: 'geojson-source',
- filter: ['all',
- ['in', '$type', 'Polygon'],
- ['>', 'fill-extrusion-height', 0]],
- paint: {
- 'fill-extrusion-color': ['coalesce',
- ['get', 'fill-extrusion-color'],
- ['get', 'user_fill-extrusion-color'],
- ['get', 'fill'],
- ['get', 'user_fill'],
- featureColor],
- 'fill-extrusion-height': ['to-number', ['coalesce',
- ['get', 'fill-extrusion-height'],
- ['get', 'user_fill-extrusion-height']]],
- 'fill-extrusion-base': ['to-number', ['coalesce',
- ['get', 'fill-extrusion-base'],
- ['get', 'user_fill-extrusion-base']]],
- // opacity does not support data expressions!?!
- 'fill-extrusion-opacity': 0.8
- }
- },
- 'line-layer-outline': {
- id: 'line-layer-outline',
- type: 'line',
- source: 'geojson-source',
- filter: ['all',
- ['in', '$type', 'LineString']],
- layout: {
- 'line-join': 'round',
- 'line-cap': 'round'
+export function styles () {
+ return {
+ 'polygon-layer': {
+ id: 'polygon-layer',
+ type: 'fill',
+ source: 'geojson-source',
+ filter: ['all',
+ ['in', '$type', 'Polygon']],
+ paint: {
+ 'fill-color': fillColor,
+ 'fill-opacity': [
+ 'case',
+ ['boolean', ['feature-state', 'active'], false],
+ fillOpacityActive,
+ fillOpacity
+ ]
+ }
},
- paint: {
- 'line-color': outlineColor,
- 'line-width': outlineWidth,
- 'line-opacity': lineOpacity
- }
- },
- // lines + polygon outlines
- 'line-layer': {
- id: 'line-layer',
- type: 'line',
- source: 'geojson-source',
- filter: ['all',
- ['in', '$type', 'LineString', 'Polygon']],
- layout: {
- 'line-join': 'round',
- 'line-cap': 'round'
+ 'polygon-layer-extrusion': {
+ id: 'polygon-layer-extrusion',
+ type: 'fill-extrusion',
+ source: 'geojson-source',
+ filter: ['all',
+ ['in', '$type', 'Polygon'],
+ ['>', 'fill-extrusion-height', 0]],
+ paint: {
+ 'fill-extrusion-color': ['coalesce',
+ ['get', 'fill-extrusion-color'],
+ ['get', 'user_fill-extrusion-color'],
+ ['get', 'fill'],
+ ['get', 'user_fill'],
+ featureColor],
+ 'fill-extrusion-height': ['to-number', ['coalesce',
+ ['get', 'fill-extrusion-height'],
+ ['get', 'user_fill-extrusion-height']]],
+ 'fill-extrusion-base': ['to-number', ['coalesce',
+ ['get', 'fill-extrusion-base'],
+ ['get', 'user_fill-extrusion-base']]],
+ // opacity does not support data expressions!?!
+ 'fill-extrusion-opacity': 0.8
+ }
},
- paint: {
- 'line-color': [
- 'case',
- ['boolean', ['==', ['geometry-type'], 'LineString'], true],
- lineColor, lineColorPolygon
- ],
- 'line-width': lineWidth,
- 'line-opacity': [
- 'case',
- ['boolean', ['==', ['geometry-type'], 'LineString'], true],
- ['case', ['boolean', ['feature-state', 'active'], false],
- lineOpacityActive, lineOpacity
- ], 1
- ]
- }
- },
- 'line-layer-hit': {
- id: 'line-layer-hit',
- type: 'line',
- source: 'geojson-source',
- filter: ['all',
- ['in', '$type', 'LineString']],
- paint: {
- 'line-width': ['+', 15, outlineWidthMax],
- 'line-opacity': 0
- }
- },
- 'points-border-layer': {
- id: 'points-border-layer',
- type: 'circle',
- source: 'geojson-source',
- filter: ['all',
- ['==', '$type', 'Point'],
- ['!=', 'meta', 'midpoint'],
- ['!=', 'meta', 'vertex'],
- ['none', ['has', 'user_marker-image-url'], ['has', 'marker-image-url'],
- ['has', 'user_marker-symbol'], ['has', 'marker-symbol']]
- ],
- paint: {
- 'circle-pitch-scale': 'map', // points get bigger when camera is closer
- 'circle-radius': pointSize,
- 'circle-opacity': 0,
- 'circle-stroke-color': pointOutlineColor,
- 'circle-blur': 0.1,
- 'circle-stroke-width': [
- 'case',
- ['boolean', ['feature-state', 'active'], false],
- pointOutlineSizeActive,
- pointOutlineSize
- ],
- 'circle-stroke-opacity': pointOpacity + 0.2
- }
- },
- 'points-layer': {
- id: 'points-layer',
- type: 'circle',
- source: 'geojson-source',
- filter: ['all',
- ['==', '$type', 'Point'],
- ['!=', 'meta', 'midpoint'],
- ['none', ['has', 'user_marker-image-url'], ['has', 'marker-image-url'],
- ['has', 'user_marker-symbol'], ['has', 'marker-symbol']]
- ],
- paint: {
- 'circle-pitch-scale': 'map', // points get bigger when camera is closer
- 'circle-radius': pointSize,
- 'circle-color': pointColor,
- 'circle-opacity': [
- 'match',
- ['coalesce', ['get', 'user_marker-color'], ['get', 'marker-color']],
- 'transparent', 0, // If marker-color is 'transparent', set circle-radius to 0
- [
+ 'line-layer-outline': {
+ id: 'line-layer-outline',
+ type: 'line',
+ source: 'geojson-source',
+ filter: ['all',
+ ['in', '$type', 'LineString']],
+ layout: {
+ 'line-join': 'round',
+ 'line-cap': 'round'
+ },
+ paint: {
+ 'line-color': outlineColor,
+ 'line-width': outlineWidth,
+ 'line-opacity': lineOpacity
+ }
+ },
+ // lines + polygon outlines
+ 'line-layer': {
+ id: 'line-layer',
+ type: 'line',
+ source: 'geojson-source',
+ filter: ['all',
+ ['in', '$type', 'LineString', 'Polygon']],
+ layout: {
+ 'line-join': 'round',
+ 'line-cap': 'round'
+ },
+ paint: {
+ 'line-color': [
'case',
- ['boolean', ['feature-state', 'active'], false],
- pointOpacityActive,
- pointOpacity
- ]],
- 'circle-blur': 0.1
- }
- },
- 'points-hit-layer': {
- id: 'points-hit-layer',
- type: 'circle',
- source: 'geojson-source',
- filter: ['all',
- ['==', '$type', 'Point'],
- ['!=', 'active', 'true']
- ],
- paint: {
- 'circle-radius': ['+', 5, pointSizeMax],
- 'circle-opacity': 0
- }
- },
- // background + border for symbols
- 'symbols-border-layer': {
- id: 'symbols-border-layer',
- type: 'circle',
- source: 'geojson-source',
- filter: ['all',
- ['==', '$type', 'Point'],
- ['!=', 'meta', 'midpoint'],
- ['!=', 'meta', 'vertex'],
- ['any', ['has', 'user_marker-image-url'], ['has', 'marker-image-url'],
- ['has', 'user_marker-symbol'], ['has', 'marker-symbol']]
- ],
- paint: {
- 'circle-pitch-scale': 'map', // points get bigger when camera is closer
- 'circle-radius': pointSize,
- 'circle-color': pointColor,
- 'circle-opacity': [
- 'match',
- ['coalesce', ['get', 'user_marker-color'], ['get', 'marker-color']],
- 'transparent', 0, // If marker-color is 'transparent', set circle-radius to 0
- [
+ ['boolean', ['==', ['geometry-type'], 'LineString'], true],
+ lineColor, lineColorPolygon
+ ],
+ 'line-width': lineWidth,
+ 'line-opacity': [
+ 'case',
+ ['boolean', ['==', ['geometry-type'], 'LineString'], true],
+ ['case', ['boolean', ['feature-state', 'active'], false],
+ lineOpacityActive, lineOpacity
+ ], 1
+ ]
+ }
+ },
+ 'line-layer-hit': {
+ id: 'line-layer-hit',
+ type: 'line',
+ source: 'geojson-source',
+ filter: ['all',
+ ['in', '$type', 'LineString']],
+ paint: {
+ 'line-width': ['+', 15, outlineWidthMax],
+ 'line-opacity': 0
+ }
+ },
+ 'points-border-layer': {
+ id: 'points-border-layer',
+ type: 'circle',
+ source: 'geojson-source',
+ filter: ['all',
+ ['==', '$type', 'Point'],
+ ['!=', 'meta', 'midpoint'],
+ ['!=', 'meta', 'vertex'],
+ ['none', ['has', 'user_marker-image-url'], ['has', 'marker-image-url'],
+ ['has', 'user_marker-symbol'], ['has', 'marker-symbol']]
+ ],
+ paint: {
+ 'circle-pitch-scale': 'map', // points get bigger when camera is closer
+ 'circle-radius': pointSize,
+ 'circle-opacity': 0,
+ 'circle-stroke-color': pointOutlineColor,
+ 'circle-blur': 0.1,
+ 'circle-stroke-width': [
'case',
['boolean', ['feature-state', 'active'], false],
- pointOpacityActive,
- pointOpacity
- ]],
- 'circle-stroke-color': pointOutlineColor,
- 'circle-blur': 0.05,
- 'circle-stroke-width': [
- 'case',
- ['boolean', ['feature-state', 'active'], false],
- pointOutlineSizeActive,
- pointOutlineSize
+ pointOutlineSizeActive,
+ pointOutlineSize
+ ],
+ 'circle-stroke-opacity': pointOpacity + 0.2
+ }
+ },
+ 'points-layer': {
+ id: 'points-layer',
+ type: 'circle',
+ source: 'geojson-source',
+ filter: ['all',
+ ['==', '$type', 'Point'],
+ ['!=', 'meta', 'midpoint'],
+ ['none', ['has', 'user_marker-image-url'], ['has', 'marker-image-url'],
+ ['has', 'user_marker-symbol'], ['has', 'marker-symbol']]
],
- 'circle-stroke-opacity': pointOpacity + 0.2
- }
- },
- // support symbols on all feature types
- 'symbols-layer': {
- id: 'symbols-layer',
- type: 'symbol',
- source: 'geojson-source',
- filter: ['!=', 'active', 'true'],
- // minzoom: 15, // TODO: only static values possible right now
- layout: {
- 'symbol-sort-key': ['to-number', ['coalesce', ['get', 'user_sort-key'], ['get', 'sort-key'], 1]],
- 'icon-image': ['coalesce',
- ['get', 'marker-image-url'],
- // replacing marker-symbol value with path to emoji png
- ['case',
- ['has', 'marker-symbol'],
- ['concat', '/emojis/noto/', ['get', 'marker-symbol'], '.png'],
- '']
+ paint: {
+ 'circle-pitch-scale': 'map', // points get bigger when camera is closer
+ 'circle-radius': pointSize,
+ 'circle-color': pointColor,
+ 'circle-opacity': [
+ 'match',
+ ['coalesce', ['get', 'user_marker-color'], ['get', 'marker-color']],
+ 'transparent', 0, // If marker-color is 'transparent', set circle-radius to 0
+ [
+ 'case',
+ ['boolean', ['feature-state', 'active'], false],
+ pointOpacityActive,
+ pointOpacity
+ ]],
+ 'circle-blur': 0.1
+ }
+ },
+ 'points-hit-layer': {
+ id: 'points-hit-layer',
+ type: 'circle',
+ source: 'geojson-source',
+ filter: ['all',
+ ['==', '$type', 'Point'],
+ ['!=', 'active', 'true']
],
- 'icon-size': iconSize, // cannot scale on hover/zoom because it's not a paint property
- 'icon-overlap': 'always' // https://maplibre.org/maplibre-style-spec/layers/#icon-overlap
- // 'icon-ignore-placement': true // other symbols can be visible even if they collide with the icon
+ paint: {
+ 'circle-radius': ['+', 5, pointSizeMax],
+ 'circle-opacity': 0
+ }
+ },
+ // background + border for symbols
+ 'symbols-border-layer': {
+ id: 'symbols-border-layer',
+ type: 'circle',
+ source: 'geojson-source',
+ filter: ['all',
+ ['==', '$type', 'Point'],
+ ['!=', 'meta', 'midpoint'],
+ ['!=', 'meta', 'vertex'],
+ ['any', ['has', 'user_marker-image-url'], ['has', 'marker-image-url'],
+ ['has', 'user_marker-symbol'], ['has', 'marker-symbol']]
+ ],
+ paint: {
+ 'circle-pitch-scale': 'map', // points get bigger when camera is closer
+ 'circle-radius': pointSize,
+ 'circle-color': pointColor,
+ 'circle-opacity': [
+ 'match',
+ ['coalesce', ['get', 'user_marker-color'], ['get', 'marker-color']],
+ 'transparent', 0, // If marker-color is 'transparent', set circle-radius to 0
+ [
+ 'case',
+ ['boolean', ['feature-state', 'active'], false],
+ pointOpacityActive,
+ pointOpacity
+ ]],
+ 'circle-stroke-color': pointOutlineColor,
+ 'circle-blur': 0.05,
+ 'circle-stroke-width': [
+ 'case',
+ ['boolean', ['feature-state', 'active'], false],
+ pointOutlineSizeActive,
+ pointOutlineSize
+ ],
+ 'circle-stroke-opacity': pointOpacity + 0.2
+ }
},
- paint: {
+ // support symbols on all feature types
+ 'symbols-layer': {
+ id: 'symbols-layer',
+ type: 'symbol',
+ source: 'geojson-source',
+ filter: ['!=', 'active', 'true'],
+ // minzoom: 15, // TODO: only static values possible right now
+ layout: {
+ 'symbol-sort-key': ['to-number', ['coalesce', ['get', 'user_sort-key'], ['get', 'sort-key'], 1]],
+ 'icon-image': ['coalesce',
+ ['get', 'marker-image-url'],
+ // replacing marker-symbol value with path to emoji png
+ ['case',
+ ['has', 'marker-symbol'],
+ ['concat', '/emojis/noto/', ['get', 'marker-symbol'], '.png'],
+ '']
+ ],
+ 'icon-size': iconSize, // cannot scale on hover/zoom because it's not a paint property
+ 'icon-overlap': 'always' // https://maplibre.org/maplibre-style-spec/layers/#icon-overlap
+ // 'icon-ignore-placement': true // other symbols can be visible even if they collide with the icon
+ },
+ paint: {
// cannot set circle-stroke-* in the symbol layer :-(
- }
- },
- 'text-layer': {
- id: 'text-layer',
- type: 'symbol',
- source: 'geojson-source',
- filter: ['has', 'label'],
- layout: {
- 'icon-overlap': 'never',
- 'text-field': ['coalesce', ['get', 'label'], ['get', 'room']],
- 'text-size': labelSize,
- 'text-font': labelFont,
- // arrange text to avoid collision
- 'text-variable-anchor': ['top'], // text under point
- // distance of the text in 'em'
- 'text-radial-offset': ['/', pointSizeMax, 14],
- 'text-justify': 'auto',
- 'text-ignore-placement': false, // hide on collision
- // TODO: sort keys on text are ascending, on symbols descending???
- 'symbol-sort-key': ['-', 1000, ['to-number', ['coalesce', ['get', 'user_sort-key'], ['get', 'sort-key'], 1]]]
+ }
},
- paint: {
- 'text-color': ['coalesce', ['get', 'user_label-color'], ['get', 'label-color'], '#000'],
- 'text-halo-color': ['coalesce', ['get', 'user_label-shadow'], ['get', 'label-shadow'], '#fff'],
- 'text-halo-width': 1
+ 'text-layer': {
+ id: 'text-layer',
+ type: 'symbol',
+ source: 'geojson-source',
+ filter: ['has', 'label'],
+ layout: {
+ 'icon-overlap': 'never',
+ 'text-field': ['coalesce', ['get', 'label'], ['get', 'room']],
+ 'text-size': labelSize,
+ 'text-font': labelFont,
+ // arrange text to avoid collision
+ 'text-variable-anchor': ['top'], // text under point
+ // distance of the text in 'em'
+ 'text-radial-offset': ['/', pointSizeMax, 14],
+ 'text-justify': 'auto',
+ 'text-ignore-placement': false, // hide on collision
+ // TODO: sort keys on text are ascending, on symbols descending???
+ 'symbol-sort-key': ['-', 1000, ['to-number', ['coalesce', ['get', 'user_sort-key'], ['get', 'sort-key'], 1]]]
+ },
+ paint: {
+ 'text-color': ['coalesce', ['get', 'user_label-color'], ['get', 'label-color'], '#000'],
+ 'text-halo-color': ['coalesce', ['get', 'user_label-shadow'], ['get', 'label-shadow'], '#fff'],
+ 'text-halo-width': 1
+ }
}
}
}