Use Open Elevation instead of MapZen to retrieve point elevation (#250)

pull/256/head
Candid Dauth 2024-03-16 06:54:42 +01:00
rodzic db8d7d6080
commit bc4d97d998
10 zmienionych plików z 52 dodań i 61 usunięć

Wyświetl plik

@ -24,10 +24,6 @@
# Get an API key on https://www.mapbox.com/signup/
#MAPBOX_TOKEN=
# MapZen is used for getting elevation information
# 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 in memory.
# Sign up here: https://www.maxmind.com/en/geolite2/signup

Wyświetl plik

@ -18,17 +18,17 @@ The config of the FacilMap server can be set either by using environment variabl
| `DB_PASSWORD` | | `facilmap` | The password to connect to the database with. |
| `ORS_TOKEN` | * | | [OpenRouteService API key](https://openrouteservice.org/). |
| `MAPBOX_TOKEN` | * | | [Mapbox API key](https://www.mapbox.com/signup/). |
| `MAPZEN_TOKEN` | | | [Mapzen API key](https://mapzen.com/developers/sign_up). |
| `MAXMIND_USER_ID` | | | [MaxMind user ID](https://www.maxmind.com/en/geolite2/signup). |
| `MAXMIND_LICENSE_KEY` | | | MaxMind license key. |
| `LIMA_LABS_TOKEN` | | | [Lima Labs](https://maps.lima-labs.com/) API key |
| `HIDE_COMMERCIAL_MAP_LINKS` | | | Set to `1` to hide the links to Google/Bing Maps in the “Map style” menu. |
| `CUSTOM_CSS_FILE` | | | The path of a CSS file that should be included ([see more details below](#custom-css-file)).
| `CUSTOM_CSS_FILE` | | | The path of a CSS file that should be included ([see more details below](#custom-css-file)). |
| `NOMINATIM_URL` | | `https://nominatim.openstreetmap.org` | The URL to the Nominatim server (used to search for places). |
| `OPEN_ELEVATION_URL` | | `https://api.open-elevation.com` | The URL to the Open Elevation server (used to look up the elevation for markers). |
FacilMap makes use of several third-party services that require you to register (for free) and generate an API key:
* Mapbox and OpenRouteService are used for calculating routes. Mapbox is used for basic routes, OpenRouteService is used when custom route mode settings are made. If these API keys are not defined, calculating routes will fail.
* Maxmind provides a free database that maps IP addresses to approximate locations. FacilMap downloads this database to decide the initial map view for users (IP addresses are looked up in FacilMaps copy of the database, on IP addresses are sent to Maxmind). This API key is optional, if it is not set, the default view will be the whole world.
* Mapzen is used to look up the elevation info for search results. The API key is optional, if it is not set, no elevation info will be available for search results.
* Lima Labs is used for nicer and higher resolution map tiles than Mapnik. The API key is optional, if it is not set, Mapnik will be the default map style instead.
## Custom CSS file

Wyświetl plik

@ -34,7 +34,6 @@ services:
DB_PASSWORD: password
ORS_TOKEN: # Get an API key on https://go.openrouteservice.org/ (needed for routing)
MAPBOX_TOKEN: # Get an API key on https://www.mapbox.com/signup/ (needed for routing)
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)
@ -76,7 +75,6 @@ services:
DB_PASSWORD: password
ORS_TOKEN: # Get an API key on https://go.openrouteservice.org/ (needed for routing)
MAPBOX_TOKEN: # Get an API key on https://www.mapbox.com/signup/ (needed for routing)
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)
@ -100,5 +98,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 TRUST_PROXY=true -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
docker create --link=facilmap_db -p 8080 --name=facilmap -e "USER_AGENT=My FacilMap (https://facilmap.example.org/, facilmap@example.org)" -e TRUST_PROXY=true -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 MAXMIND_USER_ID= -e MAXMIND_LICENSE_KEY= -e LIMA_LABS_TOKEN= --restart=unless-stopped facilmap/facilmap
```

Wyświetl plik

@ -93,7 +93,7 @@
<template v-if="marker.ele != null">
<dt class="elevation">Elevation</dt>
<dd class="elevation">{{marker.ele}}^m</dd>
<dd class="elevation">{{marker.ele}}&#x202F;m</dd>
</template>
<template v-for="field in client.types[marker.typeId].fields" :key="field.name">

Wyświetl plik

@ -20,13 +20,14 @@ export interface Config {
db: DbConfig;
orsToken?: string;
mapboxToken?: string;
mapzenToken?: string;
maxmindUserId?: string;
maxmindLicenseKey?: string;
limaLabsToken?: string;
/** Hide the "Open this on Google/Bing Maps" links in the map style menu */
hideCommercialMapLinks?: boolean;
customCssFile?: string;
nominatimUrl: string;
openElevationApiUrl: string;
}
const config: Config = {
@ -51,7 +52,6 @@ const config: Config = {
},
orsToken: process.env.ORS_TOKEN || "", // Get a token on https://go.openrouteservice.org/
mapboxToken: process.env.MAPBOX_TOKEN || "", // Get an API key on https://www.mapbox.com/signup/
mapzenToken: process.env.MAPZEN_TOKEN || "", // Get an API key on https://mapzen.com/developers/sign_up
// 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
@ -63,6 +63,9 @@ const config: Config = {
hideCommercialMapLinks: process.env.HIDE_COMMERCIAL_MAP_LINKS === "1",
customCssFile: process.env.CUSTOM_CSS_FILE || undefined,
nominatimUrl: process.env.NOMINATIM_URL || "https://nominatim.openstreetmap.org",
openElevationApiUrl: process.env.OPEN_ELEVATION_URL || "https://api.open-elevation.com",
};
export default config;

Wyświetl plik

@ -274,7 +274,6 @@ export default class DatabaseLines {
}
async _setLinePoints(padId: PadId, lineId: ID, trackPoints: Point[], _noEvent?: boolean): Promise<void> {
// First get elevation, so that if that fails, we don't update anything
await this.LinePointModel.destroy({ where: { lineId: lineId } });
const create = [ ];

Wyświetl plik

@ -9,7 +9,7 @@ interface MetaModel extends Model<InferAttributes<MetaModel>, InferCreationAttri
export interface MetaProperties {
dropdownKeysMigrated: "1";
hasElevation: "1";
hasElevation: "1" | "2";
hasLegendOption: "1";
hasBboxes: "1";
untitledMigrationCompleted: "1";

Wyświetl plik

@ -1,48 +1,44 @@
import type { Point } from "facilmap-types";
import config from "./config";
// const API_URL = "https://elevation.mapzen.com/height";
// const LIMIT = 500;
// const MIN_TIME_BETWEEN_REQUESTS = 600;
// const throttle = highland<() => void>();
// throttle.ratelimit(1, MIN_TIME_BETWEEN_REQUESTS).each((func) => {
// func();
// });
export async function _getThrottledSlot(): Promise<void> {
// return new Promise<void>((resolve) => {
// throttle.write(resolve);
// });
}
export async function getElevationForPoint(point: Point): Promise<number | undefined> {
const points = await getElevationForPoints([point]);
export async function getElevationForPoint(point: Point, failOnError = false): Promise<number | undefined> {
const points = await getElevationForPoints([point], failOnError);
return points[0];
}
export async function getElevationForPoints(points: Array<{ lat: string | number; lon: string | number }>): Promise<Array<number | undefined>> {
return points.map(() => undefined);
/*if(points.length == 0)
return Promise.resolve([ ]);
let ret = Promise.resolve([ ]);
for(let i=0; i<points.length; i+=LIMIT) {
ret = ret.then((heights) => {
return elevation._getThrottledSlot().then(() => (heights));
}).then((heights) => {
let json = {
encoded_polyline: polyline.encode(points.slice(i, i+LIMIT).map((point) => ([point.lat, point.lon])), 6),
range: false
};
return request.get({
url: `${API_URL}?json=${encodeURI(JSON.stringify(json))}&api_key=${config.mapzenToken}`,
json: true
}).then((res) => (heights.concat(res.height)));
});
export async function getElevationForPoints(points: Point[], failOnError = false): Promise<Array<number | undefined>> {
if(points.length == 0) {
return [];
}
try {
const res = await fetch(`${config.openElevationApiUrl}/api/v1/lookup`, {
method: "post",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify({
locations: points.map((point) => ({ latitude: point.lat, longitude: point.lon }))
})
});
if (!res.ok) {
throw new Error(`Looking up elevations failed with status ${res.status}.`);
}
const json: { results: Array<{ latitude: number; longitude: number; elevation: number }> } = await res.json();
return json.results.map((result: any) => {
if (result.elevation !== 0) {
return result.elevation;
}
});
} catch (err: any) {
if (failOnError) {
throw err;
} else {
console.warn("Error lookup up elevation", err);
return points.map(() => undefined);
}
}
return ret;*/
}
interface AscentDescent {

Wyświetl plik

@ -37,7 +37,6 @@ interface NominatimError {
error: { code?: number; message: string } | string;
}
const nameFinderUrl = "https://nominatim.openstreetmap.org";
const limit = 25;
const stateAbbr: Record<string, Record<string, string>> = {
"us" : {
@ -115,7 +114,7 @@ export async function find(query: string, loadUrls = false, loadElevation = fals
async function _findQuery(query: string, loadElevation = false): Promise<Array<SearchResult>> {
const body: Array<NominatimResult> | NominatimError = await throttledFetch(
nameFinderUrl + "/search?format=jsonv2&polygon_geojson=1&addressdetails=1&namedetails=1&limit=" + encodeURIComponent(limit) + "&extratags=1&q=" + encodeURIComponent(query),
config.nominatimUrl + "/search?format=jsonv2&polygon_geojson=1&addressdetails=1&namedetails=1&limit=" + encodeURIComponent(limit) + "&extratags=1&q=" + encodeURIComponent(query),
{
headers: {
"User-Agent": config.userAgent
@ -131,7 +130,7 @@ async function _findQuery(query: string, loadElevation = false): Promise<Array<S
const points = body.filter((res) => (res.lon && res.lat));
if(loadElevation && points.length > 0) {
const elevations = await getElevationForPoints(points);
const elevations = await getElevationForPoints(points.map((point) => ({ lat: Number(point.lat), lon: Number(point.lon) })));
elevations.forEach((elevation, i) => {
points[i].elevation = elevation;
});
@ -142,7 +141,7 @@ async function _findQuery(query: string, loadElevation = false): Promise<Array<S
async function _findOsmObject(type: string, id: string, loadElevation = false): Promise<Array<SearchResult>> {
const body: Array<NominatimResult> | NominatimError = await throttledFetch(
`${nameFinderUrl}/lookup?format=jsonv2&addressdetails=1&polygon_geojson=1&extratags=1&namedetails=1&osm_ids=${encodeURI(type.toUpperCase())}${encodeURI(id)}`,
`${config.nominatimUrl}/lookup?format=jsonv2&addressdetails=1&polygon_geojson=1&extratags=1&namedetails=1&osm_ids=${encodeURI(type.toUpperCase())}${encodeURI(id)}`,
{
headers: {
"User-Agent": config.userAgent
@ -158,7 +157,7 @@ async function _findOsmObject(type: string, id: string, loadElevation = false):
const points = body.filter((res) => (res.lon && res.lat));
if(loadElevation && points.length > 0) {
const elevations = await getElevationForPoints(points);
const elevations = await getElevationForPoints(points.map((point) => ({ lat: Number(point.lat), lon: Number(point.lon) })));
elevations.forEach((elevation, i) => {
points[i].elevation = elevation;
});
@ -170,7 +169,7 @@ async function _findOsmObject(type: string, id: string, loadElevation = false):
async function _findLonLat(lonlatWithZoom: PointWithZoom, loadElevation = false): Promise<Array<SearchResult>> {
const [body, elevation] = await Promise.all([
throttledFetch(
`${nameFinderUrl}/reverse?format=jsonv2&addressdetails=1&polygon_geojson=0&extratags=1&namedetails=1&lat=${encodeURIComponent(lonlatWithZoom.lat)}&lon=${encodeURIComponent(lonlatWithZoom.lon)}&zoom=${encodeURIComponent(lonlatWithZoom.zoom != null ? (lonlatWithZoom.zoom >= 12 ? lonlatWithZoom.zoom+2 : lonlatWithZoom.zoom) : 17)}`,
`${config.nominatimUrl}/reverse?format=jsonv2&addressdetails=1&polygon_geojson=0&extratags=1&namedetails=1&lat=${encodeURIComponent(lonlatWithZoom.lat)}&lon=${encodeURIComponent(lonlatWithZoom.lon)}&zoom=${encodeURIComponent(lonlatWithZoom.zoom != null ? (lonlatWithZoom.zoom >= 12 ? lonlatWithZoom.zoom+2 : lonlatWithZoom.zoom) : 17)}`,
{
headers: {
"User-Agent": config.userAgent

Wyświetl plik

@ -13,6 +13,6 @@ export const markerValidator = cruValidator({
colour: optionalCreate(colourValidator), // defaults to type.defaultColour
size: optionalCreate(sizeValidator), // defaults to type.defaultSize
data: optionalCreate(z.record(z.string())),
ele: optionalCreate(z.number().or(z.null()), null)
ele: optionalCreate(z.number().or(z.null()))
});
export type Marker<Mode extends CRU = CRU.READ> = CRUType<Mode, typeof markerValidator>;