You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
64 lines
2.1 KiB
64 lines
2.1 KiB
import { compact } from 'lodash';
|
|
import NodeGeocoder from 'node-geocoder';
|
|
import fetch from 'node-fetch';
|
|
import PQueue from 'p-queue';
|
|
import { mean } from '../../utils/math';
|
|
import type { GeocodingProvider } from './types';
|
|
|
|
export const createOsmClient = (): GeocodingProvider => {
|
|
const geocoder = NodeGeocoder({
|
|
provider: 'openstreetmap',
|
|
fetch: (url, options) => {
|
|
return fetch(url, {
|
|
...options,
|
|
headers: {
|
|
...options?.headers,
|
|
'user-agent':
|
|
'test-assignment-parcellab by Inga. This agent is only supposed to send a few requests in two weeks starting on October 23th 2023, and none after that',
|
|
},
|
|
});
|
|
},
|
|
});
|
|
|
|
// Rate limit according to https://operations.osmfoundation.org/policies/nominatim/
|
|
const queue = new PQueue({
|
|
interval: 1000,
|
|
intervalCap: 1,
|
|
timeout: 2000,
|
|
throwOnTimeout: true,
|
|
});
|
|
|
|
return {
|
|
geocode: async (query) =>
|
|
queue.add(async () => {
|
|
const result = await geocoder.geocode(query);
|
|
|
|
if (!result.length) {
|
|
throw new Error('No results found');
|
|
}
|
|
|
|
const meanLatitude = mean(
|
|
compact(result.map(({ latitude }) => latitude)),
|
|
);
|
|
const meanLongitude = mean(
|
|
compact(result.map(({ longitude }) => longitude)),
|
|
);
|
|
if (
|
|
!result.every(
|
|
({ latitude, longitude }) =>
|
|
latitude &&
|
|
longitude &&
|
|
Math.abs(latitude - meanLatitude) < 0.01 &&
|
|
Math.abs(longitude - meanLongitude) < 0.01,
|
|
)
|
|
) {
|
|
throw new Error('Ambiguous address');
|
|
}
|
|
|
|
return {
|
|
latitude: meanLatitude,
|
|
longitude: meanLongitude,
|
|
};
|
|
}),
|
|
};
|
|
};
|
|
|