UNPKG

universal-geocoder

Version:

Universal geocoding abstraction server-side and client-side with multiple built-in providers

262 lines (237 loc) 7.8 kB
import { ExternalLoaderBody, ExternalLoaderHeaders, ExternalLoaderInterface, ExternalLoaderParams, } from "ExternalLoader"; import { ErrorCallback, GeocodedResultsCallback, GeoPluginGeocoded, ProviderHelpers, ProviderInterface, ProviderOptionsInterface, defaultProviderOptions, } from "provider"; import { GeocodeQuery, GeocodeQueryObject, ReverseQuery, ReverseQueryObject, } from "query"; import { ResponseError } from "error"; import AdminLevel, { ADMIN_LEVEL_CODES } from "AdminLevel"; interface GeoPluginRequestParams { [param: string]: string | undefined; readonly ip: string; } export interface GeoPluginResult { // eslint-disable-next-line camelcase geoplugin_request: string; // eslint-disable-next-line camelcase geoplugin_status: number; // eslint-disable-next-line camelcase geoplugin_delay: string; // eslint-disable-next-line camelcase geoplugin_credit: string; // eslint-disable-next-line camelcase geoplugin_city: string; // eslint-disable-next-line camelcase geoplugin_region: string; // eslint-disable-next-line camelcase geoplugin_regionCode: string; // eslint-disable-next-line camelcase geoplugin_regionName: string; // eslint-disable-next-line camelcase geoplugin_areaCode: string; // eslint-disable-next-line camelcase geoplugin_dmaCode: string; // eslint-disable-next-line camelcase geoplugin_countryCode: string; // eslint-disable-next-line camelcase geoplugin_countryName: string; // eslint-disable-next-line camelcase geoplugin_inEU: boolean; // eslint-disable-next-line camelcase geoplugin_euVATrate: number; // eslint-disable-next-line camelcase geoplugin_continentCode: string; // eslint-disable-next-line camelcase geoplugin_continentName: string; // eslint-disable-next-line camelcase geoplugin_latitude: string; // eslint-disable-next-line camelcase geoplugin_longitude: string; // eslint-disable-next-line camelcase geoplugin_locationAccuracyRadius: string; // eslint-disable-next-line camelcase geoplugin_timezone: string; // eslint-disable-next-line camelcase geoplugin_currencyCode: string; // eslint-disable-next-line camelcase geoplugin_currencySymbol: string; // eslint-disable-next-line camelcase geoplugin_currencySymbol_UTF8: string; // eslint-disable-next-line camelcase geoplugin_currencyConverter: string; } type GeoPluginGeocodedResultsCallback = GeocodedResultsCallback<GeoPluginGeocoded>; export default class GeoPluginProvider implements ProviderInterface<GeoPluginGeocoded> { private externalLoader: ExternalLoaderInterface; private options: ProviderOptionsInterface; public constructor( _externalLoader: ExternalLoaderInterface, options: ProviderOptionsInterface = defaultProviderOptions ) { this.externalLoader = _externalLoader; this.options = { ...defaultProviderOptions, ...options }; } public geocode( query: string | GeocodeQuery | GeocodeQueryObject ): Promise<GeoPluginGeocoded[]>; public geocode( query: string | GeocodeQuery | GeocodeQueryObject, callback: GeoPluginGeocodedResultsCallback, errorCallback?: ErrorCallback ): void; public geocode( query: string | GeocodeQuery | GeocodeQueryObject, callback?: GeoPluginGeocodedResultsCallback, errorCallback?: ErrorCallback ): void | Promise<GeoPluginGeocoded[]> { const geocodeQuery = ProviderHelpers.getGeocodeQueryFromParameter(query); if (geocodeQuery.getText()) { throw new Error( "The GeoPlugin provider does not support location geocoding, only IP geolocation." ); } if (["127.0.0.1", "::1"].includes(geocodeQuery.getIp() || "")) { const geocoded = GeoPluginGeocoded.create({ locality: "localhost", country: "localhost", }); if (!callback) { return new Promise((resolve) => resolve([geocoded])); } return callback([geocoded]); } this.externalLoader.setOptions({ protocol: this.options.useSsl ? "https" : "http", host: "www.geoplugin.net", pathname: "json.gp", }); const params: GeoPluginRequestParams = { ip: geocodeQuery.getIp() || "", }; if (!callback) { return new Promise((resolve, reject) => this.executeRequest( params, (results) => resolve(results), {}, {}, (error) => reject(error) ) ); } return this.executeRequest(params, callback, {}, {}, errorCallback); } public geodecode( query: ReverseQuery | ReverseQueryObject ): Promise<GeoPluginGeocoded[]>; public geodecode( query: ReverseQuery | ReverseQueryObject, callback: GeoPluginGeocodedResultsCallback, errorCallback?: ErrorCallback ): void; public geodecode( latitude: number | string, longitude: number | string ): Promise<GeoPluginGeocoded[]>; public geodecode( latitude: number | string, longitude: number | string, callback: GeoPluginGeocodedResultsCallback, errorCallback?: ErrorCallback ): void; // eslint-disable-next-line class-methods-use-this public geodecode( // eslint-disable-next-line @typescript-eslint/no-unused-vars latitudeOrQuery: number | string | ReverseQuery | ReverseQueryObject, // eslint-disable-next-line @typescript-eslint/no-unused-vars longitudeOrCallback?: number | string | GeoPluginGeocodedResultsCallback, // eslint-disable-next-line @typescript-eslint/no-unused-vars callbackOrErrorCallback?: GeoPluginGeocodedResultsCallback | ErrorCallback, // eslint-disable-next-line @typescript-eslint/no-unused-vars errorCallback?: ErrorCallback ): void | Promise<GeoPluginGeocoded[]> { throw new Error( "The GeoPlugin provider does not support reverse geocoding." ); } public executeRequest( params: ExternalLoaderParams, callback: GeoPluginGeocodedResultsCallback, headers?: ExternalLoaderHeaders, body?: ExternalLoaderBody, errorCallback?: ErrorCallback ): void { this.externalLoader.executeRequest( params, (data: GeoPluginResult) => { if (![200, 206].includes(data.geoplugin_status)) { const errorMessage = `An error has occurred. Status: ${data.geoplugin_status}.`; if (errorCallback) { errorCallback(new ResponseError(errorMessage, data)); return; } setTimeout(() => { throw new Error(errorMessage); }); return; } callback([GeoPluginProvider.mapToGeocoded(data)]); }, headers, body, errorCallback ); } public static mapToGeocoded(result: GeoPluginResult): GeoPluginGeocoded { const latitude = parseFloat(result.geoplugin_latitude); const longitude = parseFloat(result.geoplugin_longitude); const locality = result.geoplugin_city || undefined; const region = result.geoplugin_region || undefined; const country = result.geoplugin_countryName || undefined; const countryCode = result.geoplugin_countryCode || undefined; const timezone = result.geoplugin_timezone || undefined; const adminLevels: AdminLevel[] = []; const attribution = result.geoplugin_credit || undefined; if (result.geoplugin_regionName) { adminLevels.push( AdminLevel.create({ level: ADMIN_LEVEL_CODES.STATE_CODE, name: result.geoplugin_regionName, code: result.geoplugin_regionCode || undefined, }) ); } const geocoded = GeoPluginGeocoded.create({ coordinates: { latitude, longitude, }, locality, region, adminLevels, country, countryCode, timezone, attribution, }); return geocoded; } }