leaflet-control-geocoder
Version:
Extendable geocoder with builtin support for OpenStreetMap Nominatim, Bing, Google, Mapbox, MapQuest, What3Words, Photon, Pelias, HERE, Neutrino, Plus codes
118 lines (104 loc) • 3.17 kB
text/typescript
import * as L from 'leaflet';
import { getJSON } from '../util';
import {
GeocoderOptions,
GeocodingContext,
geocodingParams,
GeocodingResult,
IGeocoder,
reverseParams
} from './api';
export interface PhotonOptions extends GeocoderOptions {
reverseUrl: string;
nameProperties: string[];
htmlTemplate?: (r: any) => string;
}
/**
* Implementation of the [Photon](http://photon.komoot.io/) geocoder
*/
export class Photon implements IGeocoder {
options: PhotonOptions = {
serviceUrl: 'https://photon.komoot.io/api/',
reverseUrl: 'https://photon.komoot.io/reverse/',
nameProperties: ['name', 'street', 'suburb', 'hamlet', 'town', 'city', 'state', 'country']
};
constructor(options?: Partial<PhotonOptions>) {
L.Util.setOptions(this, options);
}
async geocode(query: string, context?: GeocodingContext): Promise<GeocodingResult[]> {
const params = geocodingParams(this.options, { q: query });
const center = context?.map?.getCenter?.();
if (center) {
params.lat = center.lat;
params.lon = center.lng;
}
const zoom = context?.map?.getZoom?.();
if (zoom) {
params.zoom = zoom;
}
const data = await getJSON<any>(this.options.serviceUrl, params);
return this._parseResults(data);
}
suggest(query: string): Promise<GeocodingResult[]> {
return this.geocode(query);
}
async reverse(latLng: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]> {
const params = reverseParams(this.options, {
lat: latLng.lat,
lon: latLng.lng
});
const data = await getJSON<any>(this.options.reverseUrl, params);
return this._parseResults(data);
}
_parseResults(data: GeoJSON.FeatureCollection<GeoJSON.Point>): GeocodingResult[] {
return (data.features || []).map((f): GeocodingResult => {
const c = f.geometry.coordinates;
const center = new L.LatLng(c[1], c[0]);
const extent = f.properties?.extent;
const bbox = extent
? new L.LatLngBounds([extent[1], extent[0]], [extent[3], extent[2]])
: new L.LatLngBounds(center, center);
return {
name: this._decodeFeatureName(f),
html: this.options.htmlTemplate ? this.options.htmlTemplate(f) : undefined,
center,
bbox,
properties: f.properties
};
});
}
_decodeFeatureName(f: GeoJSON.Feature) {
return (this.options.nameProperties || [])
.map(p => f.properties?.[p])
.filter(v => !!v)
.join(', ');
}
}
/**
* [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link Photon}
* @param options the options
*/
export function photon(options?: Partial<PhotonOptions>) {
return new Photon(options);
}
/**
* @internal
*/
export type PhotonResponse = GeoJSON.FeatureCollection<GeoJSON.Geometry, PhotonProperties>;
interface PhotonProperties {
osm_id: number;
osm_type: string;
extent?: number[];
country: string;
osm_key: string;
city: string;
countrycode: string;
osm_value: string;
name: string;
state: string;
type: string;
postcode?: string;
housenumber?: string;
street?: string;
district?: string;
}