universal-geocoder
Version:
Universal geocoding abstraction server-side and client-side with multiple built-in providers
376 lines • 17.8 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var provider_1 = require("./..");
var AdminLevel_1 = __importDefault(require("../../AdminLevel"));
var error_1 = require("../../error");
var utils_1 = require("../../utils");
var GoogleMapsProvider = /** @class */ (function () {
function GoogleMapsProvider(_externalLoader, options) {
if (options === void 0) { options = provider_1.defaultProviderOptions; }
this.externalLoader = _externalLoader;
this.options = __assign(__assign({}, provider_1.defaultProviderOptions), options);
if (!this.options.apiKey && !this.options.clientId) {
throw new Error('An API key or a client ID is required for the Google Maps provider. Please add it in the "apiKey" or the "clientId" option.');
}
if (this.options.clientId && !this.options.secret) {
throw new Error('An URL signing secret is required if you use a client ID (Premium only). Please add it in the "secret" option.');
}
if (this.options.secret && utils_1.isBrowser()) {
throw new Error('The "secret" option cannot be used in a browser environment.');
}
if (this.options.countryCodes && this.options.countryCodes.length !== 1) {
throw new Error('The "countryCodes" option must have only one country code top-level domain.');
}
}
GoogleMapsProvider.prototype.geocode = function (query, callback, errorCallback) {
var _this = this;
var _a, _b, _c, _d, _e, _f, _g;
var geocodeQuery = provider_1.ProviderHelpers.getGeocodeQueryFromParameter(query, provider_1.GoogleMapsGeocodeQuery);
if (geocodeQuery.getIp()) {
throw new Error("The GoogleMaps provider does not support IP geolocation, only location geocoding.");
}
this.externalLoader.setOptions({
protocol: this.options.useSsl ? "https" : "http",
host: "maps.googleapis.com",
pathname: "maps/api/geocode/json",
});
var params = this.withCommonParams({
address: geocodeQuery.getText(),
bounds: geocodeQuery.getBounds()
? ((_a = geocodeQuery.getBounds()) === null || _a === void 0 ? void 0 : _a.latitudeSW) + "," + ((_b = geocodeQuery.getBounds()) === null || _b === void 0 ? void 0 : _b.longitudeSW) + "|" + ((_c = geocodeQuery.getBounds()) === null || _c === void 0 ? void 0 : _c.latitudeNE) + "," + ((_d = geocodeQuery.getBounds()) === null || _d === void 0 ? void 0 : _d.longitudeNE)
: undefined,
components: geocodeQuery.getComponents()
? (_e = geocodeQuery
.getComponents()) === null || _e === void 0 ? void 0 : _e.map(function (component) { return component.name + ":" + component.value; }).join("|")
: undefined,
region: geocodeQuery.getCountryCodes()
? (_f = geocodeQuery.getCountryCodes()) === null || _f === void 0 ? void 0 : _f.join(",")
: (_g = this.options.countryCodes) === null || _g === void 0 ? void 0 : _g.join(","),
}, geocodeQuery);
if (!callback) {
return new Promise(function (resolve, reject) {
return _this.executeRequest(params, function (results) { return resolve(results); }, {}, {}, function (error) { return reject(error); });
});
}
return this.executeRequest(params, callback, {}, {}, errorCallback);
};
GoogleMapsProvider.prototype.geodecode = function (latitudeOrQuery, longitudeOrCallback, callbackOrErrorCallback, errorCallback) {
var _this = this;
var _a, _b;
var reverseQuery = provider_1.ProviderHelpers.getReverseQueryFromParameters(latitudeOrQuery, longitudeOrCallback, provider_1.GoogleMapsReverseQuery);
var reverseCallback = provider_1.ProviderHelpers.getCallbackFromParameters(longitudeOrCallback, callbackOrErrorCallback);
var reverseErrorCallback = provider_1.ProviderHelpers.getErrorCallbackFromParameters(longitudeOrCallback, callbackOrErrorCallback, errorCallback);
this.externalLoader.setOptions({
protocol: this.options.useSsl ? "https" : "http",
host: "maps.googleapis.com",
pathname: "maps/api/geocode/json",
});
var params = this.withCommonParams({
latlng: reverseQuery.getCoordinates().latitude + "," + reverseQuery.getCoordinates().longitude,
result_type: reverseQuery.getTypes()
? (_a = reverseQuery.getTypes()) === null || _a === void 0 ? void 0 : _a.join("|")
: undefined,
location_type: reverseQuery.getPrecisions()
? (_b = reverseQuery.getPrecisions()) === null || _b === void 0 ? void 0 : _b.join("|")
: undefined,
}, reverseQuery);
if (!reverseCallback) {
return new Promise(function (resolve, reject) {
return _this.executeRequest(params, function (results) { return resolve(results); }, {}, {}, function (error) { return reject(error); });
});
}
return this.executeRequest(params, reverseCallback, {}, {}, reverseErrorCallback);
};
GoogleMapsProvider.prototype.withCommonParams = function (params, query) {
var withCommonParams = __assign(__assign({}, params), { key: this.options.apiKey, client: this.options.clientId, channel: query.getChannel(), language: query.getLocale(), limit: query.getLimit().toString() });
if (this.options.secret) {
withCommonParams = __assign(__assign({}, withCommonParams), { signature: GoogleMapsProvider.signQuery(this.options.secret, this.externalLoader.getOptions().pathname || "", withCommonParams) });
}
return withCommonParams;
};
GoogleMapsProvider.prototype.executeRequest = function (params, callback, headers, body, errorCallback) {
var limit = params.limit, externalLoaderParams = __rest(params, ["limit"]);
this.externalLoader.executeRequest(externalLoaderParams, function (data) {
var errorMessage;
switch (data.status) {
case "REQUEST_DENIED":
errorMessage = "Request has been denied";
if (data.error_message) {
errorMessage += ": " + data.error_message;
}
break;
case "OVER_QUERY_LIMIT":
errorMessage =
"Exceeded daily quota when attempting geocoding request";
if (data.error_message) {
errorMessage += ": " + data.error_message;
}
break;
case "OVER_DAILY_LIMIT":
errorMessage = "API usage has been limited";
if (data.error_message) {
errorMessage += ": " + data.error_message;
}
break;
case "INVALID_REQUEST":
errorMessage = "The request is invalid";
if (data.error_message) {
errorMessage += ": " + data.error_message;
}
break;
case "UNKNOWN_ERROR":
errorMessage = "Unknown error";
if (data.error_message) {
errorMessage += ": " + data.error_message;
}
break;
default:
// Intentionnaly left empty
}
if (errorMessage && errorCallback) {
errorCallback(new error_1.ResponseError(errorMessage, data));
return;
}
if (errorMessage) {
setTimeout(function () {
throw new Error(errorMessage);
});
return;
}
var results = data.results;
var resultsToRemove = results.length - parseInt(limit || results.length.toString(), 10);
if (resultsToRemove > 0) {
results.splice(-resultsToRemove);
}
callback(results.map(function (result) {
return GoogleMapsProvider.mapToGeocoded(result);
}));
}, headers, body, errorCallback);
};
GoogleMapsProvider.mapToGeocoded = function (result) {
var latitude = result.geometry.location.lat;
var longitude = result.geometry.location.lng;
var formattedAddress = result.formatted_address;
var streetNumber;
var streetName;
var subLocality;
var locality;
var postalCode;
var region;
var country;
var countryCode;
var adminLevels = [];
var placeId = result.place_id;
var partialMatch = result.partial_match;
var types = result.types;
var precision = result.geometry.location_type;
var streetAddress;
var intersection;
var political;
var colloquialArea;
var ward;
var neighborhood;
var premise;
var subpremise;
var naturalFeature;
var airport;
var park;
var pointOfInterest;
var establishment;
var postalCodeSuffix;
var subLocalityLevels = [];
result.address_components.forEach(function (addressComponent) {
addressComponent.types.forEach(function (type) {
switch (type) {
case "street_number":
streetNumber = addressComponent.long_name;
break;
case "route":
streetName = addressComponent.long_name;
break;
case "sublocality":
subLocality = addressComponent.long_name;
break;
case "locality":
case "postal_town":
locality = addressComponent.long_name;
break;
case "postal_code":
postalCode = addressComponent.long_name;
break;
case "administrative_area_level_1":
case "administrative_area_level_2":
case "administrative_area_level_3":
case "administrative_area_level_4":
case "administrative_area_level_5":
if (type === "administrative_area_level_1") {
region = addressComponent.long_name;
}
adminLevels.push(AdminLevel_1.default.create({
level: parseInt(type.substr(-1), 10),
name: addressComponent.long_name,
code: addressComponent.short_name,
}));
break;
case "sublocality_level_1":
case "sublocality_level_2":
case "sublocality_level_3":
case "sublocality_level_4":
case "sublocality_level_5":
subLocalityLevels.push(AdminLevel_1.default.create({
level: parseInt(type.substr(-1), 10),
name: addressComponent.long_name,
code: addressComponent.short_name,
}));
break;
case "country":
country = addressComponent.long_name;
countryCode = addressComponent.short_name;
break;
case "street_address":
streetAddress = addressComponent.long_name;
break;
case "intersection":
intersection = addressComponent.long_name;
break;
case "political":
political = addressComponent.long_name;
break;
case "colloquial_area":
colloquialArea = addressComponent.long_name;
break;
case "ward":
ward = addressComponent.long_name;
break;
case "neighborhood":
neighborhood = addressComponent.long_name;
break;
case "premise":
premise = addressComponent.long_name;
break;
case "subpremise":
subpremise = addressComponent.long_name;
break;
case "natural_feature":
naturalFeature = addressComponent.long_name;
break;
case "airport":
airport = addressComponent.long_name;
break;
case "park":
park = addressComponent.long_name;
break;
case "point_of_interest":
pointOfInterest = addressComponent.long_name;
break;
case "establishment":
establishment = addressComponent.long_name;
break;
case "postal_code_suffix":
postalCodeSuffix = addressComponent.long_name;
break;
default:
}
});
});
var geocoded = provider_1.GoogleMapsGeocoded.create({
coordinates: {
latitude: latitude,
longitude: longitude,
},
formattedAddress: formattedAddress,
streetNumber: streetNumber,
streetName: streetName,
subLocality: subLocality,
locality: locality,
postalCode: postalCode,
region: region,
country: country,
countryCode: countryCode,
adminLevels: adminLevels,
placeId: placeId,
partialMatch: partialMatch,
types: types,
precision: precision,
streetAddress: streetAddress,
intersection: intersection,
political: political,
colloquialArea: colloquialArea,
ward: ward,
neighborhood: neighborhood,
premise: premise,
subpremise: subpremise,
naturalFeature: naturalFeature,
airport: airport,
park: park,
pointOfInterest: pointOfInterest,
establishment: establishment,
postalCodeSuffix: postalCodeSuffix,
subLocalityLevels: subLocalityLevels,
});
if (result.geometry.bounds) {
var bounds = result.geometry.bounds;
geocoded = geocoded.withBounds({
latitudeSW: bounds.southwest.lat,
longitudeSW: bounds.southwest.lng,
latitudeNE: bounds.northeast.lat,
longitudeNE: bounds.northeast.lng,
});
}
else if (result.geometry.viewport) {
var viewport = result.geometry.viewport;
geocoded = geocoded.withBounds({
latitudeSW: viewport.southwest.lat,
longitudeSW: viewport.southwest.lng,
latitudeNE: viewport.northeast.lat,
longitudeNE: viewport.northeast.lng,
});
}
else if (precision === "ROOFTOP") {
// Fake bounds
geocoded = geocoded.withBounds({
latitudeSW: latitude,
longitudeSW: longitude,
latitudeNE: latitude,
longitudeNE: longitude,
});
}
return geocoded;
};
GoogleMapsProvider.signQuery = function (secret, pathname, params) {
var crypto = utils_1.getRequireFunc()("crypto");
var filteredRequestParams = utils_1.filterUndefinedObjectValues(params);
var safeSecret = utils_1.decodeBase64(utils_1.decodeUrlSafeBase64(secret));
var toSign = pathname + "?" + new URLSearchParams(filteredRequestParams).toString();
var hashedSignature = utils_1.encodeUrlSafeBase64(crypto.createHmac("sha1", safeSecret).update(toSign).digest("base64"));
return hashedSignature;
};
return GoogleMapsProvider;
}());
exports.default = GoogleMapsProvider;
//# sourceMappingURL=GoogleMapsProvider.js.map