UNPKG

leaflet-control-geocoder

Version:

Extendable geocoder with builtin support for OpenStreetMap Nominatim, Bing, Google, Mapbox, MapQuest, What3Words, Photon, Pelias, HERE, Neutrino, Plus codes

264 lines (235 loc) 6.73 kB
import * as L from 'leaflet'; import { getJSON } from '../util'; import { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api'; export interface HereOptions extends GeocoderOptions { /** * Use `apiKey` and the new `HEREv2` geocoder * @deprecated */ app_id: string; /** * Use `apiKey` and the new `HEREv2` geocoder * @deprecated */ app_code: string; reverseGeocodeProxRadius?: any; apiKey: string; maxResults: number; } /** * Implementation of the [HERE Geocoder API](https://developer.here.com/documentation/geocoder/topics/introduction.html) */ export class HERE implements IGeocoder { options: HereOptions = { serviceUrl: 'https://geocoder.api.here.com/6.2/', app_id: '', app_code: '', apiKey: '', maxResults: 5 }; constructor(options?: Partial<HereOptions>) { L.Util.setOptions(this, options); if (options?.apiKey) throw Error('apiKey is not supported, use app_id/app_code instead!'); } geocode(query: string): Promise<GeocodingResult[]> { const params = geocodingParams(this.options, { searchtext: query, gen: 9, app_id: this.options.app_id, app_code: this.options.app_code, jsonattributes: 1, maxresults: this.options.maxResults }); return this.getJSON(this.options.serviceUrl + 'geocode.json', params); } reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]> { let prox = location.lat + ',' + location.lng; if (this.options.reverseGeocodeProxRadius) { prox += ',' + this.options.reverseGeocodeProxRadius; } const params = reverseParams(this.options, { prox, mode: 'retrieveAddresses', app_id: this.options.app_id, app_code: this.options.app_code, gen: 9, jsonattributes: 1, maxresults: this.options.maxResults }); return this.getJSON(this.options.serviceUrl + 'reversegeocode.json', params); } async getJSON(url: string, params: any): Promise<GeocodingResult[]> { const data = await getJSON<any>(url, params); const results: GeocodingResult[] = []; if (data.response.view && data.response.view.length) { for (let i = 0; i <= data.response.view[0].result.length - 1; i++) { const loc = data.response.view[0].result[i].location; const center = L.latLng(loc.displayPosition.latitude, loc.displayPosition.longitude); const bbox = L.latLngBounds( L.latLng(loc.mapView.topLeft.latitude, loc.mapView.topLeft.longitude), L.latLng(loc.mapView.bottomRight.latitude, loc.mapView.bottomRight.longitude) ); results[i] = { name: loc.address.label, properties: loc.address, bbox: bbox, center: center }; } } return results; } } /** * Implementation of the new [HERE Geocoder API](https://developer.here.com/documentation/geocoding-search-api/api-reference-swagger.html) */ export class HEREv2 implements IGeocoder { options: HereOptions = { serviceUrl: 'https://geocode.search.hereapi.com/v1', apiKey: '', app_id: '', app_code: '', maxResults: 10 }; constructor(options?: Partial<HereOptions>) { L.Util.setOptions(this, options); } geocode(query: string): Promise<GeocodingResult[]> { const params = geocodingParams(this.options, { q: query, apiKey: this.options.apiKey, limit: this.options.maxResults }); if (!params.at && !params.in) { throw Error( 'at / in parameters not found. Please define coordinates (at=latitude,longitude) or other (in) in your geocodingQueryParams.' ); } return this.getJSON(this.options.serviceUrl + '/discover', params); } reverse(location: L.LatLngLiteral, scale: number): Promise<GeocodingResult[]> { const params = reverseParams(this.options, { at: location.lat + ',' + location.lng, limit: this.options.reverseGeocodeProxRadius, apiKey: this.options.apiKey }); return this.getJSON(this.options.serviceUrl + '/revgeocode', params); } async getJSON(url: string, params: any): Promise<GeocodingResult[]> { const data = await getJSON<HEREv2Response>(url, params); const results: GeocodingResult[] = []; if (data.items && data.items.length) { for (let i = 0; i <= data.items.length - 1; i++) { const item = data.items[i]; const latLng = L.latLng(item.position.lat, item.position.lng); let bbox: L.LatLngBounds; if (item.mapView) { bbox = L.latLngBounds( L.latLng(item.mapView.south, item.mapView.west), L.latLng(item.mapView.north, item.mapView.east) ); } else { // Using only position when not provided bbox = L.latLngBounds( L.latLng(item.position.lat, item.position.lng), L.latLng(item.position.lat, item.position.lng) ); } results[i] = { name: item.address.label, properties: item.address, bbox: bbox, center: latLng }; } } return results; } } /** * [Class factory method](https://leafletjs.com/reference.html#class-class-factories) for {@link HERE} * @param options the options */ export function here(options?: Partial<HereOptions>) { if (options?.apiKey) { return new HEREv2(options); } else { return new HERE(options); } } /** * @internal */ export interface HEREv2Response { items: Item[]; } interface Item { title: string; id: string; ontologyId: string; resultType: string; address: Address; mapView?: MapView; position: Position; access: Position[]; distance: number; categories: Category[]; references: Reference[]; foodTypes: Category[]; contacts: Contact[]; openingHours: OpeningHour[]; } interface MapView { east: number; north: number; south: number; west: number; } interface Position { lat: number; lng: number; } interface Address { label: string; countryCode: string; countryName: string; stateCode: string; state: string; county: string; city: string; district: string; street: string; postalCode: string; houseNumber: string; } interface Category { id: string; name: string; primary?: boolean; } interface Contact { phone: Email[]; fax: Email[]; www: Email[]; email: Email[]; } interface Email { value: string; } interface OpeningHour { text: string[]; isOpen: boolean; structured: Structured[]; } interface Structured { start: string; duration: string; recurrence: string; } interface Reference { supplier: Supplier; id: string; } interface Supplier { id: string; }