kopia lustrzana https://github.com/FacilMap/facilmap
Use Open Elevation instead of MapZen to retrieve point elevation (#250)
rodzic
db8d7d6080
commit
bc4d97d998
|
@ -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
|
||||
|
|
|
@ -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 FacilMap’s 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
|
||||
|
|
|
@ -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
|
||||
```
|
|
@ -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}} m</dd>
|
||||
</template>
|
||||
|
||||
<template v-for="field in client.types[marker.typeId].fields" :key="field.name">
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = [ ];
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
|
|
Ładowanie…
Reference in New Issue