From fb2b631cb51edb83562371762b6e29c8d24c1de5 Mon Sep 17 00:00:00 2001 From: Candid Dauth Date: Fri, 16 Feb 2024 06:23:35 +0100 Subject: [PATCH] Add Lima Labs as the default layer --- config.env.example | 7 ++++- docs/src/developers/leaflet/layers.md | 16 ++++++++++++ docs/src/developers/server/docker.md | 4 ++- facilmap.conf.example | 37 --------------------------- frontend/src/map/config.ts | 3 ++- frontend/src/map/map.ts | 6 +++++ leaflet/src/layers.ts | 26 ++++++++++++++++++- server/src/config.ts | 3 +++ server/src/frontend.ts | 7 +++-- utils/src/types.ts | 7 +++++ 10 files changed, 73 insertions(+), 43 deletions(-) delete mode 100644 facilmap.conf.example diff --git a/config.env.example b/config.env.example index d894b958..89a0fb88 100644 --- a/config.env.example +++ b/config.env.example @@ -31,4 +31,9 @@ # for Geo IP lookup (to show the initial map state) and kept in memory. # Sign up here: https://www.maxmind.com/en/geolite2/signup #MAXMIND_USER_ID= -#MAXMIND_LICENSE_KEY= \ No newline at end of file +#MAXMIND_LICENSE_KEY= + +# Lima Labs provides nice double resolution layers. +# If this is defined, they are used as the default layer instead of Mapnik. +# Get an API key here: https://maps.lima-labs.com/ +#LIMA_LABS_TOKEN= \ No newline at end of file diff --git a/docs/src/developers/leaflet/layers.md b/docs/src/developers/leaflet/layers.md index ed4e9d58..30d07d64 100644 --- a/docs/src/developers/leaflet/layers.md +++ b/docs/src/developers/leaflet/layers.md @@ -25,6 +25,22 @@ const byName = (layerMap) => Object.fromEntries(Object.entries(layerMap).map(([k L.control.layers(byName(layers.baseLayers), byName(layers.overlays)).addTo(map); ``` +## Set the layer options + +There are some global layer options that change the behaviour of the available layers: +* `limaLabsToken`: A [Lima Labs](https://maps.lima-labs.com/) API token. If defined, the Lima Labs layer will be available and used as the default layer instead of Mapnik. Lima Labs layers are very similar to Mapnik in style, but they are double resolution (so they don’t look pixely on high-resolution screens) and have English place names in addition to the local language. + +To set the global layer options, use the `setLayerOptions()` function: +```javascript +import { setLayerOptions } from "facilmap-leaflet"; + +setLayerOptions({ + limaLabsToken: "..." +}); +``` + +Note that to avoid unexpected inconsistencies, this should be called before `getLayers()` or any other of functions documented on this page are called. + ## Change the available layers To change the available layers for a particular Leaflet map, set the `_fmLayers` properties of that map. diff --git a/docs/src/developers/server/docker.md b/docs/src/developers/server/docker.md index a64a81e2..6c047c5f 100644 --- a/docs/src/developers/server/docker.md +++ b/docs/src/developers/server/docker.md @@ -33,6 +33,7 @@ services: MAPZEN_TOKEN: # Get an API key on https://mapzen.com/developers/sign_up (needed for elevation info) MAXMIND_USER_ID: # Sign up here https://www.maxmind.com/en/geolite2/signup (needed for geoip lookup to show initial map state) MAXMIND_LICENSE_KEY: + LIMA_LABS_TOKEN: # Get an API key on https://maps.lima-labs.com/ (optional, needed for double-resolution tiles) restart: unless-stopped db: image: mariadb @@ -68,6 +69,7 @@ services: MAPZEN_TOKEN: # Get an API key on https://mapzen.com/developers/sign_up (needed for elevation info) MAXMIND_USER_ID: # Sign up here https://www.maxmind.com/en/geolite2/signup (needed for geoip lookup to show initial map state) MAXMIND_LICENSE_KEY: + LIMA_LABS_TOKEN: # Get an API key on https://maps.lima-labs.com/ (optional, needed for double-resolution tiles) restart: unless-stopped db: image: postgis/postgis @@ -86,5 +88,5 @@ To manually create the necessary docker containers, use these commands: ```bash docker create --name=facilmap_db -e MYSQL_DATABASE=facilmap -e MYSQL_USER=facilmap -e MYSQL_PASSWORD=password -e MYSQL_RANDOM_ROOT_PASSWORD=true --restart=unless-stopped mariadb --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci -docker create --link=facilmap_db -p 8080 --name=facilmap -e "USER_AGENT=My FacilMap (https://facilmap.example.org/, facilmap@example.org)" -e DB_TYPE=mysql -e DB_HOST=facilmap_db -e DB_NAME=facilmap -e DB_USER=facilmap -e DB_PASSWORD=facilmap -e ORS_TOKEN= -e MAPBOX_TOKEN= -e MAPZEN_TOKEN= -e MAXMIND_USER_ID= -e MAXMIND_LICENSE_KEY= --restart=unless-stopped facilmap/facilmap +docker create --link=facilmap_db -p 8080 --name=facilmap -e "USER_AGENT=My FacilMap (https://facilmap.example.org/, facilmap@example.org)" -e DB_TYPE=mysql -e DB_HOST=facilmap_db -e DB_NAME=facilmap -e DB_USER=facilmap -e DB_PASSWORD=facilmap -e ORS_TOKEN= -e MAPBOX_TOKEN= -e MAPZEN_TOKEN= -e MAXMIND_USER_ID= -e MAXMIND_LICENSE_KEY= -e LIMA_LABS_TOKEN= --restart=unless-stopped facilmap/facilmap ``` \ No newline at end of file diff --git a/facilmap.conf.example b/facilmap.conf.example deleted file mode 100644 index b9541e39..00000000 --- a/facilmap.conf.example +++ /dev/null @@ -1,37 +0,0 @@ -# FacilMap configuration file. See https://github.com/motdotla/dotenv#rules for the syntax. - -# Database configuration. Possible database types: mysql, postgres, mariadb, sqlite -#DB_TYPE=mysql -#DB_HOST=localhost -#DB_PORT= -#DB_NAME=facilmap -#DB_USER=facilmap -#DB_PASSWORD=password - -# Where the FacilMap server should listen -#HOST= -#PORT=8080 - -# The User-Agent header that the FacilMap server will use when talking to other services (for example to OpenStreetMap, Mapbox, OpenRouteService, ...) -#USER_AGENT=FacilMap (https://facilmap.org/) - -# Mapbox configuration. Needed for routing without any advanced mode settings. Get an API key on https://www.mapbox.com/signup/. -#MAPBOX_TOKEN= - -# OpenRouteService configuration. Needed for routing with any advanced mode settings. Get a token on https://go.openrouteservice.org/. -#ORS_TOKEN= - -# MapZen configuration. Needed to get the elevation of markers, search results or GPS tracks. Get an API key on https://mapzen.com/developers/sign_up. -#MAPZEN_TOKEN= - -# Maxmind configuration. If specified, the maxmind GeoLite2 database will be downloaded for Geo IP lookup (to show the initial map state) and kept it in memory -# Sign up here: https://www.maxmind.com/en/geolite2/signup -#MAXMIND_USER_ID= -#MAXMIND_LICENSE_KEY= - -# Set to true to enable dev mode and webpack dev middleware -#FM_DEV= - -# Set to a comma-separated list of values (or *) to enable debug output by particular components. See https://github.com/visionmedia/debug for the syntax. -# Some possible values: sql, express:* -#DEBUG= \ No newline at end of file diff --git a/frontend/src/map/config.ts b/frontend/src/map/config.ts index 52e14634..1d96b7b8 100644 --- a/frontend/src/map/config.ts +++ b/frontend/src/map/config.ts @@ -1,4 +1,5 @@ +import type { InjectedConfig } from "facilmap-utils"; import $ from "jquery"; -const config = JSON.parse($("meta[name=fmConfig]").attr("content")!); +const config: InjectedConfig = JSON.parse($("meta[name=fmConfig]").attr("content")!); export default config; diff --git a/frontend/src/map/map.ts b/frontend/src/map/map.ts index 7a089d3b..c7cc7775 100644 --- a/frontend/src/map/map.ts +++ b/frontend/src/map/map.ts @@ -4,6 +4,8 @@ import { FacilMap } from "../lib"; import { decodeQueryString, encodeQueryString, normalizePadName } from "facilmap-utils"; import decodeURIComponent from "decode-uri-component"; import "../lib/bootstrap.scss"; // Not imported in lib/index.ts because we don't want it to be bundled +import { setLayerOptions } from "facilmap-leaflet"; +import config from "./config"; // Dereferrer $(document).on("click", "a", function() { @@ -21,6 +23,10 @@ $(document).on("click", "a", function() { if ('serviceWorker' in navigator) navigator.serviceWorker.register('./sw.js'); +setLayerOptions({ + limaLabsToken: config.limaLabsToken +}); + const queryParams = decodeQueryString(location.search); const toBoolean = (val: string, def: boolean) => (val == null ? def : val != "0" && val != "false" && val != "no"); diff --git a/leaflet/src/layers.ts b/leaflet/src/layers.ts index e1dcac82..4102e3eb 100644 --- a/leaflet/src/layers.ts +++ b/leaflet/src/layers.ts @@ -3,7 +3,9 @@ import AutoGraticule from "leaflet-auto-graticule"; import FreieTonne from "leaflet-freie-tonne"; export const defaultVisibleLayers: VisibleLayers = { - baseLayer: 'Mpnk', + get baseLayer() { + return layerOptions.limaLabsToken ? 'Lima' : 'Mpnk'; + }, overlays: [] }; @@ -15,6 +17,14 @@ export interface Layers { export function createDefaultLayers(): Layers & { fallbackLayer: string | undefined } { return { baseLayers: { + ...(layerOptions.limaLabsToken ? { + Lima: tileLayer(`https://cdn.lima-labs.com/{z}/{x}/{y}.png?api=${encodeURIComponent(layerOptions.limaLabsToken)}`, { + fmName: "Lima Labs", + attribution: '© Lima Labs / OSM Contributors', + noWrap: true + }) + } : {}), + Mpnk: tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { fmName: "Mapnik", attribution: '© OSM Contributors', @@ -120,6 +130,20 @@ export function setLayers(create: typeof createDefaultLayers): void { createLayers = create; } +export interface LayerOptions { + limaLabsToken?: string; +} + +let layerOptions: LayerOptions = {}; + +export function getLayerOptions(): LayerOptions { + return layerOptions; +} + +export function setLayerOptions(options: LayerOptions): void { + layerOptions = options; +} + export function getLayers(map: Map): Layers { if (!map._fmLayers) { const { baseLayers, overlays, fallbackLayer } = createLayers(); diff --git a/server/src/config.ts b/server/src/config.ts index a402f2be..327a4cc3 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -20,6 +20,7 @@ export interface Config { mapzenToken?: string; maxmindUserId?: string; maxmindLicenseKey?: string; + limaLabsToken?: string; } const config: Config = { @@ -42,6 +43,8 @@ const config: Config = { // Sign up here: https://www.maxmind.com/en/geolite2/signup maxmindUserId: process.env.MAXMIND_USER_ID || "", maxmindLicenseKey: process.env.MAXMIND_LICENSE_KEY || "", + + limaLabsToken: process.env.LIMA_LABS_TOKEN || "" // Get a token on https://maps.lima-labs.com/ }; export default config; diff --git a/server/src/frontend.ts b/server/src/frontend.ts index 68b90845..31217f29 100644 --- a/server/src/frontend.ts +++ b/server/src/frontend.ts @@ -6,7 +6,8 @@ import * as ejs from "ejs"; import * as utils from "facilmap-utils"; import { Router, type RequestHandler } from "express"; import { static as expressStatic } from "express"; -import { normalizeLineName, normalizeMarkerName, normalizePadName } from "facilmap-utils"; +import { normalizeLineName, normalizeMarkerName, normalizePadName, type InjectedConfig } from "facilmap-utils"; +import config from "./config"; export const isDevMode = !!process.env.FM_DEV; @@ -72,7 +73,9 @@ export async function renderMap(params: RenderMapParams): Promise { ]); return ejs.render(template, { - config: {}, + config: { + limaLabsToken: config.limaLabsToken + } satisfies InjectedConfig, ...injections, paths, ...params diff --git a/utils/src/types.ts b/utils/src/types.ts index cde88680..5cd5a409 100644 --- a/utils/src/types.ts +++ b/utils/src/types.ts @@ -2,4 +2,11 @@ export type Optional = Omit & Partial>; export function isPromise(object: any): object is Promise { return typeof object === 'object' && 'then' in object && typeof object.then === 'function'; +} + +/** + * The config that the backend injects into the EJS template to be read by the frontend. + */ +export interface InjectedConfig { + limaLabsToken?: string; } \ No newline at end of file