From 2232046dc1d77e1a538fed81f819812bae45161f Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sun, 15 Dec 2024 16:11:08 +0100 Subject: [PATCH] Add response types to geocoders --- spec/arcgis.spec.ts | 4 +- spec/google.spec.ts | 4 +- spec/here.spec.ts | 4 +- spec/mapbox.spec.ts | 4 +- spec/nominatim.spec.ts | 6 +-- spec/pelias.spec.ts | 4 +- spec/photon.spec.ts | 4 +- src/geocoders/arcgis.ts | 37 +++++++++++++++- src/geocoders/google.ts | 41 ++++++++++++++++- src/geocoders/here.ts | 90 +++++++++++++++++++++++++++++++++++++- src/geocoders/mapbox.ts | 55 ++++++++++++++++++++--- src/geocoders/nominatim.ts | 8 ++-- src/geocoders/pelias.ts | 44 +++++++++++++++++++ src/geocoders/photon.ts | 23 ++++++++++ 14 files changed, 301 insertions(+), 27 deletions(-) diff --git a/spec/arcgis.spec.ts b/spec/arcgis.spec.ts index b6fa854..4fc373b 100644 --- a/spec/arcgis.spec.ts +++ b/spec/arcgis.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { mockFetchRequest } from './mockFetchRequest'; -import { ArcGis } from '../src/geocoders/arcgis'; +import { ArcGis, ArcGisResponse } from '../src/geocoders/arcgis'; describe('L.Control.Geocoder.ArcGis', () => { afterEach(() => vi.clearAllMocks()); @@ -24,7 +24,7 @@ describe('L.Control.Geocoder.ArcGis', () => { } } ] - }, + } satisfies ArcGisResponse, () => geocoder.geocode('Innsbruck') ); diff --git a/spec/google.spec.ts b/spec/google.spec.ts index 82a098c..0654564 100644 --- a/spec/google.spec.ts +++ b/spec/google.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { mockFetchRequest } from './mockFetchRequest'; -import { Google } from '../src/geocoders/google'; +import { Google, GoogleResponse } from '../src/geocoders/google'; import { GeocodingResult } from '../src/geocoders/api'; describe('L.Control.Geocoder.Google', () => { @@ -67,7 +67,7 @@ describe('L.Control.Geocoder.Google', () => { } ], status: 'OK' - }, + } satisfies GoogleResponse, () => geocoder.geocode('Innsbruck') ); diff --git a/spec/here.spec.ts b/spec/here.spec.ts index 55b7c65..d765ed7 100644 --- a/spec/here.spec.ts +++ b/spec/here.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { mockFetchRequest } from './mockFetchRequest'; -import { HERE } from '../src/geocoders/here'; +import { HERE, HEREv2Response } from '../src/geocoders/here'; import { HEREv2 } from '../src/geocoders/here'; import { GeocodingResult } from '../src/geocoders/api'; @@ -162,7 +162,7 @@ describe('L.Control.Geocoder.HEREv2', () => { ] } ] - }, + } satisfies HEREv2Response, () => geocoder.geocode('Innsbruck') ); diff --git a/spec/mapbox.spec.ts b/spec/mapbox.spec.ts index d9901bb..43dbf0d 100644 --- a/spec/mapbox.spec.ts +++ b/spec/mapbox.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { mockFetchRequest } from './mockFetchRequest'; -import { Mapbox } from '../src/geocoders/mapbox'; +import { Mapbox, MapboxResponse } from '../src/geocoders/mapbox'; describe('L.Control.Geocoder.Mapbox', () => { afterEach(() => vi.clearAllMocks()); @@ -62,7 +62,7 @@ describe('L.Control.Geocoder.Mapbox', () => { ], attribution: 'NOTICE: © 2018 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained. POI(s) provided by Foursquare.' - }, + } satisfies MapboxResponse, () => geocoder.geocode('Milwaukee Ave') ); diff --git a/spec/nominatim.spec.ts b/spec/nominatim.spec.ts index 4844e8b..4de236e 100644 --- a/spec/nominatim.spec.ts +++ b/spec/nominatim.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { mockFetchRequest } from './mockFetchRequest'; -import { Nominatim } from '../src/geocoders/nominatim'; +import { Nominatim, NominatimResponse } from '../src/geocoders/nominatim'; describe('L.Control.Geocoder.Nominatim', () => { afterEach(() => vi.clearAllMocks()); @@ -32,7 +32,7 @@ describe('L.Control.Geocoder.Nominatim', () => { country_code: 'at' } } - ], + ] satisfies NominatimResponse, () => geocoder.geocode('innsbruck') ); @@ -70,7 +70,7 @@ describe('L.Control.Geocoder.Nominatim', () => { country_code: 'at' }, boundingbox: ['46.9624854', '47.4499229', '10.9896868', '11.7051742'] - }, + } satisfies NominatimResponse[number], () => geocoder.reverse({ lat: 47.3, lng: 11.3 }, 131000) ); diff --git a/spec/pelias.spec.ts b/spec/pelias.spec.ts index 00008e8..7d119b3 100644 --- a/spec/pelias.spec.ts +++ b/spec/pelias.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { mockFetchRequest } from './mockFetchRequest'; -import { Openrouteservice } from '../src/geocoders/pelias'; +import { Openrouteservice, PeliasResponse } from '../src/geocoders/pelias'; describe('L.Control.Geocoder.Openrouteservice', () => { afterEach(() => vi.clearAllMocks()); @@ -45,7 +45,7 @@ describe('L.Control.Geocoder.Openrouteservice', () => { } ], bbox: [10.9896885523, 46.9624806033, 11.7051690163, 47.4499185397] - }, + } satisfies PeliasResponse, () => geocoder.geocode('innsbruck') ); diff --git a/spec/photon.spec.ts b/spec/photon.spec.ts index 54a9725..3b14327 100644 --- a/spec/photon.spec.ts +++ b/spec/photon.spec.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { mockFetchRequest } from './mockFetchRequest'; -import { Photon } from '../src/geocoders/photon'; +import { Photon, PhotonResponse } from '../src/geocoders/photon'; import { GeocodingResult } from '../src/geocoders/api'; describe('L.Control.Geocoder.Photon', () => { @@ -50,7 +50,7 @@ describe('L.Control.Geocoder.Photon', () => { } ], type: 'FeatureCollection' - }, + } satisfies PhotonResponse, () => geocoder.geocode('Innsbruck') ); diff --git a/src/geocoders/arcgis.ts b/src/geocoders/arcgis.ts index 1dd0a9b..8e41744 100644 --- a/src/geocoders/arcgis.ts +++ b/src/geocoders/arcgis.ts @@ -4,6 +4,8 @@ import { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reversePa export interface ArcGisOptions extends GeocoderOptions {} + + /** * Implementation of the [ArcGIS geocoder](https://developers.arcgis.com/features/geocoding/) */ @@ -27,7 +29,10 @@ export class ArcGis implements IGeocoder { f: 'json' }); - const data = await getJSON(this.options.serviceUrl + '/findAddressCandidates', params); + const data = await getJSON( + this.options.serviceUrl + '/findAddressCandidates', + params + ); const results: GeocodingResult[] = []; if (data.candidates && data.candidates.length) { for (let i = 0; i <= data.candidates.length - 1; i++) { @@ -81,3 +86,33 @@ export class ArcGis implements IGeocoder { export function arcgis(options?: Partial) { return new ArcGis(options); } + +/** + * @internal + */ +export interface ArcGisResponse { + spatialReference: { + wkid: number; + latestWkid: number; + }; + candidates: Candidate[]; +} + +interface Candidate { + address: string; + location: { + x: number; + y: number; + }; + score: number; + attributes: { + Addr_Type: string; + }; + extent: { + xmin: number; + ymin: number; + xmax: number; + ymax: number; + }; +} + diff --git a/src/geocoders/google.ts b/src/geocoders/google.ts index 59986d2..85e633b 100644 --- a/src/geocoders/google.ts +++ b/src/geocoders/google.ts @@ -21,7 +21,7 @@ export class Google implements IGeocoder { key: this.options.apiKey, address: query }); - const data = await getJSON(this.options.serviceUrl, params); + const data = await getJSON(this.options.serviceUrl, params); const results: GeocodingResult[] = []; if (data.results && data.results.length) { for (let i = 0; i <= data.results.length - 1; i++) { @@ -78,3 +78,42 @@ export class Google implements IGeocoder { export function google(options?: Partial) { return new Google(options); } + +/** + * @internal + */ +export interface GoogleResponse { + results: Result[]; + status: string; +} + +interface Result { + address_components: AddressComponent[]; + formatted_address: string; + geometry: Geometry; + place_id: string; + types: string[]; +} + +interface AddressComponent { + long_name: string; + short_name: string; + types: string[]; +} + +interface Geometry { + bounds: Bounds; + location: Location; + location_type: string; + viewport: Bounds; +} + +interface Bounds { + northeast: Location; + southwest: Location; +} + +interface Location { + lat: number; + lng: number; +} diff --git a/src/geocoders/here.ts b/src/geocoders/here.ts index 72356c7..17db95f 100755 --- a/src/geocoders/here.ts +++ b/src/geocoders/here.ts @@ -130,7 +130,7 @@ export class HEREv2 implements IGeocoder { } async getJSON(url: string, params: any): Promise { - const data = await getJSON(url, params); + const data = await getJSON(url, params); const results: GeocodingResult[] = []; if (data.items && data.items.length) { @@ -173,3 +173,91 @@ export function here(options?: Partial) { 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; +} diff --git a/src/geocoders/mapbox.ts b/src/geocoders/mapbox.ts index 0d56ff6..6705a59 100644 --- a/src/geocoders/mapbox.ts +++ b/src/geocoders/mapbox.ts @@ -46,7 +46,7 @@ export class Mapbox implements IGeocoder { ) { params.proximity = params.proximity.lng + ',' + params.proximity.lat; } - const data = await getJSON(url, params); + const data = await getJSON(url, params); return this._parseResults(data); } @@ -59,23 +59,23 @@ export class Mapbox implements IGeocoder { const param = reverseParams(this.options, { access_token: this.options.apiKey }); - const data = await getJSON(url, param); + const data = await getJSON(url, param); return this._parseResults(data); } - private _parseResults(data): any[] | GeocodingResult[] { + private _parseResults(data: MapboxResponse): any[] | GeocodingResult[] { if (!data.features?.length) { return []; } const results: GeocodingResult[] = []; for (let i = 0; i <= data.features.length - 1; i++) { const loc = data.features[i]; - const center = L.latLng(loc.center.reverse()); + const center = L.latLng(loc.center.reverse() as [number, number]); let bbox: L.LatLngBounds; if (loc.bbox) { bbox = L.latLngBounds( - L.latLng(loc.bbox.slice(0, 2).reverse()), - L.latLng(loc.bbox.slice(2, 4).reverse()) + L.latLng(loc.bbox.slice(0, 2).reverse() as [number, number]), + L.latLng(loc.bbox.slice(2, 4).reverse() as [number, number]) ); } else { bbox = L.latLngBounds(center, center); @@ -99,3 +99,46 @@ export class Mapbox implements IGeocoder { export function mapbox(options?: Partial) { return new Mapbox(options); } + +/** + * @internal + */ +export interface MapboxResponse { + type: string; + query: string[]; + features: Feature[]; + attribution: string; +} + +interface Feature { + id: string; + type: string; + place_type: string[]; + relevance: number; + properties: Properties; + text: string; + place_name: string; + matching_text: string; + matching_place_name: string; + center: [number, number]; + bbox?: [number, number, number, number]; + geometry: Geometry; + address: string; + context: Context[]; +} + +interface Context { + id: string; + text: string; + wikidata?: string; + short_code?: string; +} + +interface Geometry { + type: string; + coordinates: number[]; + interpolated: boolean; + omitted: boolean; +} + +interface Properties {} diff --git a/src/geocoders/nominatim.ts b/src/geocoders/nominatim.ts index bf0b4e7..943de12 100644 --- a/src/geocoders/nominatim.ts +++ b/src/geocoders/nominatim.ts @@ -2,6 +2,8 @@ import * as L from 'leaflet'; import { template, getJSON } from '../util'; import { IGeocoder, GeocoderOptions, geocodingParams, GeocodingResult, reverseParams } from './api'; +export type NominatimResponse = NominatimResult[]; + export interface NominatimResult { place_id: number; licence: string; @@ -11,9 +13,9 @@ export interface NominatimResult { lat: string; lon: string; display_name: string; - class: string; - type: string; - importance: number; + class?: string; + type?: string; + importance?: number; icon?: string; address: NominatimAddress; } diff --git a/src/geocoders/pelias.ts b/src/geocoders/pelias.ts index cd466c1..7e73554 100644 --- a/src/geocoders/pelias.ts +++ b/src/geocoders/pelias.ts @@ -125,3 +125,47 @@ export class Openrouteservice extends Pelias { export function openrouteservice(options?: Partial) { return new Openrouteservice(options); } + +/** + * @internal + */ +export type PeliasResponse = GeoJSON.FeatureCollection & { + geocoding: Geocoding; +}; + +interface Properties { + id: string; + layer: string; + source_id: string; + name: string; + confidence: number; + match_type: string; + accuracy: string; + country: string; + country_a: string; + region: string; + region_a: string; + county: string; + county_a: string; + localadmin: string; + locality: string; + continent: string; + label: string; +} + +interface Geocoding { + version: string; + attribution: string; + query: Query; + warnings: string[]; + engine: Engine; +} + +interface Engine { + name: string; + author: string; + version: string; +} + +interface Query { +} diff --git a/src/geocoders/photon.ts b/src/geocoders/photon.ts index 4070d99..cacdcdd 100644 --- a/src/geocoders/photon.ts +++ b/src/geocoders/photon.ts @@ -83,3 +83,26 @@ export class Photon implements IGeocoder { export function photon(options?: Partial) { return new Photon(options); } + +/** + * @internal + */ +export type PhotonResponse = GeoJSON.FeatureCollection; + +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; +}