@magmacomputing/tempo-plugin-geo 1.2.0 → 1.3.0

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/dist/install.js CHANGED
@@ -2774,8 +2774,8 @@ __export(sdk_exports, {
2774
2774
  __reExport(sdk_exports, sdk_star);
2775
2775
  import * as sdk_star from "@magmacomputing/tempo/plugin/sdk";
2776
2776
  import { definePlugin as originalPlugin, defineTerm as originalTerm } from "@magmacomputing/tempo/plugin/sdk";
2777
- var definePlugin = (config) => originalPlugin({ ...config, version: "1.2.0" });
2778
- var defineTerm = (config) => originalTerm({ ...config, version: "1.2.0" });
2777
+ var definePlugin = (config) => originalPlugin({ ...config, version: "1.3.0" });
2778
+ var defineTerm = (config) => originalTerm({ ...config, version: "1.3.0" });
2779
2779
 
2780
2780
  // ../../library/dist/common/runtime/mapper.library.js
2781
2781
  init_utility_library();
@@ -2794,6 +2794,7 @@ function evaluate(...values) {
2794
2794
  }
2795
2795
 
2796
2796
  // ../../library/dist/common/runtime/mapper.library.js
2797
+ init_international_library();
2797
2798
  var MAP_KEY2 = "_magma_geo_";
2798
2799
  var DEFAULT_GEO_TTL = 24 * 60 * 60 * 1e3;
2799
2800
  var GEO_PROPERTIES = [
@@ -2895,16 +2896,25 @@ var resolveCacheKey = (keyOrOpts) => {
2895
2896
  return trimmed.startsWith(MAP_KEY2) ? trimmed : `${MAP_KEY2}:${trimmed}`;
2896
2897
  }
2897
2898
  if (keyOrOpts && isObject(keyOrOpts)) {
2899
+ const provider = keyOrOpts.provider ?? activeGeoProvider;
2900
+ const providerPrefix = provider && isString(provider.name) && !isEmpty(provider.name) ? `provider:${provider.name}` : void 0;
2898
2901
  const k = keyOrOpts.key ?? keyOrOpts.ip ?? keyOrOpts.query;
2899
- if (isString(k) && !isEmpty(k))
2900
- return `${MAP_KEY2}:${k.trim()}`;
2902
+ if (isString(k) && !isEmpty(k)) {
2903
+ const trimmedKey = k.trim();
2904
+ return providerPrefix ? `${MAP_KEY2}:${providerPrefix}:${trimmedKey}` : `${MAP_KEY2}:${trimmedKey}`;
2905
+ }
2906
+ if (providerPrefix) {
2907
+ return `${MAP_KEY2}:${providerPrefix}`;
2908
+ }
2909
+ } else if (activeGeoProvider && isString(activeGeoProvider.name) && !isEmpty(activeGeoProvider.name)) {
2910
+ return `${MAP_KEY2}:provider:${activeGeoProvider.name}`;
2901
2911
  }
2902
2912
  return MAP_KEY2;
2903
2913
  };
2904
2914
  var getStashedGeo = (keyOrOpts) => {
2905
2915
  const cacheKey = resolveCacheKey(keyOrOpts);
2906
2916
  try {
2907
- const raw = getStorage(cacheKey) ?? (typeof localStorage !== "undefined" ? localStorage.getItem(cacheKey) : void 0);
2917
+ const raw = getStorage(cacheKey) ?? (isCallable(globalThis.localStorage?.getItem) ? globalThis.localStorage.getItem(cacheKey) : void 0);
2908
2918
  if (!raw)
2909
2919
  return void 0;
2910
2920
  const parsed = isString(raw) && (raw.startsWith("{") || raw.startsWith("[")) ? JSON.parse(raw) : raw;
@@ -2958,7 +2968,15 @@ var clearStashedGeo = (keyOrOpts) => {
2958
2968
  const cacheKey = resolveCacheKey(keyOrOpts);
2959
2969
  setStorage(cacheKey, void 0);
2960
2970
  };
2971
+ var activeGeoProvider;
2972
+ function setGeoProvider(provider) {
2973
+ activeGeoProvider = provider;
2974
+ }
2975
+ function getGeoProvider() {
2976
+ return activeGeoProvider;
2977
+ }
2961
2978
  var geoLookup = async (opts = {}) => {
2979
+ const provider = opts.provider ?? activeGeoProvider;
2962
2980
  const useCache = opts.refresh !== true;
2963
2981
  if (useCache) {
2964
2982
  const stashed = getStashedGeo(opts);
@@ -2974,26 +2992,45 @@ var geoLookup = async (opts = {}) => {
2974
2992
  };
2975
2993
  }
2976
2994
  }
2977
- const { type } = getContext();
2978
2995
  let res;
2979
- switch (type) {
2980
- case CONTEXT.Browser: {
2981
- const { geoLocation: geoLocation2 } = await Promise.resolve().then(() => (init_mapper_library(), mapper_library_exports));
2982
- const browserRes = await geoLocation2(opts);
2983
- if (browserRes.error)
2984
- return { error: browserRes.error };
2985
- const lat = browserRes.coords?.latitude;
2986
- const lng = browserRes.coords?.longitude;
2987
- res = { ...browserRes, lat, lng, latitude: lat, longitude: lng };
2988
- break;
2996
+ if (provider && isFunction(provider.lookup)) {
2997
+ try {
2998
+ const providerRes = await provider.lookup(opts);
2999
+ if (providerRes) {
3000
+ res = providerRes;
3001
+ if (providerRes.error && opts.fallback === false)
3002
+ return providerRes;
3003
+ } else if (opts.fallback === false) {
3004
+ return { error: "Provider returned null and fallback is disabled" };
3005
+ }
3006
+ } catch (err) {
3007
+ if (opts.fallback === false)
3008
+ return { error: err?.message ?? "Provider lookup failed" };
2989
3009
  }
2990
- case CONTEXT.WebWorker:
2991
- case CONTEXT.NodeJS:
2992
- case CONTEXT.Deno:
2993
- default: {
2994
- const { serverGeoLocation: serverGeoLocation2 } = await Promise.resolve().then(() => (init_mapper_library2(), mapper_library_exports2));
2995
- res = await serverGeoLocation2(opts);
2996
- break;
3010
+ }
3011
+ if (!res || res.error) {
3012
+ if (opts.fallback === false && (provider || res?.error))
3013
+ return res && res.error ? res : { error: "Geolocation resolution failed and fallback is disabled" };
3014
+ const { type } = getContext();
3015
+ switch (type) {
3016
+ case CONTEXT.Browser: {
3017
+ const { geoLocation: geoLocation2 } = await Promise.resolve().then(() => (init_mapper_library(), mapper_library_exports));
3018
+ const browserRes = await geoLocation2(opts);
3019
+ if (browserRes.error)
3020
+ return { error: browserRes.error };
3021
+ const lat = browserRes.coords?.latitude;
3022
+ const lng = browserRes.coords?.longitude;
3023
+ res = { ...browserRes, lat, lng, latitude: lat, longitude: lng };
3024
+ break;
3025
+ }
3026
+ case CONTEXT.WebWorker:
3027
+ case CONTEXT.NodeJS:
3028
+ case CONTEXT.Deno:
3029
+ default: {
3030
+ const { serverGeoLocation: serverGeoLocation2 } = await Promise.resolve().then(() => (init_mapper_library2(), mapper_library_exports2));
3031
+ res = await serverGeoLocation2(opts);
3032
+ break;
3033
+ }
2997
3034
  }
2998
3035
  }
2999
3036
  const rawLat = res.latitude ?? res.lat;
@@ -3023,6 +3060,21 @@ var geoLookup = async (opts = {}) => {
3023
3060
  }
3024
3061
  return res;
3025
3062
  };
3063
+ async function reverseGeocode(coords, opts) {
3064
+ const provider = opts?.provider ?? activeGeoProvider;
3065
+ if (provider && isFunction(provider.reverseGeocode)) {
3066
+ return provider.reverseGeocode(coords, opts);
3067
+ }
3068
+ const resolved = coerceGeo(coords);
3069
+ return resolved ?? null;
3070
+ }
3071
+ async function forwardGeocode(query, opts) {
3072
+ const provider = opts?.provider ?? activeGeoProvider;
3073
+ if (provider && isFunction(provider.forwardGeocode)) {
3074
+ return provider.forwardGeocode(query, opts);
3075
+ }
3076
+ return null;
3077
+ }
3026
3078
  var resolveGeoCoordinates = async (input, opts = {}) => {
3027
3079
  const coerced = coerceGeo(input);
3028
3080
  if (coerced && isNumber(coerced.latitude) && isNumber(coerced.longitude))
@@ -3053,35 +3105,80 @@ var resolveGeoCoordinates = async (input, opts = {}) => {
3053
3105
  }
3054
3106
  return null;
3055
3107
  };
3056
- function haversineDistance(from, to, unit = "km") {
3108
+ var extractRawCoords = (input) => {
3109
+ if (isNullish(input))
3110
+ return void 0;
3111
+ if (isString(input) && input.includes(",")) {
3112
+ const segments = input.split(",").map((s) => s.trim());
3113
+ if (segments.length >= 2 && segments[0] !== "" && segments[1] !== "") {
3114
+ const lat = Number(segments[0]);
3115
+ const lng = Number(segments[1]);
3116
+ if (isNumber(lat) && isNumber(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
3117
+ return { lat, lng };
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) => {
3057
3150
  const c1 = coerceGeo(from);
3058
3151
  const c2 = coerceGeo(to);
3059
- if (!c1 || !c2 || !isNumber(c1.latitude) || !isNumber(c1.longitude) || !isNumber(c2.latitude) || !isNumber(c2.longitude)) {
3060
- return NaN;
3061
- }
3152
+ if (!c1 || !c2 || !isNumber(c1.latitude) || !isNumber(c1.longitude) || !isNumber(c2.latitude) || !isNumber(c2.longitude))
3153
+ return void 0;
3062
3154
  const toRad = Math.PI / 180;
3063
3155
  const lat1 = c1.latitude * toRad;
3064
3156
  const lng1 = c1.longitude * toRad;
3065
3157
  const lat2 = c2.latitude * toRad;
3066
3158
  const lng2 = c2.longitude * toRad;
3067
- const dLat = lat2 - lat1;
3068
- const dLng = lng2 - lng1;
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;
3069
3173
  const a = Math.sin(dLat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLng / 2) ** 2;
3070
3174
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
3071
- let radius;
3072
- if (unit === "miles") {
3073
- radius = 3958.7613;
3074
- } else if (unit === "m") {
3075
- radius = 63710088e-1;
3076
- } else {
3077
- radius = 6371.0088;
3078
- }
3175
+ const radius = unit === "miles" ? 3958.7613 : unit === "m" ? 63710088e-1 : 6371.0088;
3079
3176
  const dist = radius * c;
3080
3177
  return unit === "m" ? Math.round(dist) : Math.round(dist * 1e3) / 1e3;
3081
3178
  }
3082
3179
  var resolveCivilTimezoneOffset = (tz, epochMs) => {
3083
3180
  try {
3084
- if (typeof Temporal !== "undefined" && typeof Temporal.Instant?.fromEpochMilliseconds === "function") {
3181
+ if (isDefined(globalThis.Temporal) && isFunction(globalThis.Temporal.Instant?.fromEpochMilliseconds)) {
3085
3182
  const zdt = Temporal.Instant.fromEpochMilliseconds(epochMs).toZonedDateTimeISO(tz);
3086
3183
  return zdt.offsetNanoseconds / 6e10;
3087
3184
  }
@@ -3106,14 +3203,14 @@ var resolveCivilTimezoneOffset = (tz, epochMs) => {
3106
3203
  };
3107
3204
  function solarOffset(coords, options) {
3108
3205
  const geo = coerceGeo(coords);
3109
- if (!geo || !isNumber(geo.latitude) || !isNumber(geo.longitude) || isNaN(geo.latitude) || isNaN(geo.longitude)) {
3206
+ if (!geo || !isNumber(geo.latitude) || !isNumber(geo.longitude)) {
3110
3207
  return NaN;
3111
3208
  }
3112
3209
  const lng = geo.longitude;
3113
3210
  const tz = options?.timeZone ?? (isReference(coords) ? coords.timezone ?? coords.tz : void 0) ?? geo.timezone;
3114
- const dateVal = options?.date ?? (isReference(coords) && isNumber(coords.epoch?.ms) ? coords.epoch.ms : Date.now());
3115
- const epochMs = typeof dateVal === "number" ? dateVal : isString(dateVal) ? Date.parse(dateVal) : isDate(dateVal) ? dateVal.getTime() : isReference(dateVal) && isNumber(dateVal?.epoch?.ms) ? dateVal.epoch.ms : Date.now();
3116
- if (!Number.isFinite(epochMs))
3211
+ const dateVal = options?.date ?? (isReference(coords) ? extractEpochMs(coords) : void 0);
3212
+ const epochMs = isDefined(dateVal) ? extractEpochMs(dateVal) : Date.now();
3213
+ if (!isNumber(epochMs))
3117
3214
  return NaN;
3118
3215
  const offsetMinutes = isText(tz) ? resolveCivilTimezoneOffset(tz, epochMs) : void 0;
3119
3216
  const refMeridian = offsetMinutes !== void 0 ? offsetMinutes / 60 * 15 : Math.round(lng / 15) * 15;
@@ -3127,11 +3224,11 @@ function solarOffset(coords, options) {
3127
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));
3128
3225
  offsetMin += eqTime;
3129
3226
  }
3130
- const unit = options?.unit ?? "minutes";
3227
+ const unit = options?.unit ?? "mi";
3131
3228
  let res;
3132
- if (unit === "seconds") {
3229
+ if (unit === "ss" || unit === "seconds") {
3133
3230
  res = offsetMin * 60;
3134
- } else if (unit === "hours") {
3231
+ } else if (unit === "hh" || unit === "hours") {
3135
3232
  res = offsetMin / 60;
3136
3233
  } else {
3137
3234
  res = offsetMin;
@@ -3140,23 +3237,308 @@ function solarOffset(coords, options) {
3140
3237
  const factor = Math.pow(10, precision);
3141
3238
  return Math.round(res * factor) / factor;
3142
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 {
3260
+ }
3261
+ }
3262
+ if (isFunction(input.getTime))
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;
3272
+ }
3273
+ return void 0;
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"
3476
+ };
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
+ }
3143
3523
 
3144
3524
  // src/index.ts
3145
3525
  init_mapper_library2();
3146
3526
  init_mapper_library();
3147
- init_assertion_library();
3527
+ import { isString as isString2, isNumber as isNumber2, isObject as isObject2, isEmpty as isEmpty2, isSafeKey as isSafeKey2 } from "@magmacomputing/tempo/library";
3148
3528
  var GeoPlugin = definePlugin({
3149
3529
  name: "geo",
3150
3530
  install(TempoClass, options) {
3151
3531
  const installedClass = TempoClass || this;
3532
+ if (options?.provider)
3533
+ setGeoProvider(options.provider);
3152
3534
  const getEffectiveOptions = (callSiteOpts, instance) => {
3153
3535
  const classOpts = installedClass.config?.pluginOptions?.geo;
3154
3536
  const instanceOpts = instance?.config?.pluginOptions?.geo;
3155
3537
  return {
3156
- ...isObject(options) ? options : {},
3157
- ...isObject(classOpts) ? classOpts : {},
3158
- ...isObject(instanceOpts) ? instanceOpts : {},
3159
- ...isObject(callSiteOpts) ? callSiteOpts : {}
3538
+ ...isObject2(options) ? options : {},
3539
+ ...isObject2(classOpts) ? classOpts : {},
3540
+ ...isObject2(instanceOpts) ? instanceOpts : {},
3541
+ ...isObject2(callSiteOpts) ? callSiteOpts : {}
3160
3542
  };
3161
3543
  };
3162
3544
  if (!Object.hasOwn(installedClass, "geo")) {
@@ -3165,10 +3547,20 @@ var GeoPlugin = definePlugin({
3165
3547
  resolve: (target, opts) => resolveGeoCoordinates(target, getEffectiveOptions(opts, target)),
3166
3548
  coerce: coerceGeo,
3167
3549
  distance: haversineDistance,
3550
+ bearing: calculateBearing,
3551
+ midpoint: calculateMidpoint,
3552
+ velocity: calculateVelocity,
3553
+ isImpossibleTravel,
3554
+ isWithin,
3555
+ inBoundingBox,
3168
3556
  solarOffset,
3169
3557
  stash: stashGeo,
3170
3558
  clear: clearStashedGeo,
3171
3559
  get: getStashedGeo,
3560
+ setProvider: setGeoProvider,
3561
+ getProvider: getGeoProvider,
3562
+ reverse: (coords, opts) => reverseGeocode(coords, getEffectiveOptions(opts)),
3563
+ forward: (query, opts) => forwardGeocode(query, getEffectiveOptions(opts)),
3172
3564
  server: serverGeoLocation,
3173
3565
  browser: geoLocation,
3174
3566
  get current() {
@@ -3185,41 +3577,44 @@ var GeoPlugin = definePlugin({
3185
3577
  TempoClass.prototype.geoLocate = async function(opts) {
3186
3578
  const effectiveOpts = getEffectiveOptions(opts, this);
3187
3579
  const setTimezone = effectiveOpts?.setTimezone !== false;
3580
+ const setLocale = effectiveOpts?.setLocale ?? true;
3188
3581
  const coords = await resolveGeoCoordinates(this, effectiveOpts);
3189
3582
  if (coords) {
3190
- const existingGeo = isObject(this.config.geo) ? this.config.geo : {};
3583
+ const existingGeo = isObject2(this.config.geo) ? this.config.geo : {};
3191
3584
  const customKeys = {};
3192
3585
  for (const key of Object.keys(existingGeo)) {
3193
- if (isSafeKey(key) && !GEO_PROPERTIES.includes(key))
3586
+ if (isSafeKey2(key) && !GEO_PROPERTIES.includes(key))
3194
3587
  customKeys[key] = existingGeo[key];
3195
3588
  }
3196
3589
  const callSiteGeo = coerceGeo(opts) ?? {};
3197
- if (isObject(opts)) {
3590
+ if (isObject2(opts)) {
3198
3591
  for (const key of Object.keys(opts)) {
3199
- if (isSafeKey(key) && !["setTimezone", "refresh", "ttl", "endpoint", "timeout", "catch", "debug", "geo"].includes(key)) {
3592
+ if (isSafeKey2(key) && !["setTimezone", "setLocale", "refresh", "ttl", "endpoint", "timeout", "catch", "debug", "geo", "provider", "fallback"].includes(key)) {
3200
3593
  if (!GEO_PROPERTIES.includes(key))
3201
3594
  customKeys[key] = opts[key];
3202
3595
  }
3203
3596
  }
3204
3597
  }
3205
- const preservedElevation = isNumber(coords?.elevation) ? coords.elevation : isNumber(existingGeo.elevation) ? existingGeo.elevation : void 0;
3598
+ const preservedElevation = isNumber2(coords?.elevation) ? coords.elevation : isNumber2(existingGeo.elevation) ? existingGeo.elevation : void 0;
3206
3599
  const mergedGeo = {
3207
3600
  ...customKeys,
3208
3601
  ...coords,
3209
- ...isNumber(preservedElevation) ? { elevation: preservedElevation } : {},
3602
+ ...isNumber2(preservedElevation) ? { elevation: preservedElevation } : {},
3210
3603
  ...callSiteGeo
3211
3604
  };
3212
- if (isNumber(mergedGeo.latitude)) mergedGeo.latitude = Math.round(mergedGeo.latitude * 1e3) / 1e3;
3213
- if (isNumber(mergedGeo.longitude)) mergedGeo.longitude = Math.round(mergedGeo.longitude * 1e3) / 1e3;
3214
- if (!mergedGeo.sphere && isNumber(mergedGeo.latitude)) {
3605
+ if (isNumber2(mergedGeo.latitude)) mergedGeo.latitude = Math.round(mergedGeo.latitude * 1e3) / 1e3;
3606
+ if (isNumber2(mergedGeo.longitude)) mergedGeo.longitude = Math.round(mergedGeo.longitude * 1e3) / 1e3;
3607
+ if (!mergedGeo.sphere && isNumber2(mergedGeo.latitude)) {
3215
3608
  mergedGeo.sphere = mergedGeo.latitude > 1e-3 ? "north" : mergedGeo.latitude < -1e-3 ? "south" : "equator";
3216
3609
  }
3217
3610
  let instance = this;
3218
3611
  const targetTz = mergedGeo.timezone;
3219
- if (setTimezone && isString(targetTz) && !isEmpty(targetTz))
3612
+ if (setTimezone && isString2(targetTz) && !isEmpty2(targetTz))
3220
3613
  instance = this.set({ timeZone: targetTz });
3614
+ const targetLocale = mergedGeo.country || isString2(setLocale) ? resolveCulturalLocale(this.locale ?? this.config.locale, mergedGeo.country, setLocale) : void 0;
3221
3615
  return new TempoClass(instance, {
3222
3616
  ...instance.config,
3617
+ ...targetLocale ? { locale: targetLocale } : {},
3223
3618
  geo: (0, sdk_exports.deepFreeze)(mergedGeo),
3224
3619
  ...mergedGeo.sphere ? { sphere: mergedGeo.sphere } : {}
3225
3620
  });
@@ -3245,18 +3640,29 @@ var index_default = GeoPlugin;
3245
3640
  export {
3246
3641
  GEO_PROPERTIES,
3247
3642
  GeoPlugin,
3643
+ calculateBearing,
3644
+ calculateMidpoint,
3645
+ calculateVelocity,
3248
3646
  clearStashedGeo,
3249
3647
  coerceGeo,
3250
3648
  index_default as default,
3649
+ forwardGeocode,
3251
3650
  geoLocation,
3252
3651
  geoLookup,
3253
3652
  geoPlugin,
3653
+ getGeoProvider,
3254
3654
  getStashedGeo,
3255
3655
  haversineDistance,
3656
+ inBoundingBox,
3657
+ isImpossibleTravel,
3658
+ isWithin,
3659
+ resolveCulturalLocale,
3256
3660
  resolveGeoCoordinates,
3661
+ reverseGeocode,
3257
3662
  serverGeoCoords,
3258
3663
  serverGeoLocation,
3259
3664
  serverMapHemisphere,
3665
+ setGeoProvider,
3260
3666
  solarOffset,
3261
3667
  stashGeo
3262
3668
  };