@magmacomputing/tempo-plugin-geo 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +36 -69
- package/dist/index.d.ts +8 -27
- package/dist/index.global.min.js +1 -1
- package/dist/index.js +351 -541
- package/dist/install.global.min.js +1 -1
- package/dist/install.js +351 -541
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -274,7 +274,7 @@ function isNumeric(str) {
|
|
|
274
274
|
return false;
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
|
-
var isType, isPrimitive, isReference, isIterable, isString, isText, isNumber, RE_BIGINT_LITERAL, isInteger, isArray, isArrayLike, isObject, isDate, isRegExp, isSymbol, isSymbolFor, isSafeKey, isNullish, isUndefined, isDefined, isFunction, isCallable, isMap, isSet, isError, isLocale, isEmpty;
|
|
277
|
+
var isType, isPrimitive, isReference, isIterable, isString, isText, isNumber, RE_BIGINT_LITERAL, isInteger, isBoolean, isArray, isArrayLike, isObject, isDate, isRegExp, isSymbol, isSymbolFor, isSafeKey, isNullish, isUndefined, isDefined, isFunction, isCallable, isMap, isSet, isError, isLocale, isEmpty;
|
|
278
278
|
var init_assertion_library = __esm({
|
|
279
279
|
"../../library/dist/common/primitives/assertion.library.js"() {
|
|
280
280
|
"use strict";
|
|
@@ -289,6 +289,7 @@ var init_assertion_library = __esm({
|
|
|
289
289
|
isNumber = (obj) => Number.isFinite(obj);
|
|
290
290
|
RE_BIGINT_LITERAL = /^[+-]?[0-9]+n$/;
|
|
291
291
|
isInteger = (obj) => typeof obj === "bigint";
|
|
292
|
+
isBoolean = (obj) => typeof obj === "boolean";
|
|
292
293
|
isArray = (obj) => Array.isArray(obj);
|
|
293
294
|
isArrayLike = (obj) => protoType(obj) === "Object" && "length" in obj && Object.keys(obj).every((key) => key === "length" || isNumber(Number(key)));
|
|
294
295
|
isObject = (obj) => isType(obj, "Object");
|
|
@@ -2774,8 +2775,8 @@ __export(sdk_exports, {
|
|
|
2774
2775
|
__reExport(sdk_exports, sdk_star);
|
|
2775
2776
|
import * as sdk_star from "@magmacomputing/tempo/plugin/sdk";
|
|
2776
2777
|
import { definePlugin as originalPlugin, defineTerm as originalTerm } from "@magmacomputing/tempo/plugin/sdk";
|
|
2777
|
-
var definePlugin = (config) => originalPlugin({ ...config, version: "1.
|
|
2778
|
-
var defineTerm = (config) => originalTerm({ ...config, version: "1.
|
|
2778
|
+
var definePlugin = (config) => originalPlugin({ ...config, version: "1.4.1" });
|
|
2779
|
+
var defineTerm = (config) => originalTerm({ ...config, version: "1.4.1" });
|
|
2779
2780
|
|
|
2780
2781
|
// ../../library/dist/common/runtime/mapper.library.js
|
|
2781
2782
|
init_utility_library();
|
|
@@ -2794,9 +2795,13 @@ function evaluate(...values) {
|
|
|
2794
2795
|
}
|
|
2795
2796
|
|
|
2796
2797
|
// ../../library/dist/common/runtime/mapper.library.js
|
|
2797
|
-
|
|
2798
|
+
init_cache_class();
|
|
2798
2799
|
var MAP_KEY2 = "_magma_geo_";
|
|
2799
2800
|
var DEFAULT_GEO_TTL = 24 * 60 * 60 * 1e3;
|
|
2801
|
+
var DEFAULT_REVERSE_GEO_ENDPOINT = "https://api.bigdatacloud.net/data/reverse-geocode-client";
|
|
2802
|
+
var DEFAULT_REVERSE_GEO_TIMEOUT = 3e3;
|
|
2803
|
+
var DEFAULT_REVERSE_GEO_MAX_BYTES = 64 * 1024;
|
|
2804
|
+
var DEFAULT_SPATIAL_CACHE_SIZE = 1e3;
|
|
2800
2805
|
var GEO_PROPERTIES = [
|
|
2801
2806
|
"latitude",
|
|
2802
2807
|
"lat",
|
|
@@ -2811,13 +2816,33 @@ var GEO_PROPERTIES = [
|
|
|
2811
2816
|
"timezone",
|
|
2812
2817
|
"tz"
|
|
2813
2818
|
];
|
|
2814
|
-
var
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2819
|
+
var parseCoordNumber = (val) => {
|
|
2820
|
+
if (!isDefined(val) || isBoolean(val))
|
|
2821
|
+
return NaN;
|
|
2822
|
+
if (isString(val)) {
|
|
2823
|
+
const trimmed = val.trim();
|
|
2824
|
+
return trimmed.length > 0 ? Number(trimmed) : NaN;
|
|
2825
|
+
}
|
|
2826
|
+
return isNumber(val) ? val : NaN;
|
|
2820
2827
|
};
|
|
2828
|
+
var normalizeLat = (lat, round = true) => {
|
|
2829
|
+
const n = parseCoordNumber(lat);
|
|
2830
|
+
if (!isNumber(n) || n < -90 || n > 90)
|
|
2831
|
+
return void 0;
|
|
2832
|
+
return round ? Math.round(n * 1e3) / 1e3 : n;
|
|
2833
|
+
};
|
|
2834
|
+
var normalizeLng = (lng, round = true) => {
|
|
2835
|
+
const n = parseCoordNumber(lng);
|
|
2836
|
+
if (!isNumber(n) || n < -180 || n > 180)
|
|
2837
|
+
return void 0;
|
|
2838
|
+
return round ? Math.round(n * 1e3) / 1e3 : n;
|
|
2839
|
+
};
|
|
2840
|
+
var normalizeCoords = (lat, lng, round = true) => {
|
|
2841
|
+
const nLat = normalizeLat(lat, round);
|
|
2842
|
+
const nLng = normalizeLng(lng, round);
|
|
2843
|
+
return isDefined(nLat) && isDefined(nLng) ? { lat: nLat, lng: nLng } : void 0;
|
|
2844
|
+
};
|
|
2845
|
+
var normalizeElevation = (elevation) => isNumber(elevation) ? Math.round(elevation * 1e3) / 1e3 : void 0;
|
|
2821
2846
|
var resolveSphere = (sphere, lat) => {
|
|
2822
2847
|
if (sphere === "north" || sphere === "south" || sphere === "equator")
|
|
2823
2848
|
return sphere;
|
|
@@ -2825,35 +2850,47 @@ var resolveSphere = (sphere, lat) => {
|
|
|
2825
2850
|
return lat > 1e-3 ? "north" : lat < -1e-3 ? "south" : "equator";
|
|
2826
2851
|
return void 0;
|
|
2827
2852
|
};
|
|
2828
|
-
var
|
|
2853
|
+
var parseCoordinatePair = (input, normalize = true) => {
|
|
2829
2854
|
if (isString(input) && input.includes(",")) {
|
|
2830
|
-
const
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2855
|
+
const parts = input.split(",").map((s) => s.trim());
|
|
2856
|
+
return parts.length >= 2 ? normalizeCoords(parts[0], parts[1], normalize) : void 0;
|
|
2857
|
+
}
|
|
2858
|
+
return Array.isArray(input) && input.length >= 2 ? normalizeCoords(input[0], input[1], normalize) : void 0;
|
|
2859
|
+
};
|
|
2860
|
+
var assembleGeoConfig = (coords, meta) => {
|
|
2861
|
+
const lat = "lat" in coords ? coords.lat : coords.latitude;
|
|
2862
|
+
const lng = "lng" in coords ? coords.lng : coords.longitude;
|
|
2863
|
+
const nLat = normalizeLat(lat);
|
|
2864
|
+
const nLng = normalizeLng(lng);
|
|
2865
|
+
const sphere = resolveSphere(meta?.sphere, nLat ?? lat);
|
|
2866
|
+
const elevation = normalizeElevation(meta?.elevation);
|
|
2867
|
+
return {
|
|
2868
|
+
latitude: nLat ?? lat,
|
|
2869
|
+
longitude: nLng ?? lng,
|
|
2870
|
+
...sphere ? { sphere } : {},
|
|
2871
|
+
...isString(meta?.city) && !isEmpty(meta.city) ? { city: meta.city.trim() } : {},
|
|
2872
|
+
...isString(meta?.country) && !isEmpty(meta.country) ? { country: meta.country.trim() } : {},
|
|
2873
|
+
...elevation !== void 0 ? { elevation } : {},
|
|
2874
|
+
...isString(meta?.timezone) && !isEmpty(meta.timezone) ? { timezone: meta.timezone.trim() } : {}
|
|
2875
|
+
};
|
|
2876
|
+
};
|
|
2877
|
+
var extractCustomProperties = (source, excludedKeys = GEO_PROPERTIES) => {
|
|
2878
|
+
const custom = {};
|
|
2879
|
+
if (isReference(source)) {
|
|
2880
|
+
for (const key of Object.keys(source)) {
|
|
2881
|
+
if (isSafeKey(key) && !excludedKeys.includes(key)) {
|
|
2882
|
+
custom[key] = source[key];
|
|
2840
2883
|
}
|
|
2841
2884
|
}
|
|
2842
|
-
return void 0;
|
|
2843
2885
|
}
|
|
2886
|
+
return custom;
|
|
2887
|
+
};
|
|
2888
|
+
var coerceGeo = (input) => {
|
|
2889
|
+
const pair = parseCoordinatePair(input);
|
|
2890
|
+
if (pair)
|
|
2891
|
+
return assembleGeoConfig(pair);
|
|
2844
2892
|
if (isPrimitive(input))
|
|
2845
2893
|
return void 0;
|
|
2846
|
-
if (Array.isArray(input) && input.length >= 2) {
|
|
2847
|
-
const coords = normalizeCoords(input[0], input[1]);
|
|
2848
|
-
if (coords) {
|
|
2849
|
-
const result2 = { latitude: coords.lat, longitude: coords.lng };
|
|
2850
|
-
const sphere2 = resolveSphere(void 0, coords.lat);
|
|
2851
|
-
if (sphere2)
|
|
2852
|
-
result2.sphere = sphere2;
|
|
2853
|
-
return result2;
|
|
2854
|
-
}
|
|
2855
|
-
return void 0;
|
|
2856
|
-
}
|
|
2857
2894
|
const geo = input.geo ?? input;
|
|
2858
2895
|
const cfg = input.config?.geo ?? input.config;
|
|
2859
2896
|
const lat = evaluate(geo?.latitude, geo?.lat, input.latitude, input.lat, cfg?.latitude, cfg?.lat);
|
|
@@ -2870,8 +2907,9 @@ var coerceGeo = (input) => {
|
|
|
2870
2907
|
result.latitude = nLat;
|
|
2871
2908
|
if (nLng !== void 0)
|
|
2872
2909
|
result.longitude = nLng;
|
|
2873
|
-
|
|
2874
|
-
|
|
2910
|
+
const nElevation = normalizeElevation(elevation);
|
|
2911
|
+
if (nElevation !== void 0)
|
|
2912
|
+
result.elevation = nElevation;
|
|
2875
2913
|
const resolvedSphere = resolveSphere(sphere, result.latitude);
|
|
2876
2914
|
if (resolvedSphere)
|
|
2877
2915
|
result.sphere = resolvedSphere;
|
|
@@ -2882,12 +2920,8 @@ var coerceGeo = (input) => {
|
|
|
2882
2920
|
if (isString(timezone))
|
|
2883
2921
|
result.timezone = timezone;
|
|
2884
2922
|
const explicitGeo = isReference(input.geo) ? input.geo : isReference(input.config?.geo) ? input.config.geo : void 0;
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
if (isSafeKey(key) && !GEO_PROPERTIES.includes(key))
|
|
2888
|
-
result[key] = explicitGeo[key];
|
|
2889
|
-
}
|
|
2890
|
-
}
|
|
2923
|
+
const customKeys = extractCustomProperties(explicitGeo);
|
|
2924
|
+
Object.assign(result, customKeys);
|
|
2891
2925
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
2892
2926
|
};
|
|
2893
2927
|
var resolveCacheKey = (keyOrOpts) => {
|
|
@@ -2924,35 +2958,23 @@ var getStashedGeo = (keyOrOpts) => {
|
|
|
2924
2958
|
return void 0;
|
|
2925
2959
|
}
|
|
2926
2960
|
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
if (coords) {
|
|
2931
|
-
return {
|
|
2932
|
-
latitude: coords.lat,
|
|
2933
|
-
longitude: coords.lng,
|
|
2934
|
-
sphere: resolveSphere(void 0, coords.lat)
|
|
2935
|
-
};
|
|
2936
|
-
}
|
|
2937
|
-
}
|
|
2961
|
+
const pair = parseCoordinatePair(parsed);
|
|
2962
|
+
if (pair)
|
|
2963
|
+
return assembleGeoConfig(pair);
|
|
2938
2964
|
if (isObject(parsed)) {
|
|
2939
2965
|
const rawCoords = parsed.geolocation?.coords ?? parsed.coords ?? parsed;
|
|
2940
2966
|
const lat = rawCoords?.latitude ?? rawCoords?.lat;
|
|
2941
2967
|
const lng = rawCoords?.longitude ?? rawCoords?.lng ?? rawCoords?.lon ?? rawCoords?.long;
|
|
2942
2968
|
const coords = normalizeCoords(lat, lng);
|
|
2943
2969
|
if (coords) {
|
|
2944
|
-
const result = { latitude: coords.lat, longitude: coords.lng };
|
|
2945
2970
|
const elevation = parsed.elevation ?? rawCoords.elevation;
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
if (isString(parsed.timezone))
|
|
2954
|
-
result.timezone = parsed.timezone;
|
|
2955
|
-
return result;
|
|
2971
|
+
return assembleGeoConfig(coords, {
|
|
2972
|
+
elevation,
|
|
2973
|
+
sphere: parsed.sphere,
|
|
2974
|
+
country: parsed.country,
|
|
2975
|
+
city: parsed.city,
|
|
2976
|
+
timezone: parsed.timezone
|
|
2977
|
+
});
|
|
2956
2978
|
}
|
|
2957
2979
|
}
|
|
2958
2980
|
} catch {
|
|
@@ -2982,7 +3004,7 @@ var geoLookup = async (opts = {}) => {
|
|
|
2982
3004
|
const stashed = getStashedGeo(opts);
|
|
2983
3005
|
if (stashed && isNumber(stashed.latitude) && isNumber(stashed.longitude)) {
|
|
2984
3006
|
const { latitude, longitude, ...rest } = stashed;
|
|
2985
|
-
|
|
3007
|
+
const cachedRes = {
|
|
2986
3008
|
...rest,
|
|
2987
3009
|
status: "cached",
|
|
2988
3010
|
lat: latitude,
|
|
@@ -2990,6 +3012,17 @@ var geoLookup = async (opts = {}) => {
|
|
|
2990
3012
|
latitude,
|
|
2991
3013
|
longitude
|
|
2992
3014
|
};
|
|
3015
|
+
const shouldReverse = opts.reverse === true || opts.autoReverse === true;
|
|
3016
|
+
if (shouldReverse && (!cachedRes.city || !cachedRes.country)) {
|
|
3017
|
+
const reversed = await reverseGeocode(cachedRes, opts);
|
|
3018
|
+
if (reversed) {
|
|
3019
|
+
if (!cachedRes.city && isText(reversed.city))
|
|
3020
|
+
cachedRes.city = reversed.city;
|
|
3021
|
+
if (!cachedRes.country && isText(reversed.country))
|
|
3022
|
+
cachedRes.country = reversed.country;
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
return cachedRes;
|
|
2993
3026
|
}
|
|
2994
3027
|
}
|
|
2995
3028
|
let res;
|
|
@@ -3043,15 +3076,17 @@ var geoLookup = async (opts = {}) => {
|
|
|
3043
3076
|
res.latitude = coords.lat;
|
|
3044
3077
|
res.longitude = coords.lng;
|
|
3045
3078
|
res.sphere = sphere;
|
|
3046
|
-
const
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3079
|
+
const shouldReverse = opts.reverse === true || opts.autoReverse === true;
|
|
3080
|
+
if (shouldReverse && (!res.city || !res.country)) {
|
|
3081
|
+
const reversed = await reverseGeocode(res, opts);
|
|
3082
|
+
if (reversed) {
|
|
3083
|
+
if (!res.city && isText(reversed.city))
|
|
3084
|
+
res.city = reversed.city;
|
|
3085
|
+
if (!res.country && isText(reversed.country))
|
|
3086
|
+
res.country = reversed.country;
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
const stashPayload = assembleGeoConfig(coords, res);
|
|
3055
3090
|
try {
|
|
3056
3091
|
const ttl = isNumber(opts.ttl) ? opts.ttl : DEFAULT_GEO_TTL;
|
|
3057
3092
|
stashGeo(stashPayload, ttl, opts);
|
|
@@ -3060,471 +3095,266 @@ var geoLookup = async (opts = {}) => {
|
|
|
3060
3095
|
}
|
|
3061
3096
|
return res;
|
|
3062
3097
|
};
|
|
3098
|
+
var requestModulePromise2 = null;
|
|
3099
|
+
var getRequestModule2 = () => {
|
|
3100
|
+
if (!requestModulePromise2)
|
|
3101
|
+
requestModulePromise2 = Promise.resolve().then(() => (init_request_library(), request_library_exports));
|
|
3102
|
+
return requestModulePromise2;
|
|
3103
|
+
};
|
|
3104
|
+
var reverseGeoCache = new BoundedCache(DEFAULT_SPATIAL_CACHE_SIZE, DEFAULT_GEO_TTL);
|
|
3105
|
+
var isValidGeoEndpoint = (urlStr) => {
|
|
3106
|
+
try {
|
|
3107
|
+
const parsed = new URL(urlStr);
|
|
3108
|
+
return parsed.protocol === "https:" || parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
3109
|
+
} catch {
|
|
3110
|
+
return false;
|
|
3111
|
+
}
|
|
3112
|
+
};
|
|
3113
|
+
var getSpatialCacheKey = (lat, lng, sourceOrOpts) => {
|
|
3114
|
+
const bucket = `${lat.toFixed(2)},${lng.toFixed(2)}`;
|
|
3115
|
+
if (isString(sourceOrOpts) && !isEmpty(sourceOrOpts)) {
|
|
3116
|
+
return `${bucket}:${sourceOrOpts.trim()}`;
|
|
3117
|
+
}
|
|
3118
|
+
if (isObject(sourceOrOpts)) {
|
|
3119
|
+
const hasExplicitProvider = Object.hasOwn(sourceOrOpts, "provider");
|
|
3120
|
+
const provider = hasExplicitProvider ? sourceOrOpts.provider : activeGeoProvider;
|
|
3121
|
+
const providerName = provider && isString(provider.name) && !isEmpty(provider.name) ? provider.name.trim() : void 0;
|
|
3122
|
+
const rawEndpoint = isString(sourceOrOpts.reverseEndpoint ?? sourceOrOpts.reverseGeoEndpoint) && !isEmpty(sourceOrOpts.reverseEndpoint ?? sourceOrOpts.reverseGeoEndpoint) ? (sourceOrOpts.reverseEndpoint ?? sourceOrOpts.reverseGeoEndpoint).trim() : void 0;
|
|
3123
|
+
const endpoint = rawEndpoint && isValidGeoEndpoint(rawEndpoint) ? rawEndpoint : void 0;
|
|
3124
|
+
if (providerName && endpoint)
|
|
3125
|
+
return `${bucket}:${providerName}:${endpoint}`;
|
|
3126
|
+
if (providerName)
|
|
3127
|
+
return `${bucket}:${providerName}`;
|
|
3128
|
+
if (endpoint)
|
|
3129
|
+
return `${bucket}:${endpoint}`;
|
|
3130
|
+
} else if (activeGeoProvider && isString(activeGeoProvider.name) && !isEmpty(activeGeoProvider.name)) {
|
|
3131
|
+
return `${bucket}:${activeGeoProvider.name.trim()}`;
|
|
3132
|
+
}
|
|
3133
|
+
return bucket;
|
|
3134
|
+
};
|
|
3063
3135
|
async function reverseGeocode(coords, opts) {
|
|
3064
|
-
const
|
|
3136
|
+
const coerced = coerceGeo(coords);
|
|
3137
|
+
if (!coerced || !isNumber(coerced.latitude) || !isNumber(coerced.longitude))
|
|
3138
|
+
return null;
|
|
3139
|
+
const lat = coerced.latitude;
|
|
3140
|
+
const lng = coerced.longitude;
|
|
3141
|
+
const key = getSpatialCacheKey(lat, lng, opts);
|
|
3142
|
+
const useCache = opts?.refresh !== true;
|
|
3143
|
+
if (useCache) {
|
|
3144
|
+
const cached = reverseGeoCache.get(key);
|
|
3145
|
+
if (cached) {
|
|
3146
|
+
return {
|
|
3147
|
+
...cached,
|
|
3148
|
+
latitude: lat,
|
|
3149
|
+
longitude: lng,
|
|
3150
|
+
sphere: resolveSphere(cached.sphere, lat),
|
|
3151
|
+
elevation: isNumber(cached.elevation) ? cached.elevation : coerced.elevation,
|
|
3152
|
+
timezone: isString(cached.timezone) ? cached.timezone : coerced.timezone
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
const hasExplicitProvider = isObject(opts) && Object.hasOwn(opts, "provider");
|
|
3157
|
+
const provider = hasExplicitProvider ? opts.provider : activeGeoProvider;
|
|
3065
3158
|
if (provider && isFunction(provider.reverseGeocode)) {
|
|
3066
|
-
|
|
3159
|
+
try {
|
|
3160
|
+
const res = await provider.reverseGeocode(coerced, opts);
|
|
3161
|
+
if (res && isObject(res)) {
|
|
3162
|
+
const result = assembleGeoConfig({ lat, lng }, {
|
|
3163
|
+
sphere: res.sphere,
|
|
3164
|
+
city: res.city,
|
|
3165
|
+
country: res.country,
|
|
3166
|
+
elevation: isNumber(res.elevation) ? res.elevation : coerced.elevation,
|
|
3167
|
+
timezone: isString(res.timezone) ? res.timezone : coerced.timezone
|
|
3168
|
+
});
|
|
3169
|
+
const cachePayload = assembleGeoConfig({ lat, lng }, {
|
|
3170
|
+
sphere: res.sphere,
|
|
3171
|
+
city: res.city,
|
|
3172
|
+
country: res.country,
|
|
3173
|
+
timezone: res.timezone
|
|
3174
|
+
});
|
|
3175
|
+
reverseGeoCache.set(key, cachePayload);
|
|
3176
|
+
return result;
|
|
3177
|
+
}
|
|
3178
|
+
if (opts?.fallback === false)
|
|
3179
|
+
return null;
|
|
3180
|
+
} catch (err) {
|
|
3181
|
+
if (opts?.fallback === false)
|
|
3182
|
+
throw err;
|
|
3183
|
+
}
|
|
3184
|
+
} else if (opts?.fallback === false && (provider || hasExplicitProvider)) {
|
|
3185
|
+
return null;
|
|
3067
3186
|
}
|
|
3068
|
-
const
|
|
3069
|
-
|
|
3187
|
+
const rawReverseEndpoint = opts?.reverseEndpoint ?? opts?.reverseGeoEndpoint;
|
|
3188
|
+
if (isText(rawReverseEndpoint)) {
|
|
3189
|
+
const rawEndpoint = rawReverseEndpoint.trim();
|
|
3190
|
+
if (!isValidGeoEndpoint(rawEndpoint))
|
|
3191
|
+
return opts?.fallback === false ? null : coerced;
|
|
3192
|
+
}
|
|
3193
|
+
const customEndpoint = isText(rawReverseEndpoint) ? rawReverseEndpoint.trim() : void 0;
|
|
3194
|
+
const fallbackKey = getSpatialCacheKey(lat, lng, customEndpoint ?? "default");
|
|
3195
|
+
if (useCache) {
|
|
3196
|
+
const cachedFallback = reverseGeoCache.get(fallbackKey);
|
|
3197
|
+
if (cachedFallback) {
|
|
3198
|
+
return {
|
|
3199
|
+
...cachedFallback,
|
|
3200
|
+
latitude: lat,
|
|
3201
|
+
longitude: lng,
|
|
3202
|
+
sphere: resolveSphere(cachedFallback.sphere, lat),
|
|
3203
|
+
elevation: isNumber(cachedFallback.elevation) ? cachedFallback.elevation : coerced.elevation,
|
|
3204
|
+
timezone: isString(cachedFallback.timezone) ? cachedFallback.timezone : coerced.timezone
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
let endpoint;
|
|
3209
|
+
if (customEndpoint) {
|
|
3210
|
+
if (/\{(?:latitude|lat|longitude|lng|lon)\}/i.test(customEndpoint)) {
|
|
3211
|
+
endpoint = customEndpoint.replace(/\{latitude\}/gi, encodeURIComponent(lat)).replace(/\{lat\}/gi, encodeURIComponent(lat)).replace(/\{longitude\}/gi, encodeURIComponent(lng)).replace(/\{lng\}/gi, encodeURIComponent(lng)).replace(/\{lon\}/gi, encodeURIComponent(lng));
|
|
3212
|
+
} else {
|
|
3213
|
+
const sep = customEndpoint.includes("?") ? "&" : "?";
|
|
3214
|
+
endpoint = `${customEndpoint}${sep}latitude=${encodeURIComponent(lat)}&longitude=${encodeURIComponent(lng)}`;
|
|
3215
|
+
}
|
|
3216
|
+
} else {
|
|
3217
|
+
endpoint = `${DEFAULT_REVERSE_GEO_ENDPOINT}?latitude=${lat}&longitude=${lng}&localityLanguage=en`;
|
|
3218
|
+
}
|
|
3219
|
+
try {
|
|
3220
|
+
const timeout = isNumber(opts?.timeout) && opts.timeout > 0 ? opts.timeout : DEFAULT_REVERSE_GEO_TIMEOUT;
|
|
3221
|
+
const maxBytes = isNumber(opts?.maxBytes) && opts.maxBytes > 0 ? opts.maxBytes : DEFAULT_REVERSE_GEO_MAX_BYTES;
|
|
3222
|
+
const { fetchRequest: fetchRequest2 } = await getRequestModule2();
|
|
3223
|
+
const data = await fetchRequest2(endpoint, {}, { timeout, maxBytes });
|
|
3224
|
+
if (isObject(data)) {
|
|
3225
|
+
const city = isText(data.city) ? data.city.trim() : isText(data.locality) ? data.locality.trim() : isText(data.principalSubdivision) ? data.principalSubdivision.trim() : void 0;
|
|
3226
|
+
const country = isText(data.countryCode) ? data.countryCode.trim() : isText(data.countryName) ? data.countryName.trim() : void 0;
|
|
3227
|
+
const result = assembleGeoConfig({ lat, lng }, {
|
|
3228
|
+
city,
|
|
3229
|
+
country,
|
|
3230
|
+
elevation: coerced.elevation,
|
|
3231
|
+
timezone: coerced.timezone
|
|
3232
|
+
});
|
|
3233
|
+
if (isText(city) || isText(country)) {
|
|
3234
|
+
const cachePayload = assembleGeoConfig({ lat, lng }, {
|
|
3235
|
+
city,
|
|
3236
|
+
country
|
|
3237
|
+
});
|
|
3238
|
+
reverseGeoCache.set(fallbackKey, cachePayload);
|
|
3239
|
+
}
|
|
3240
|
+
return result;
|
|
3241
|
+
}
|
|
3242
|
+
} catch {
|
|
3243
|
+
}
|
|
3244
|
+
return coerced;
|
|
3070
3245
|
}
|
|
3071
3246
|
async function forwardGeocode(query, opts) {
|
|
3072
3247
|
const provider = opts?.provider ?? activeGeoProvider;
|
|
3073
|
-
|
|
3074
|
-
return provider.forwardGeocode(query, opts);
|
|
3075
|
-
}
|
|
3076
|
-
return null;
|
|
3248
|
+
return provider && isFunction(provider.forwardGeocode) ? provider.forwardGeocode(query, opts) : null;
|
|
3077
3249
|
}
|
|
3078
3250
|
var resolveGeoCoordinates = async (input, opts = {}) => {
|
|
3251
|
+
const effectiveOpts = isObject(input) ? { ...input, ...opts } : opts;
|
|
3079
3252
|
const coerced = coerceGeo(input);
|
|
3080
|
-
if (coerced && isNumber(coerced.latitude) && isNumber(coerced.longitude))
|
|
3253
|
+
if (coerced && isNumber(coerced.latitude) && isNumber(coerced.longitude)) {
|
|
3254
|
+
const shouldReverse = effectiveOpts.reverse === true || effectiveOpts.autoReverse === true;
|
|
3255
|
+
if (shouldReverse && (!coerced.city || !coerced.country)) {
|
|
3256
|
+
const reversed = await reverseGeocode(coerced, effectiveOpts);
|
|
3257
|
+
if (reversed) {
|
|
3258
|
+
return {
|
|
3259
|
+
...reversed,
|
|
3260
|
+
...coerced,
|
|
3261
|
+
city: isText(coerced.city) ? coerced.city : reversed.city,
|
|
3262
|
+
country: isText(coerced.country) ? coerced.country : reversed.country,
|
|
3263
|
+
lat: coerced.latitude,
|
|
3264
|
+
lng: coerced.longitude,
|
|
3265
|
+
latitude: coerced.latitude,
|
|
3266
|
+
longitude: coerced.longitude
|
|
3267
|
+
};
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3081
3270
|
return {
|
|
3082
3271
|
...coerced,
|
|
3083
3272
|
lat: coerced.latitude,
|
|
3084
3273
|
lng: coerced.longitude
|
|
3085
3274
|
};
|
|
3086
|
-
|
|
3087
|
-
|
|
3275
|
+
}
|
|
3276
|
+
const stashed = effectiveOpts.refresh === true ? void 0 : getStashedGeo(effectiveOpts);
|
|
3277
|
+
if (stashed && isNumber(stashed.latitude) && isNumber(stashed.longitude)) {
|
|
3278
|
+
const shouldReverse = effectiveOpts.reverse === true || effectiveOpts.autoReverse === true;
|
|
3279
|
+
if (shouldReverse && (!stashed.city || !stashed.country)) {
|
|
3280
|
+
const reversed = await reverseGeocode(stashed, effectiveOpts);
|
|
3281
|
+
if (reversed) {
|
|
3282
|
+
return {
|
|
3283
|
+
...reversed,
|
|
3284
|
+
...stashed,
|
|
3285
|
+
city: isText(stashed.city) ? stashed.city : reversed.city,
|
|
3286
|
+
country: isText(stashed.country) ? stashed.country : reversed.country,
|
|
3287
|
+
lat: stashed.latitude,
|
|
3288
|
+
lng: stashed.longitude,
|
|
3289
|
+
latitude: stashed.latitude,
|
|
3290
|
+
longitude: stashed.longitude
|
|
3291
|
+
};
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3088
3294
|
return {
|
|
3089
3295
|
...stashed,
|
|
3090
3296
|
lat: stashed.latitude,
|
|
3091
3297
|
lng: stashed.longitude
|
|
3092
3298
|
};
|
|
3093
|
-
|
|
3299
|
+
}
|
|
3300
|
+
const lookup = await geoLookup(effectiveOpts);
|
|
3094
3301
|
const coords = normalizeCoords(lookup.lat, lookup.lng);
|
|
3095
3302
|
if (isNullish(lookup.error) && coords) {
|
|
3096
3303
|
const sphere = resolveSphere(lookup.sphere, coords.lat);
|
|
3097
|
-
|
|
3098
|
-
...lookup,
|
|
3304
|
+
const base = {
|
|
3305
|
+
...assembleGeoConfig(coords, { ...lookup, sphere }),
|
|
3099
3306
|
lat: coords.lat,
|
|
3100
3307
|
lng: coords.lng,
|
|
3101
3308
|
latitude: coords.lat,
|
|
3102
3309
|
longitude: coords.lng,
|
|
3103
3310
|
sphere
|
|
3104
3311
|
};
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
return void 0;
|
|
3121
|
-
}
|
|
3122
|
-
if (Array.isArray(input) && input.length >= 2) {
|
|
3123
|
-
const rawLat = input[0];
|
|
3124
|
-
const rawLng = input[1];
|
|
3125
|
-
if (rawLat !== null && rawLng !== null && rawLat !== "" && rawLng !== "" && typeof rawLat !== "boolean" && typeof rawLng !== "boolean") {
|
|
3126
|
-
const lat = typeof rawLat === "string" && rawLat.trim() === "" ? NaN : Number(rawLat);
|
|
3127
|
-
const lng = typeof rawLng === "string" && rawLng.trim() === "" ? NaN : Number(rawLng);
|
|
3128
|
-
if (isNumber(lat) && isNumber(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
|
|
3129
|
-
return { lat, lng };
|
|
3130
|
-
}
|
|
3131
|
-
}
|
|
3132
|
-
return void 0;
|
|
3133
|
-
}
|
|
3134
|
-
if (isReference(input)) {
|
|
3135
|
-
const geo = input.geo ?? input.config?.geo ?? input;
|
|
3136
|
-
const cfg = input.config?.geo ?? input.config;
|
|
3137
|
-
const rawLat = evaluate(geo?.latitude, geo?.lat, input.latitude, input.lat, cfg?.latitude, cfg?.lat);
|
|
3138
|
-
const rawLng = evaluate(geo?.longitude, geo?.lng, geo?.lon, geo?.long, input.longitude, input.lng, input.lon, input.long, cfg?.longitude, cfg?.lng, cfg?.lon, cfg?.long);
|
|
3139
|
-
if (rawLat !== null && rawLng !== null && rawLat !== "" && rawLng !== "" && typeof rawLat !== "boolean" && typeof rawLng !== "boolean") {
|
|
3140
|
-
const lat = typeof rawLat === "string" && rawLat.trim() === "" ? NaN : Number(rawLat);
|
|
3141
|
-
const lng = typeof rawLng === "string" && rawLng.trim() === "" ? NaN : Number(rawLng);
|
|
3142
|
-
if (isNumber(lat) && isNumber(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
|
|
3143
|
-
return { lat, lng };
|
|
3144
|
-
}
|
|
3145
|
-
}
|
|
3146
|
-
}
|
|
3147
|
-
return void 0;
|
|
3148
|
-
};
|
|
3149
|
-
var toRadianCoordinates = (from, to) => {
|
|
3150
|
-
const c1 = coerceGeo(from);
|
|
3151
|
-
const c2 = coerceGeo(to);
|
|
3152
|
-
if (!c1 || !c2 || !isNumber(c1.latitude) || !isNumber(c1.longitude) || !isNumber(c2.latitude) || !isNumber(c2.longitude))
|
|
3153
|
-
return void 0;
|
|
3154
|
-
const toRad = Math.PI / 180;
|
|
3155
|
-
const lat1 = c1.latitude * toRad;
|
|
3156
|
-
const lng1 = c1.longitude * toRad;
|
|
3157
|
-
const lat2 = c2.latitude * toRad;
|
|
3158
|
-
const lng2 = c2.longitude * toRad;
|
|
3159
|
-
return {
|
|
3160
|
-
lat1,
|
|
3161
|
-
lat2,
|
|
3162
|
-
lng1,
|
|
3163
|
-
lng2,
|
|
3164
|
-
dLat: lat2 - lat1,
|
|
3165
|
-
dLng: lng2 - lng1
|
|
3166
|
-
};
|
|
3167
|
-
};
|
|
3168
|
-
function haversineDistance(from, to, unit = "km") {
|
|
3169
|
-
const coords = toRadianCoordinates(from, to);
|
|
3170
|
-
if (!coords)
|
|
3171
|
-
return NaN;
|
|
3172
|
-
const { lat1, lat2, dLat, dLng } = coords;
|
|
3173
|
-
const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLng / 2) ** 2;
|
|
3174
|
-
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
3175
|
-
const radius = unit === "miles" ? 3958.7613 : unit === "m" ? 63710088e-1 : 6371.0088;
|
|
3176
|
-
const dist = radius * c;
|
|
3177
|
-
return unit === "m" ? Math.round(dist) : Math.round(dist * 1e3) / 1e3;
|
|
3178
|
-
}
|
|
3179
|
-
var resolveCivilTimezoneOffset = (tz, epochMs) => {
|
|
3180
|
-
try {
|
|
3181
|
-
if (isDefined(globalThis.Temporal) && isFunction(globalThis.Temporal.Instant?.fromEpochMilliseconds)) {
|
|
3182
|
-
const zdt = Temporal.Instant.fromEpochMilliseconds(epochMs).toZonedDateTimeISO(tz);
|
|
3183
|
-
return zdt.offsetNanoseconds / 6e10;
|
|
3184
|
-
}
|
|
3185
|
-
const parts = new Intl.DateTimeFormat("en-US", {
|
|
3186
|
-
timeZone: tz,
|
|
3187
|
-
timeZoneName: "longOffset",
|
|
3188
|
-
year: "numeric"
|
|
3189
|
-
}).formatToParts(new Date(epochMs));
|
|
3190
|
-
const tzPart = parts.find((p) => p.type === "timeZoneName")?.value;
|
|
3191
|
-
if (tzPart) {
|
|
3192
|
-
if (tzPart === "GMT" || tzPart === "UTC")
|
|
3193
|
-
return 0;
|
|
3194
|
-
const match = tzPart.match(/GMT([+-])(\d{2}):(\d{2})/);
|
|
3195
|
-
if (match) {
|
|
3196
|
-
const sign = match[1] === "-" ? -1 : 1;
|
|
3197
|
-
return sign * (parseInt(match[2], 10) * 60 + parseInt(match[3], 10));
|
|
3198
|
-
}
|
|
3199
|
-
}
|
|
3200
|
-
} catch {
|
|
3201
|
-
}
|
|
3202
|
-
return void 0;
|
|
3203
|
-
};
|
|
3204
|
-
function solarOffset(coords, options) {
|
|
3205
|
-
const geo = coerceGeo(coords);
|
|
3206
|
-
if (!geo || !isNumber(geo.latitude) || !isNumber(geo.longitude)) {
|
|
3207
|
-
return NaN;
|
|
3208
|
-
}
|
|
3209
|
-
const lng = geo.longitude;
|
|
3210
|
-
const tz = options?.timeZone ?? (isReference(coords) ? coords.timezone ?? coords.tz : void 0) ?? geo.timezone;
|
|
3211
|
-
const dateVal = options?.date ?? (isReference(coords) ? extractEpochMs(coords) : void 0);
|
|
3212
|
-
const epochMs = isDefined(dateVal) ? extractEpochMs(dateVal) : Date.now();
|
|
3213
|
-
if (!isNumber(epochMs))
|
|
3214
|
-
return NaN;
|
|
3215
|
-
const offsetMinutes = isText(tz) ? resolveCivilTimezoneOffset(tz, epochMs) : void 0;
|
|
3216
|
-
const refMeridian = offsetMinutes !== void 0 ? offsetMinutes / 60 * 15 : Math.round(lng / 15) * 15;
|
|
3217
|
-
const deltaLng = lng - refMeridian;
|
|
3218
|
-
let offsetMin = deltaLng * 4;
|
|
3219
|
-
if (options?.apparent) {
|
|
3220
|
-
const d = new Date(epochMs);
|
|
3221
|
-
const startOfYear = Date.UTC(d.getUTCFullYear(), 0, 1);
|
|
3222
|
-
const dayOfYear = Math.floor((epochMs - startOfYear) / 864e5) + 1;
|
|
3223
|
-
const gamma = 2 * Math.PI / 365 * (dayOfYear - 1);
|
|
3224
|
-
const eqTime = 229.18 * (75e-6 + 1868e-6 * Math.cos(gamma) - 0.032077 * Math.sin(gamma) - 0.014615 * Math.cos(2 * gamma) - 0.040849 * Math.sin(2 * gamma));
|
|
3225
|
-
offsetMin += eqTime;
|
|
3226
|
-
}
|
|
3227
|
-
const unit = options?.unit ?? "mi";
|
|
3228
|
-
let res;
|
|
3229
|
-
if (unit === "ss" || unit === "seconds") {
|
|
3230
|
-
res = offsetMin * 60;
|
|
3231
|
-
} else if (unit === "hh" || unit === "hours") {
|
|
3232
|
-
res = offsetMin / 60;
|
|
3233
|
-
} else {
|
|
3234
|
-
res = offsetMin;
|
|
3235
|
-
}
|
|
3236
|
-
const precision = options?.precision ?? 2;
|
|
3237
|
-
const factor = Math.pow(10, precision);
|
|
3238
|
-
return Math.round(res * factor) / factor;
|
|
3239
|
-
}
|
|
3240
|
-
function extractEpochMs(input) {
|
|
3241
|
-
if (isNullish(input))
|
|
3242
|
-
return void 0;
|
|
3243
|
-
if (isNumber(input))
|
|
3244
|
-
return input;
|
|
3245
|
-
if (isDate(input))
|
|
3246
|
-
return input.getTime();
|
|
3247
|
-
if (isReference(input)) {
|
|
3248
|
-
if (isNumber(input.epoch?.ms))
|
|
3249
|
-
return input.epoch.ms;
|
|
3250
|
-
if (isNumber(input.epochMilliseconds))
|
|
3251
|
-
return input.epochMilliseconds;
|
|
3252
|
-
if (isNumber(input.timestamp))
|
|
3253
|
-
return input.timestamp;
|
|
3254
|
-
if (isDate(input.date))
|
|
3255
|
-
return input.date.getTime();
|
|
3256
|
-
if (isFunction(input.toInstant)) {
|
|
3257
|
-
try {
|
|
3258
|
-
return input.toInstant().epochMilliseconds;
|
|
3259
|
-
} catch {
|
|
3312
|
+
const shouldReverse = effectiveOpts.reverse === true || effectiveOpts.autoReverse === true;
|
|
3313
|
+
if (shouldReverse && (!base.city || !base.country)) {
|
|
3314
|
+
const reversed = await reverseGeocode(base, effectiveOpts);
|
|
3315
|
+
if (reversed) {
|
|
3316
|
+
return {
|
|
3317
|
+
...reversed,
|
|
3318
|
+
...base,
|
|
3319
|
+
city: isText(base.city) ? base.city : reversed.city,
|
|
3320
|
+
country: isText(base.country) ? base.country : reversed.country,
|
|
3321
|
+
lat: coords.lat,
|
|
3322
|
+
lng: coords.lng,
|
|
3323
|
+
latitude: coords.lat,
|
|
3324
|
+
longitude: coords.lng,
|
|
3325
|
+
sphere
|
|
3326
|
+
};
|
|
3260
3327
|
}
|
|
3261
3328
|
}
|
|
3262
|
-
|
|
3263
|
-
try {
|
|
3264
|
-
return input.getTime();
|
|
3265
|
-
} catch {
|
|
3266
|
-
}
|
|
3267
|
-
}
|
|
3268
|
-
if (isString(input)) {
|
|
3269
|
-
const parsed = Date.parse(input);
|
|
3270
|
-
if (isNumber(parsed))
|
|
3271
|
-
return parsed;
|
|
3329
|
+
return base;
|
|
3272
3330
|
}
|
|
3273
|
-
return
|
|
3274
|
-
}
|
|
3275
|
-
function calculateBearing(from, to, options) {
|
|
3276
|
-
const coords = toRadianCoordinates(from, to);
|
|
3277
|
-
if (!coords)
|
|
3278
|
-
return NaN;
|
|
3279
|
-
const { lat1, lat2, dLng } = coords;
|
|
3280
|
-
const y = Math.sin(dLng) * Math.cos(lat2);
|
|
3281
|
-
const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLng);
|
|
3282
|
-
const bearing = (Math.atan2(y, x) * 180 / Math.PI + 360) % 360;
|
|
3283
|
-
const precision = options?.precision ?? 1;
|
|
3284
|
-
const factor = Math.pow(10, precision);
|
|
3285
|
-
return Math.round(bearing * factor) / factor;
|
|
3286
|
-
}
|
|
3287
|
-
function calculateMidpoint(from, to) {
|
|
3288
|
-
const coords = toRadianCoordinates(from, to);
|
|
3289
|
-
if (!coords)
|
|
3290
|
-
return void 0;
|
|
3291
|
-
const { lat1, lat2, lng1, dLng } = coords;
|
|
3292
|
-
const Bx = Math.cos(lat2) * Math.cos(dLng);
|
|
3293
|
-
const By = Math.cos(lat2) * Math.sin(dLng);
|
|
3294
|
-
const latMidRad = Math.atan2(Math.sin(lat1) + Math.sin(lat2), Math.sqrt((Math.cos(lat1) + Bx) ** 2 + By ** 2));
|
|
3295
|
-
const lngMidRad = lng1 + Math.atan2(By, Math.cos(lat1) + Bx);
|
|
3296
|
-
const midLat = Math.round(latMidRad * 180 / Math.PI * 1e3) / 1e3;
|
|
3297
|
-
const midLng = Math.round(((lngMidRad * 180 / Math.PI + 540) % 360 - 180) * 1e3) / 1e3;
|
|
3298
|
-
const sphere = midLat > 1e-3 ? "north" : midLat < -1e-3 ? "south" : "equator";
|
|
3299
|
-
return {
|
|
3300
|
-
latitude: midLat,
|
|
3301
|
-
longitude: midLng,
|
|
3302
|
-
sphere
|
|
3303
|
-
};
|
|
3304
|
-
}
|
|
3305
|
-
function calculateVelocity(from, to, options) {
|
|
3306
|
-
const opts = isString(options) ? { unit: options } : options ?? {};
|
|
3307
|
-
const unit = opts.unit ?? "km";
|
|
3308
|
-
const timeUnit = opts.timeUnit ?? "hh";
|
|
3309
|
-
const dist = haversineDistance(from, to, unit);
|
|
3310
|
-
if (isNaN(dist))
|
|
3311
|
-
return NaN;
|
|
3312
|
-
const t1 = extractEpochMs(from);
|
|
3313
|
-
const t2 = extractEpochMs(to);
|
|
3314
|
-
if (!isNumber(t1) || !isNumber(t2))
|
|
3315
|
-
return NaN;
|
|
3316
|
-
const deltaMs = Math.abs(t2 - t1);
|
|
3317
|
-
if (deltaMs === 0) {
|
|
3318
|
-
return dist === 0 ? 0 : Infinity;
|
|
3319
|
-
}
|
|
3320
|
-
let deltaUnits;
|
|
3321
|
-
if (timeUnit === "ss" || timeUnit === "s" || timeUnit === "seconds" || timeUnit === "second") {
|
|
3322
|
-
deltaUnits = deltaMs / 1e3;
|
|
3323
|
-
} else if (timeUnit === "mi" || timeUnit === "m" || timeUnit === "minutes" || timeUnit === "minute") {
|
|
3324
|
-
deltaUnits = deltaMs / 6e4;
|
|
3325
|
-
} else {
|
|
3326
|
-
deltaUnits = deltaMs / 36e5;
|
|
3327
|
-
}
|
|
3328
|
-
const velocity = dist / deltaUnits;
|
|
3329
|
-
const precision = opts.precision ?? 2;
|
|
3330
|
-
const factor = Math.pow(10, precision);
|
|
3331
|
-
return Math.round(velocity * factor) / factor;
|
|
3332
|
-
}
|
|
3333
|
-
function isImpossibleTravel(from, to, options) {
|
|
3334
|
-
const unit = options?.unit ?? "km";
|
|
3335
|
-
const defaultMax = unit === "miles" ? 560 : unit === "m" ? 250 : 900;
|
|
3336
|
-
const threshold = options?.maxSpeed ?? options?.maxCommercialSpeedKmH ?? defaultMax;
|
|
3337
|
-
const velocity = calculateVelocity(from, to, { unit, timeUnit: unit === "m" ? "ss" : "hh" });
|
|
3338
|
-
return velocity > threshold;
|
|
3339
|
-
}
|
|
3340
|
-
function isWithin(from, to, maxDistance, unit = "km") {
|
|
3341
|
-
if (!isNumber(maxDistance) || maxDistance < 0)
|
|
3342
|
-
return false;
|
|
3343
|
-
const c1 = extractRawCoords(from);
|
|
3344
|
-
const c2 = extractRawCoords(to);
|
|
3345
|
-
if (!c1 || !c2 || !isNumber(c1.lat) || !isNumber(c1.lng) || !isNumber(c2.lat) || !isNumber(c2.lng))
|
|
3346
|
-
return false;
|
|
3347
|
-
const toRad = Math.PI / 180;
|
|
3348
|
-
const lat1 = c1.lat * toRad;
|
|
3349
|
-
const lng1 = c1.lng * toRad;
|
|
3350
|
-
const lat2 = c2.lat * toRad;
|
|
3351
|
-
const lng2 = c2.lng * toRad;
|
|
3352
|
-
const dLat = lat2 - lat1;
|
|
3353
|
-
const dLng = lng2 - lng1;
|
|
3354
|
-
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
|
3355
|
-
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
3356
|
-
const radius = unit === "miles" ? 3958.7613 : unit === "m" ? 63710088e-1 : 6371.0088;
|
|
3357
|
-
const dist = radius * c;
|
|
3358
|
-
return dist <= maxDistance;
|
|
3359
|
-
}
|
|
3360
|
-
function inBoundingBox(coords, bbox) {
|
|
3361
|
-
const geo = extractRawCoords(coords);
|
|
3362
|
-
if (!geo || !isNumber(geo.lat) || !isNumber(geo.lng))
|
|
3363
|
-
return false;
|
|
3364
|
-
if (isNullish(bbox))
|
|
3365
|
-
return false;
|
|
3366
|
-
let minLat;
|
|
3367
|
-
let maxLat;
|
|
3368
|
-
let minLng;
|
|
3369
|
-
let maxLng;
|
|
3370
|
-
if (Array.isArray(bbox) && bbox.length >= 4) {
|
|
3371
|
-
minLat = bbox[0];
|
|
3372
|
-
minLng = bbox[1];
|
|
3373
|
-
maxLat = bbox[2];
|
|
3374
|
-
maxLng = bbox[3];
|
|
3375
|
-
} else if (isReference(bbox)) {
|
|
3376
|
-
const b = bbox;
|
|
3377
|
-
minLat = b.minLat ?? b.minLatitude;
|
|
3378
|
-
maxLat = b.maxLat ?? b.maxLatitude;
|
|
3379
|
-
minLng = b.minLng ?? b.minLongitude;
|
|
3380
|
-
maxLng = b.maxLng ?? b.maxLongitude;
|
|
3381
|
-
}
|
|
3382
|
-
if (!isNumber(minLat) || !isNumber(maxLat) || !isNumber(minLng) || !isNumber(maxLng))
|
|
3383
|
-
return false;
|
|
3384
|
-
const lat = geo.lat;
|
|
3385
|
-
const lng = geo.lng;
|
|
3386
|
-
if (lat < minLat || lat > maxLat)
|
|
3387
|
-
return false;
|
|
3388
|
-
return minLng <= maxLng ? lng >= minLng && lng <= maxLng : lng >= minLng || lng <= maxLng;
|
|
3389
|
-
}
|
|
3390
|
-
var COMMON_COUNTRY_NAMES = {
|
|
3391
|
-
australia: "AU",
|
|
3392
|
-
"united states": "US",
|
|
3393
|
-
"united kingdom": "GB",
|
|
3394
|
-
"great britain": "GB",
|
|
3395
|
-
france: "FR",
|
|
3396
|
-
germany: "DE",
|
|
3397
|
-
italy: "IT",
|
|
3398
|
-
spain: "ES",
|
|
3399
|
-
japan: "JP",
|
|
3400
|
-
china: "CN",
|
|
3401
|
-
canada: "CA",
|
|
3402
|
-
brazil: "BR",
|
|
3403
|
-
egypt: "EG",
|
|
3404
|
-
"saudi arabia": "SA",
|
|
3405
|
-
russia: "RU",
|
|
3406
|
-
india: "IN",
|
|
3407
|
-
mexico: "MX",
|
|
3408
|
-
"new zealand": "NZ",
|
|
3409
|
-
netherlands: "NL",
|
|
3410
|
-
switzerland: "CH",
|
|
3411
|
-
austria: "AT",
|
|
3412
|
-
belgium: "BE",
|
|
3413
|
-
sweden: "SE",
|
|
3414
|
-
norway: "NO",
|
|
3415
|
-
denmark: "DK",
|
|
3416
|
-
finland: "FI",
|
|
3417
|
-
poland: "PL",
|
|
3418
|
-
turkey: "TR",
|
|
3419
|
-
greece: "GR",
|
|
3420
|
-
israel: "IL",
|
|
3421
|
-
thailand: "TH",
|
|
3422
|
-
vietnam: "VN",
|
|
3423
|
-
indonesia: "ID",
|
|
3424
|
-
malaysia: "MY",
|
|
3425
|
-
philippines: "PH",
|
|
3426
|
-
"south africa": "ZA",
|
|
3427
|
-
"south korea": "KR",
|
|
3428
|
-
korea: "KR",
|
|
3429
|
-
ireland: "IE",
|
|
3430
|
-
portugal: "PT",
|
|
3431
|
-
argentina: "AR"
|
|
3432
|
-
};
|
|
3433
|
-
var COUNTRY_PRIMARY_LOCALES = {
|
|
3434
|
-
AU: "en-AU",
|
|
3435
|
-
US: "en-US",
|
|
3436
|
-
GB: "en-GB",
|
|
3437
|
-
CA: "en-CA",
|
|
3438
|
-
NZ: "en-NZ",
|
|
3439
|
-
IE: "en-IE",
|
|
3440
|
-
SA: "ar-SA",
|
|
3441
|
-
AE: "ar-AE",
|
|
3442
|
-
EG: "ar-EG",
|
|
3443
|
-
JP: "ja-JP",
|
|
3444
|
-
CN: "zh-CN",
|
|
3445
|
-
TW: "zh-TW",
|
|
3446
|
-
HK: "zh-HK",
|
|
3447
|
-
KR: "ko-KR",
|
|
3448
|
-
FR: "fr-FR",
|
|
3449
|
-
DE: "de-DE",
|
|
3450
|
-
IT: "it-IT",
|
|
3451
|
-
ES: "es-ES",
|
|
3452
|
-
MX: "es-MX",
|
|
3453
|
-
AR: "es-AR",
|
|
3454
|
-
BR: "pt-BR",
|
|
3455
|
-
PT: "pt-PT",
|
|
3456
|
-
RU: "ru-RU",
|
|
3457
|
-
IN: "hi-IN",
|
|
3458
|
-
NL: "nl-NL",
|
|
3459
|
-
SE: "sv-SE",
|
|
3460
|
-
NO: "nb-NO",
|
|
3461
|
-
DK: "da-DK",
|
|
3462
|
-
FI: "fi-FI",
|
|
3463
|
-
PL: "pl-PL",
|
|
3464
|
-
TR: "tr-TR",
|
|
3465
|
-
GR: "el-GR",
|
|
3466
|
-
IL: "he-IL",
|
|
3467
|
-
TH: "th-TH",
|
|
3468
|
-
VN: "vi-VN",
|
|
3469
|
-
ID: "id-ID",
|
|
3470
|
-
MY: "ms-MY",
|
|
3471
|
-
PH: "fil-PH",
|
|
3472
|
-
ZA: "en-ZA",
|
|
3473
|
-
CH: "de-CH",
|
|
3474
|
-
AT: "de-AT",
|
|
3475
|
-
BE: "nl-BE"
|
|
3331
|
+
return null;
|
|
3476
3332
|
};
|
|
3477
|
-
function resolveCulturalLocale(currentLocale, countryCode, mode = true) {
|
|
3478
|
-
if (mode === false || mode === "none" || mode === "off")
|
|
3479
|
-
return void 0;
|
|
3480
|
-
const toCode = (val) => {
|
|
3481
|
-
const trimmed = val.trim();
|
|
3482
|
-
if (trimmed.length === 2)
|
|
3483
|
-
return trimmed.toUpperCase();
|
|
3484
|
-
return COMMON_COUNTRY_NAMES[trimmed.toLowerCase()];
|
|
3485
|
-
};
|
|
3486
|
-
let country;
|
|
3487
|
-
if (Array.isArray(countryCode)) {
|
|
3488
|
-
for (const c of countryCode) {
|
|
3489
|
-
if (isString(c)) {
|
|
3490
|
-
const mapped = toCode(c);
|
|
3491
|
-
if (mapped) {
|
|
3492
|
-
country = mapped;
|
|
3493
|
-
break;
|
|
3494
|
-
}
|
|
3495
|
-
}
|
|
3496
|
-
}
|
|
3497
|
-
} else if (isString(countryCode)) {
|
|
3498
|
-
country = toCode(countryCode);
|
|
3499
|
-
}
|
|
3500
|
-
if (isString(mode) && mode !== "regional" && mode !== "region" && mode !== "native" && mode !== "full") {
|
|
3501
|
-
const loc2 = getLC(mode);
|
|
3502
|
-
return loc2?.baseName ?? cleanLocaleTag(mode);
|
|
3503
|
-
}
|
|
3504
|
-
if (!country)
|
|
3505
|
-
return void 0;
|
|
3506
|
-
if (mode === "native" || mode === "full") {
|
|
3507
|
-
const nativeTag = COUNTRY_PRIMARY_LOCALES[country] ?? `en-${country}`;
|
|
3508
|
-
const loc2 = getLC(nativeTag);
|
|
3509
|
-
return loc2?.baseName ?? nativeTag;
|
|
3510
|
-
}
|
|
3511
|
-
let baseLang = "en";
|
|
3512
|
-
let script;
|
|
3513
|
-
if (isString(currentLocale) && currentLocale.length > 0) {
|
|
3514
|
-
const loc2 = getLC(currentLocale);
|
|
3515
|
-
baseLang = loc2?.language ?? cleanLocaleTag(currentLocale)?.split("-")[0] ?? "en";
|
|
3516
|
-
if (loc2?.script)
|
|
3517
|
-
script = loc2.script;
|
|
3518
|
-
}
|
|
3519
|
-
const targetTag = script ? `${baseLang}-${script}-${country}` : `${baseLang}-${country}`;
|
|
3520
|
-
const loc = getLC(targetTag);
|
|
3521
|
-
return loc?.baseName ?? targetTag;
|
|
3522
|
-
}
|
|
3523
3333
|
|
|
3524
3334
|
// src/index.ts
|
|
3525
3335
|
init_mapper_library2();
|
|
3526
3336
|
init_mapper_library();
|
|
3527
|
-
|
|
3337
|
+
init_assertion_library();
|
|
3338
|
+
import {
|
|
3339
|
+
resolveCulturalLocale
|
|
3340
|
+
} from "@magmacomputing/tempo-fns";
|
|
3341
|
+
var NON_GEO_CONTROL_KEYS = [
|
|
3342
|
+
"setTimezone",
|
|
3343
|
+
"setLocale",
|
|
3344
|
+
"refresh",
|
|
3345
|
+
"ttl",
|
|
3346
|
+
"endpoint",
|
|
3347
|
+
"timeout",
|
|
3348
|
+
"maxBytes",
|
|
3349
|
+
"highAccuracy",
|
|
3350
|
+
"catch",
|
|
3351
|
+
"debug",
|
|
3352
|
+
"geo",
|
|
3353
|
+
"provider",
|
|
3354
|
+
"fallback",
|
|
3355
|
+
"reverse",
|
|
3356
|
+
"autoReverse"
|
|
3357
|
+
];
|
|
3528
3358
|
var GeoPlugin = definePlugin({
|
|
3529
3359
|
name: "geo",
|
|
3530
3360
|
install(TempoClass, options) {
|
|
@@ -3535,10 +3365,10 @@ var GeoPlugin = definePlugin({
|
|
|
3535
3365
|
const classOpts = installedClass.config?.pluginOptions?.geo;
|
|
3536
3366
|
const instanceOpts = instance?.config?.pluginOptions?.geo;
|
|
3537
3367
|
return {
|
|
3538
|
-
...
|
|
3539
|
-
...
|
|
3540
|
-
...
|
|
3541
|
-
...
|
|
3368
|
+
...isObject(options) ? options : {},
|
|
3369
|
+
...isObject(classOpts) ? classOpts : {},
|
|
3370
|
+
...isObject(instanceOpts) ? instanceOpts : {},
|
|
3371
|
+
...isObject(callSiteOpts) ? callSiteOpts : {}
|
|
3542
3372
|
};
|
|
3543
3373
|
};
|
|
3544
3374
|
if (!Object.hasOwn(installedClass, "geo")) {
|
|
@@ -3546,14 +3376,6 @@ var GeoPlugin = definePlugin({
|
|
|
3546
3376
|
lookup: (opts) => geoLookup(getEffectiveOptions(opts)),
|
|
3547
3377
|
resolve: (target, opts) => resolveGeoCoordinates(target, getEffectiveOptions(opts, target)),
|
|
3548
3378
|
coerce: coerceGeo,
|
|
3549
|
-
distance: haversineDistance,
|
|
3550
|
-
bearing: calculateBearing,
|
|
3551
|
-
midpoint: calculateMidpoint,
|
|
3552
|
-
velocity: calculateVelocity,
|
|
3553
|
-
isImpossibleTravel,
|
|
3554
|
-
isWithin,
|
|
3555
|
-
inBoundingBox,
|
|
3556
|
-
solarOffset,
|
|
3557
3379
|
stash: stashGeo,
|
|
3558
3380
|
clear: clearStashedGeo,
|
|
3559
3381
|
get: getStashedGeo,
|
|
@@ -3580,38 +3402,39 @@ var GeoPlugin = definePlugin({
|
|
|
3580
3402
|
const setLocale = effectiveOpts?.setLocale ?? true;
|
|
3581
3403
|
const coords = await resolveGeoCoordinates(this, effectiveOpts);
|
|
3582
3404
|
if (coords) {
|
|
3583
|
-
const existingGeo =
|
|
3405
|
+
const existingGeo = isObject(this.config.geo) ? this.config.geo : {};
|
|
3584
3406
|
const customKeys = {};
|
|
3585
3407
|
for (const key of Object.keys(existingGeo)) {
|
|
3586
|
-
if (
|
|
3408
|
+
if (isSafeKey(key) && !GEO_PROPERTIES.includes(key))
|
|
3587
3409
|
customKeys[key] = existingGeo[key];
|
|
3588
3410
|
}
|
|
3589
3411
|
const callSiteGeo = coerceGeo(opts) ?? {};
|
|
3590
|
-
if (
|
|
3412
|
+
if (isObject(opts)) {
|
|
3591
3413
|
for (const key of Object.keys(opts)) {
|
|
3592
|
-
if (
|
|
3414
|
+
if (isSafeKey(key) && !NON_GEO_CONTROL_KEYS.includes(key)) {
|
|
3593
3415
|
if (!GEO_PROPERTIES.includes(key))
|
|
3594
3416
|
customKeys[key] = opts[key];
|
|
3595
3417
|
}
|
|
3596
3418
|
}
|
|
3597
3419
|
}
|
|
3598
|
-
const preservedElevation =
|
|
3420
|
+
const preservedElevation = isNumber(coords?.elevation) ? coords.elevation : isNumber(existingGeo.elevation) ? existingGeo.elevation : void 0;
|
|
3599
3421
|
const mergedGeo = {
|
|
3600
3422
|
...customKeys,
|
|
3601
3423
|
...coords,
|
|
3602
|
-
...
|
|
3424
|
+
...isNumber(preservedElevation) ? { elevation: preservedElevation } : {},
|
|
3603
3425
|
...callSiteGeo
|
|
3604
3426
|
};
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
if (
|
|
3608
|
-
|
|
3609
|
-
|
|
3427
|
+
const lat = mergedGeo.latitude;
|
|
3428
|
+
const lng = mergedGeo.longitude;
|
|
3429
|
+
if (isNumber(lat)) mergedGeo.latitude = Math.round(lat * 1e3) / 1e3;
|
|
3430
|
+
if (isNumber(lng)) mergedGeo.longitude = Math.round(lng * 1e3) / 1e3;
|
|
3431
|
+
if (!mergedGeo.sphere && isNumber(lat))
|
|
3432
|
+
mergedGeo.sphere = lat > 1e-3 ? "north" : lat < -1e-3 ? "south" : "equator";
|
|
3610
3433
|
let instance = this;
|
|
3611
3434
|
const targetTz = mergedGeo.timezone;
|
|
3612
|
-
if (setTimezone &&
|
|
3435
|
+
if (setTimezone && isString(targetTz) && !isEmpty(targetTz))
|
|
3613
3436
|
instance = this.set({ timeZone: targetTz });
|
|
3614
|
-
const targetLocale = mergedGeo.country ||
|
|
3437
|
+
const targetLocale = mergedGeo.country || isString(setLocale) ? resolveCulturalLocale(this.locale ?? this.config.locale, mergedGeo.country, setLocale) : void 0;
|
|
3615
3438
|
return new TempoClass(instance, {
|
|
3616
3439
|
...instance.config,
|
|
3617
3440
|
...targetLocale ? { locale: targetLocale } : {},
|
|
@@ -3624,12 +3447,6 @@ var GeoPlugin = definePlugin({
|
|
|
3624
3447
|
TempoClass.prototype.geoLookup = async function(opts) {
|
|
3625
3448
|
return resolveGeoCoordinates(this, getEffectiveOptions(opts, this));
|
|
3626
3449
|
};
|
|
3627
|
-
TempoClass.prototype.geoDistance = function(other, unit) {
|
|
3628
|
-
return haversineDistance(this, other, unit);
|
|
3629
|
-
};
|
|
3630
|
-
TempoClass.prototype.geoSolarOffset = function(options2) {
|
|
3631
|
-
return solarOffset(this, options2);
|
|
3632
|
-
};
|
|
3633
3450
|
}
|
|
3634
3451
|
});
|
|
3635
3452
|
var geoPlugin = GeoPlugin;
|
|
@@ -3637,9 +3454,7 @@ var index_default = GeoPlugin;
|
|
|
3637
3454
|
export {
|
|
3638
3455
|
GEO_PROPERTIES,
|
|
3639
3456
|
GeoPlugin,
|
|
3640
|
-
|
|
3641
|
-
calculateMidpoint,
|
|
3642
|
-
calculateVelocity,
|
|
3457
|
+
NON_GEO_CONTROL_KEYS,
|
|
3643
3458
|
clearStashedGeo,
|
|
3644
3459
|
coerceGeo,
|
|
3645
3460
|
index_default as default,
|
|
@@ -3649,10 +3464,6 @@ export {
|
|
|
3649
3464
|
geoPlugin,
|
|
3650
3465
|
getGeoProvider,
|
|
3651
3466
|
getStashedGeo,
|
|
3652
|
-
haversineDistance,
|
|
3653
|
-
inBoundingBox,
|
|
3654
|
-
isImpossibleTravel,
|
|
3655
|
-
isWithin,
|
|
3656
3467
|
resolveCulturalLocale,
|
|
3657
3468
|
resolveGeoCoordinates,
|
|
3658
3469
|
reverseGeocode,
|
|
@@ -3660,6 +3471,5 @@ export {
|
|
|
3660
3471
|
serverGeoLocation,
|
|
3661
3472
|
serverMapHemisphere,
|
|
3662
3473
|
setGeoProvider,
|
|
3663
|
-
solarOffset,
|
|
3664
3474
|
stashGeo
|
|
3665
3475
|
};
|