Skip to content

Commit

Permalink
Allow specifying custom Open Elevation config
Browse files Browse the repository at this point in the history
  • Loading branch information
cdauth committed Mar 26, 2024
1 parent d20356f commit 13dd7f6
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 26 deletions.
2 changes: 2 additions & 0 deletions docs/src/developers/server/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ The config of the FacilMap server can be set either by using environment variabl
| `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). |
| `OPEN_ELEVATION_THROTTLE_MS` | | `1000` | The minimum time between two requests to the Open Elevation API. Set to `0` if you are using your own self-hosted instance of Open Elevation. |
| `OPEN_ELEVATION_MAX_BATCH_SIZE` | | `200` | The maximum number of points to resolve in one request through the Open Elevation API. Set this to `1000` if you are using your own self-hosted Open Elevation instance. |

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.
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/map/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ const config: InjectedConfig = JSON.parse(document.querySelector("meta[name=fmCo

setConfig({
nominatimUrl: config.nominatimUrl,
openElevationApiUrl: config.openElevationApiUrl
openElevationApiUrl: config.openElevationApiUrl,
openElevationThrottleMs: config.openElevationThrottleMs,
openElevationMaxBatchSize: config.openElevationMaxBatchSize
});

export default config;
7 changes: 7 additions & 0 deletions server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export interface Config {
customCssFile?: string;
nominatimUrl: string;
openElevationApiUrl: string;
openElevationThrottleMs: number;
openElevationMaxBatchSize: number;
}

const config: Config = {
Expand Down Expand Up @@ -66,11 +68,16 @@ const config: Config = {
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",
openElevationThrottleMs: process.env.OPEN_ELEVATION_THROTTLE_MS ? Number(process.env.OPEN_ELEVATION_THROTTLE_MS) : 1000, // Maximum one request per second, see https://github.com/Jorl17/open-elevation/issues/3
openElevationMaxBatchSize: process.env.OPEN_ELEVATION_MAX_BATCH_SIZE ? Number(process.env.OPEN_ELEVATION_MAX_BATCH_SIZE) : 200,
};

setConfig({
openElevationApiUrl: config.openElevationApiUrl,
openElevationThrottleMs: config.openElevationThrottleMs,
openElevationMaxBatchSize: config.openElevationMaxBatchSize,
nominatimUrl: config.nominatimUrl
});

Expand Down
24 changes: 14 additions & 10 deletions server/src/database/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export default class DatabaseMigrations {
await this._changeColMigrations();
await this._addColMigrations();
await this._dropdownKeyMigration();
await this._elevationMigration();
await this._legendMigration();
await this._bboxMigration();
await this._spatialMigration();
Expand All @@ -32,10 +31,15 @@ export default class DatabaseMigrations {
await this._typesIdxMigration();
await this._viewsIdxMigration();

console.log("DB migration: All migrations finished");
(async () => {
await this._elevationMigration();
})().catch((err) => {
console.error("DB migration: Unexpected error in background migration", err);
}).finally(() => {
console.log("DB migration: All migrations finished");
});
}


/** Run any migrations that rename columns */
async _renameColMigrations(): Promise<void> {
const queryInterface = this._db._conn.getQueryInterface();
Expand Down Expand Up @@ -376,13 +380,13 @@ export default class DatabaseMigrations {

/* Get elevation data for all lines/markers that don't have any yet */
async _elevationMigration(): Promise<void> {
const hasElevation = await this._db.meta.getMeta("hasElevation");
if(hasElevation == "2")
return;
try {
const hasElevation = await this._db.meta.getMeta("hasElevation");
if(hasElevation == "2")
return;

console.log("DB migration: Get marker elevations in background");
console.log("DB migration: Get marker elevations in background");

(async () => {
const markers = await this._db.markers.MarkerModel.findAll({ where: { ele: null } });

let anyError = false;
Expand Down Expand Up @@ -417,9 +421,9 @@ export default class DatabaseMigrations {
console.log("DB migration: Elevation migration completed");
await this._db.meta.setMeta("hasElevation", "2");
}
})().catch((err) => {
} catch (err: any) {
console.error("DB migration: Elevation migration crashed", err);
});
}
}


Expand Down
2 changes: 2 additions & 0 deletions server/src/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ function getInjectedConfig(): InjectedConfig {
return {
appName: config.appName,
openElevationApiUrl: config.openElevationApiUrl,
openElevationThrottleMs: config.openElevationThrottleMs,
openElevationMaxBatchSize: config.openElevationMaxBatchSize,
nominatimUrl: config.nominatimUrl,
limaLabsToken: config.limaLabsToken,
hideCommercialMapLinks: config.hideCommercialMapLinks,
Expand Down
2 changes: 2 additions & 0 deletions utils/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export interface Config {
openElevationApiUrl: string;
openElevationThrottleMs: number;
openElevationMaxBatchSize: number;
nominatimUrl: string;
}

Expand Down
28 changes: 13 additions & 15 deletions utils/src/elevation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import type { Point } from "facilmap-types";
import { RetryError, throttledBatch } from "./utils";
import { fetchAdapter, getConfig } from "./config";

const MIN_DELAY_MS = 1000; // Maximum one request per second, see https://github.com/Jorl17/open-elevation/issues/3
const MAX_DELAY_MS = 60_000;
const MAX_BATCH_SIZE = 200;

let delayMs = MIN_DELAY_MS;
let maxBatchSize = MAX_BATCH_SIZE;
let lastFailed = false;
let retryCount = 0;
let delayMs = () => Math.min(MAX_DELAY_MS, getConfig().openElevationThrottleMs * (2 ** retryCount));
let maxBatchSize = () => Math.max(1, Math.floor(getConfig().openElevationMaxBatchSize / (2 ** retryCount)));
export const getElevationForPoint = throttledBatch<[Point], number | undefined>(async (args) => {
const res = await fetchAdapter(`${getConfig().openElevationApiUrl}/api/v1/lookup`, {
method: "post",
Expand All @@ -24,23 +22,18 @@ export const getElevationForPoint = throttledBatch<[Point], number | undefined>(
if (res.status === 504) {
// Probably caused by an overload on the server. Usually it goes away after a while. Let's exponentially increase delays
// between requests until it succeeds again.
delayMs = Math.min(MAX_DELAY_MS, delayMs * 2);
maxBatchSize = Math.max(1, Math.floor(maxBatchSize / 2));
lastFailed = true;

console.warn(`Looking up elevations failed with status ${res.status}, retrying (delay ${delayMs/1000}s, batch size ${maxBatchSize}).`);
retryCount++;
console.warn(`Looking up elevations failed with status ${res.status}, retrying (delay ${delayMs()/1000}s, batch size ${maxBatchSize()}).`);
throw new RetryError(error);
} else {
throw error;
}
}

if (lastFailed) {
if (retryCount > 0) {
console.log(`Looking up elevations retry succeeded.`);
retryCount = 0;
}
delayMs = MIN_DELAY_MS;
maxBatchSize = MAX_BATCH_SIZE;
lastFailed = false;

const json: { results: Array<{ latitude: number; longitude: number; elevation: number }> } = await res.json();

Expand All @@ -49,7 +42,12 @@ export const getElevationForPoint = throttledBatch<[Point], number | undefined>(
return result.elevation;
}
});
}, { delayMs: () => delayMs, maxSize: () => maxBatchSize, maxRetries: Infinity, noParallel: true });
}, {
delayMs,
maxSize: maxBatchSize,
maxRetries: Infinity,
noParallel: true
});

interface AscentDescent {
ascent: number | undefined;
Expand Down
2 changes: 2 additions & 0 deletions utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export function isPromise(object: any): object is Promise<unknown> {
export interface InjectedConfig {
appName: string;
openElevationApiUrl: string;
openElevationThrottleMs: number;
openElevationMaxBatchSize: number;
nominatimUrl: string;
limaLabsToken?: string;
hideCommercialMapLinks?: boolean;
Expand Down

0 comments on commit 13dd7f6

Please sign in to comment.