@ingissa/navcore-core 1.0.4 → 1.0.5
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/README.md +279 -136
- package/dist/{NavCore-DzTPBxIc.d.mts → NavCore-qf5_0qsR.d.mts} +50 -47
- package/dist/{NavCore-DzTPBxIc.d.ts → NavCore-qf5_0qsR.d.ts} +50 -47
- package/dist/core.d.mts +7 -23
- package/dist/core.d.ts +7 -23
- package/dist/core.js +2 -2
- package/dist/core.mjs +2 -2
- package/dist/index.d.mts +2 -65
- package/dist/index.d.ts +2 -65
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/dist/index.pro-C5J1xlzt.d.ts +535 -0
- package/dist/index.pro-Cg6394f_.d.mts +535 -0
- package/dist/pro.d.mts +2 -488
- package/dist/pro.d.ts +2 -488
- package/dist/pro.js +1 -1
- package/dist/pro.mjs +1 -1
- package/package.json +5 -5
package/dist/pro.d.ts
CHANGED
|
@@ -1,488 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ParallelRoadResolver
|
|
5
|
-
*
|
|
6
|
-
* First-class feature: resolve GPS ambiguity on parallel roads (e.g. dual carriageways,
|
|
7
|
-
* motorway + service road) and prevent U-turn snap.
|
|
8
|
-
*
|
|
9
|
-
* Extracts and extends the bearing penalty + inertia logic from RouteSnapper
|
|
10
|
-
* into a standalone, composable API that can be used independently.
|
|
11
|
-
*
|
|
12
|
-
* FEATURE: PARALLEL_ROAD_RESOLUTION + U_TURN_PROTECTION
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* const resolver = new ParallelRoadResolver({ bearingPenalty: 80, inertiaPenalty: 40 });
|
|
17
|
-
* const best = resolver.resolve(candidates, vehicleBearing, prevSegmentIndex);
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
interface SnapCandidate {
|
|
22
|
-
/** Route segment index */
|
|
23
|
-
segmentIndex: number;
|
|
24
|
-
/** Projected point on segment */
|
|
25
|
-
projectedCoord: Coordinate;
|
|
26
|
-
/** Raw distance to route in metres */
|
|
27
|
-
distanceMetres: number;
|
|
28
|
-
/** Start of segment */
|
|
29
|
-
segA: Coordinate;
|
|
30
|
-
/** End of segment */
|
|
31
|
-
segB: Coordinate;
|
|
32
|
-
}
|
|
33
|
-
interface ResolvedCandidate extends SnapCandidate {
|
|
34
|
-
/** Final score (lower is better) */
|
|
35
|
-
score: number;
|
|
36
|
-
/** Bearing delta that triggered U-turn protection (null if not triggered) */
|
|
37
|
-
bearingDeltaDeg: number | null;
|
|
38
|
-
/** Whether inertia penalty was applied */
|
|
39
|
-
inertiaPenaltyApplied: boolean;
|
|
40
|
-
}
|
|
41
|
-
interface ParallelRoadResolverOptions {
|
|
42
|
-
/**
|
|
43
|
-
* Penalty added to score when vehicle bearing mismatches segment bearing
|
|
44
|
-
* beyond `bearingMismatchThresholdDeg`. Default: 80m equivalent.
|
|
45
|
-
*/
|
|
46
|
-
bearingPenalty?: number;
|
|
47
|
-
/**
|
|
48
|
-
* Bearing mismatch threshold in degrees that activates the penalty.
|
|
49
|
-
* Default: 60°. Higher = more tolerant of U-turns.
|
|
50
|
-
*/
|
|
51
|
-
bearingMismatchThresholdDeg?: number;
|
|
52
|
-
/**
|
|
53
|
-
* Penalty added when the candidate segment is far from the previously
|
|
54
|
-
* snapped segment, preventing oscillation. Default: 40m equivalent.
|
|
55
|
-
*/
|
|
56
|
-
inertiaPenalty?: number;
|
|
57
|
-
/**
|
|
58
|
-
* Minimum segment jump (in index units) to trigger inertia penalty.
|
|
59
|
-
* Default: 3.
|
|
60
|
-
*/
|
|
61
|
-
inertiaSegmentDist?: number;
|
|
62
|
-
}
|
|
63
|
-
declare class ParallelRoadResolver {
|
|
64
|
-
private readonly options;
|
|
65
|
-
constructor(options?: ParallelRoadResolverOptions);
|
|
66
|
-
private warned;
|
|
67
|
-
/**
|
|
68
|
-
* Score and sort snap candidates — lowest score wins.
|
|
69
|
-
*
|
|
70
|
-
* @param candidates Raw snap candidates (distance-sorted or unsorted)
|
|
71
|
-
* @param vehicleBearing Current vehicle heading in degrees (null disables bearing check)
|
|
72
|
-
* @param prevSegmentIdx Previously snapped segment index (null disables inertia)
|
|
73
|
-
* @returns Candidates sorted by score ascending, with debug metadata attached
|
|
74
|
-
*/
|
|
75
|
-
resolve(candidates: SnapCandidate[], vehicleBearing?: number | null, prevSegmentIdx?: number | null): ResolvedCandidate[];
|
|
76
|
-
/**
|
|
77
|
-
* Resolve and return only the best candidate.
|
|
78
|
-
*/
|
|
79
|
-
resolveBest(candidates: SnapCandidate[], vehicleBearing?: number | null, prevSegmentIdx?: number | null): ResolvedCandidate | null;
|
|
80
|
-
private score;
|
|
81
|
-
/**
|
|
82
|
-
* Quick utility: detect if the vehicle is likely performing a U-turn
|
|
83
|
-
* based on the difference between current bearing and route segment bearing.
|
|
84
|
-
*
|
|
85
|
-
* @returns true if the bearing difference exceeds the configured threshold
|
|
86
|
-
*/
|
|
87
|
-
isLikelyUTurn(vehicleBearing: number, segA: Coordinate, segB: Coordinate): boolean;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Module 3: DeadReckoning
|
|
92
|
-
* Predicts vehicle position during GPS outages or stale fixes.
|
|
93
|
-
*/
|
|
94
|
-
|
|
95
|
-
interface DRAnchor {
|
|
96
|
-
coord: Coordinate;
|
|
97
|
-
idx: number;
|
|
98
|
-
ts: number;
|
|
99
|
-
}
|
|
100
|
-
interface DRPrediction {
|
|
101
|
-
predictedCoord: Coordinate;
|
|
102
|
-
isActive: boolean;
|
|
103
|
-
ageMs: number;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Given a snapped anchor on the route, extrapolate forward using speed and bearing.
|
|
107
|
-
* Note: This function is a pure utility; license enforcement is in DeadReckoningEngine.predict().
|
|
108
|
-
*/
|
|
109
|
-
declare function computeDeadReckoningOnRoute(anchor: DRAnchor | null, speed: number, bearing: number, now: number, maxAgeMs?: number): DRPrediction;
|
|
110
|
-
/**
|
|
111
|
-
* Stateful dead reckoning engine.
|
|
112
|
-
*
|
|
113
|
-
* FEATURE: DEAD_RECKONING (Pro license required)
|
|
114
|
-
* predict() is a no-op and returns isActive=false when no valid Pro license is present.
|
|
115
|
-
*/
|
|
116
|
-
declare class DeadReckoningEngine {
|
|
117
|
-
private anchor;
|
|
118
|
-
private readonly maxAgeMs;
|
|
119
|
-
private warned;
|
|
120
|
-
constructor(maxAgeMs?: number);
|
|
121
|
-
/** Set the anchor point (called when GPS is available and snapping succeeds) */
|
|
122
|
-
setAnchor(coord: Coordinate, idx: number, timestamp: number): void;
|
|
123
|
-
/**
|
|
124
|
-
* Predict position given current speed and bearing.
|
|
125
|
-
* Requires a valid Pro license — returns isActive=false without one.
|
|
126
|
-
*/
|
|
127
|
-
predict(speed: number, bearing: number, now: number): DRPrediction;
|
|
128
|
-
/** Clear the anchor (GPS restored) */
|
|
129
|
-
clearAnchor(): void;
|
|
130
|
-
getAnchor(): DRAnchor | null;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Module 4b: DeviationDetector
|
|
135
|
-
* Compound deviation detection using distance, bearing, and movement signals.
|
|
136
|
-
*
|
|
137
|
-
* FEATURE: COMPOUND_DEVIATION (Pro license required)
|
|
138
|
-
* evaluate() returns a neutral "no-deviation" signal without a valid Pro license.
|
|
139
|
-
*/
|
|
140
|
-
|
|
141
|
-
interface DeviationSignals {
|
|
142
|
-
/** Distance from GPS to nearest route segment (metres) */
|
|
143
|
-
distanceToRoute: number;
|
|
144
|
-
/** Whether the distance exceeds the dynamic corridor */
|
|
145
|
-
outsideCorridor: boolean;
|
|
146
|
-
/** Whether the vehicle bearing diverges from the route bearing */
|
|
147
|
-
bearingMismatch: boolean;
|
|
148
|
-
/** Whether the vehicle is moving away from the next segment */
|
|
149
|
-
movingAway: boolean;
|
|
150
|
-
/** Compound confirmed: outsideCorridor AND (bearingMismatch OR movingAway) */
|
|
151
|
-
confirmed: boolean;
|
|
152
|
-
}
|
|
153
|
-
interface ClosestSegment {
|
|
154
|
-
segmentIndex: number;
|
|
155
|
-
distance: number;
|
|
156
|
-
projection: Coordinate;
|
|
157
|
-
}
|
|
158
|
-
declare class DeviationDetector {
|
|
159
|
-
private warned;
|
|
160
|
-
/**
|
|
161
|
-
* Evaluate deviation signals for the current GPS position.
|
|
162
|
-
* Requires a valid Pro license — returns a neutral signal without one.
|
|
163
|
-
*/
|
|
164
|
-
evaluate(gps: Coordinate, gpsBearing: number | null, speedMps: number, closest: ClosestSegment, route: Coordinate[], corridor: number, prevDistToNextSegment: number | null): DeviationSignals;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Module 4a: DynamicCorridor
|
|
169
|
-
* Speed-adaptive corridor width calculation.
|
|
170
|
-
*/
|
|
171
|
-
/**
|
|
172
|
-
* Compute speed-adaptive corridor width in metres.
|
|
173
|
-
*
|
|
174
|
-
* Corridor widens with speed: BASE + speed * FACTOR, capped at MAX.
|
|
175
|
-
* GPS accuracy optionally widens the corridor further.
|
|
176
|
-
*/
|
|
177
|
-
declare function calculateDynamicCorridor(speedMps: number, gpsAccuracy?: number | null, baseMeters?: number, maxMeters?: number, speedFactor?: number, accuracyFactor?: number): number;
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* InstructionEditor — Fluent builder for NavInstruction arrays.
|
|
181
|
-
*
|
|
182
|
-
* Create, edit, and annotate navigation instructions for custom routes.
|
|
183
|
-
* Supports standard turn instructions and exam-specific annotations.
|
|
184
|
-
*
|
|
185
|
-
* @example
|
|
186
|
-
* ```typescript
|
|
187
|
-
* const editor = new InstructionEditor();
|
|
188
|
-
* editor
|
|
189
|
-
* .addTurn([2.347, 48.859], 'Turn left onto Rue de Rivoli', 42)
|
|
190
|
-
* .addExamPoint([2.350, 48.860], 'Check mirrors before changing lane', 50)
|
|
191
|
-
* .addCheckpoint([2.355, 48.861], 65);
|
|
192
|
-
*
|
|
193
|
-
* editor.attachToCumulative(routeGeometry);
|
|
194
|
-
* engine.setRoute(routeGeometry, editor.toArray());
|
|
195
|
-
* ```
|
|
196
|
-
*/
|
|
197
|
-
|
|
198
|
-
type InstructionType = 'turn' | 'exam_point' | 'checkpoint' | 'depart' | 'arrive' | 'custom';
|
|
199
|
-
interface RichInstruction extends NavInstruction {
|
|
200
|
-
/** Instruction category */
|
|
201
|
-
type: InstructionType;
|
|
202
|
-
/** Unique id for referencing in edit operations */
|
|
203
|
-
id: string;
|
|
204
|
-
/** Display text (same as NavInstruction.text) */
|
|
205
|
-
text: string;
|
|
206
|
-
/** Whether to suppress voice read-out */
|
|
207
|
-
silent?: boolean;
|
|
208
|
-
/** Exam-specific severity level */
|
|
209
|
-
severity?: 'info' | 'warning' | 'critical';
|
|
210
|
-
/** Custom metadata for integrators */
|
|
211
|
-
meta?: Record<string, unknown>;
|
|
212
|
-
}
|
|
213
|
-
declare class InstructionEditor {
|
|
214
|
-
private instructions;
|
|
215
|
-
/** Add a turn instruction at the given route geometry index */
|
|
216
|
-
addTurn(location: Coordinate, text: string, geometryIndex: number, opts?: {
|
|
217
|
-
silent?: boolean;
|
|
218
|
-
meta?: Record<string, unknown>;
|
|
219
|
-
}): this;
|
|
220
|
-
/** Add an exam-specific point (e.g. "Check mirrors", "Yield here") */
|
|
221
|
-
addExamPoint(location: Coordinate, text: string, geometryIndex: number, opts?: {
|
|
222
|
-
severity?: RichInstruction['severity'];
|
|
223
|
-
silent?: boolean;
|
|
224
|
-
meta?: Record<string, unknown>;
|
|
225
|
-
}): this;
|
|
226
|
-
/** Add a silent progress checkpoint (does not trigger voice) */
|
|
227
|
-
addCheckpoint(location: Coordinate, geometryIndex: number, meta?: Record<string, unknown>): this;
|
|
228
|
-
/** Add a departure instruction (auto-inserted at index 0) */
|
|
229
|
-
addDepart(location: Coordinate, text?: string): this;
|
|
230
|
-
/** Add an arrival instruction */
|
|
231
|
-
addArrive(location: Coordinate, text?: string): this;
|
|
232
|
-
/** Update any field of an instruction by id */
|
|
233
|
-
update(id: string, patch: Partial<Omit<RichInstruction, 'id'>>): this;
|
|
234
|
-
/** Remove an instruction by id */
|
|
235
|
-
remove(id: string): this;
|
|
236
|
-
/** Move an instruction to a new position in the list */
|
|
237
|
-
reorder(id: string, newIndex: number): this;
|
|
238
|
-
/** Sort instructions by geometryIndex ascending */
|
|
239
|
-
sort(): this;
|
|
240
|
-
/** Remove all instructions */
|
|
241
|
-
clear(): this;
|
|
242
|
-
/**
|
|
243
|
-
* Compute and attach `cumulativeOffset` and `bearingBefore`/`bearingAfter`
|
|
244
|
-
* from the route geometry. Call this after loading the route but before
|
|
245
|
-
* passing instructions to the engine.
|
|
246
|
-
*/
|
|
247
|
-
attachToCumulative(route: Coordinate[]): this;
|
|
248
|
-
/** Export as plain NavInstruction[] for use with engine.setRoute() */
|
|
249
|
-
toArray(): NavInstruction[];
|
|
250
|
-
/** Export rich instructions with all metadata */
|
|
251
|
-
toRichArray(): ReadonlyArray<RichInstruction>;
|
|
252
|
-
get size(): number;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* VoiceTriggerEngine
|
|
257
|
-
*
|
|
258
|
-
* Converts NavCore instruction events into timed voice cues.
|
|
259
|
-
* Handles deduplication, look-ahead windows, and priority levels.
|
|
260
|
-
* Consumer calls TTS — this engine only computes WHEN to speak.
|
|
261
|
-
*
|
|
262
|
-
* FEATURE: VOICE_TRIGGERS
|
|
263
|
-
*
|
|
264
|
-
* @example
|
|
265
|
-
* ```typescript
|
|
266
|
-
* const voice = new VoiceTriggerEngine({ earlyTriggerMeters: 150 });
|
|
267
|
-
* voice.setInstructions(instructions);
|
|
268
|
-
*
|
|
269
|
-
* engine.on('instruction', (instr) => {
|
|
270
|
-
* const cue = voice.onInstruction(instr, state);
|
|
271
|
-
* if (cue) speakText(cue.text);
|
|
272
|
-
* });
|
|
273
|
-
*
|
|
274
|
-
* // Or poll each GPS tick:
|
|
275
|
-
* const cue = voice.update(state);
|
|
276
|
-
* if (cue) speakText(cue.text);
|
|
277
|
-
* ```
|
|
278
|
-
*/
|
|
279
|
-
|
|
280
|
-
type VoicePriority = 'low' | 'normal' | 'high' | 'urgent';
|
|
281
|
-
interface VoiceCue {
|
|
282
|
-
/** The instruction that triggered this cue */
|
|
283
|
-
instruction: NavInstruction;
|
|
284
|
-
/** Text to pass to TTS */
|
|
285
|
-
text: string;
|
|
286
|
-
/** Distance to the instruction point in metres */
|
|
287
|
-
distanceMetres: number;
|
|
288
|
-
/** Priority hint for TTS queue management */
|
|
289
|
-
priority: VoicePriority;
|
|
290
|
-
/** Monotonic trigger ID — use this to deduplicate */
|
|
291
|
-
triggerId: string;
|
|
292
|
-
}
|
|
293
|
-
interface VoiceTriggerOptions {
|
|
294
|
-
/**
|
|
295
|
-
* Trigger voice this many metres before the instruction point.
|
|
296
|
-
* Default: 120m (comfortable at 50 km/h).
|
|
297
|
-
*/
|
|
298
|
-
earlyTriggerMeters?: number;
|
|
299
|
-
/**
|
|
300
|
-
* Stop trying to trigger if vehicle is already past this many metres.
|
|
301
|
-
* Default: 20m.
|
|
302
|
-
*/
|
|
303
|
-
lateTriggerMeters?: number;
|
|
304
|
-
/**
|
|
305
|
-
* Re-announce if user is still approaching after this distance.
|
|
306
|
-
* Set to 0 to disable re-announcement. Default: 300m.
|
|
307
|
-
*/
|
|
308
|
-
reannounceMeters?: number;
|
|
309
|
-
}
|
|
310
|
-
declare class VoiceTriggerEngine {
|
|
311
|
-
private instructions;
|
|
312
|
-
private triggeredIds;
|
|
313
|
-
private reannouncedIds;
|
|
314
|
-
private readonly options;
|
|
315
|
-
constructor(options?: VoiceTriggerOptions);
|
|
316
|
-
/** Load the instruction set (call after engine.setRoute) */
|
|
317
|
-
setInstructions(instructions: NavInstruction[]): void;
|
|
318
|
-
/**
|
|
319
|
-
* Poll on every GPS state update.
|
|
320
|
-
* Returns a `VoiceCue` when an instruction should be spoken, or null.
|
|
321
|
-
*/
|
|
322
|
-
update(state: NavCoreState): VoiceCue | null;
|
|
323
|
-
/**
|
|
324
|
-
* Called directly from engine's 'instruction' event.
|
|
325
|
-
* Always fires immediately regardless of distance window.
|
|
326
|
-
*/
|
|
327
|
-
onInstruction(instr: NavInstruction, state: NavCoreState): VoiceCue;
|
|
328
|
-
/** Reset triggered state (call on setRoute / new journey) */
|
|
329
|
-
reset(): void;
|
|
330
|
-
private instrId;
|
|
331
|
-
private makeCue;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* ETAEngine — Local ETA computation. Zero API calls.
|
|
336
|
-
*
|
|
337
|
-
* Computes Estimated Time of Arrival from current NavCore state using
|
|
338
|
-
* a rolling speed average. No server required — entirely offline.
|
|
339
|
-
*
|
|
340
|
-
* FEATURE: ETA
|
|
341
|
-
*
|
|
342
|
-
* @example
|
|
343
|
-
* ```typescript
|
|
344
|
-
* const eta = new ETAEngine({ speedWindowSize: 8 });
|
|
345
|
-
* engine.on('update', (state) => {
|
|
346
|
-
* const result = eta.update(state);
|
|
347
|
-
* console.log(`ETA: ${result.etaSeconds}s, ${result.distanceRemainingM.toFixed(0)}m`);
|
|
348
|
-
* });
|
|
349
|
-
* ```
|
|
350
|
-
*/
|
|
351
|
-
|
|
352
|
-
interface ETAResult {
|
|
353
|
-
/** ETA in seconds from now */
|
|
354
|
-
etaSeconds: number;
|
|
355
|
-
/** ETA as absolute timestamp (Date.now() + etaSeconds * 1000) */
|
|
356
|
-
etaMs: number;
|
|
357
|
-
/** Remaining distance in metres */
|
|
358
|
-
distanceRemainingM: number;
|
|
359
|
-
/** Rolling average speed in m/s */
|
|
360
|
-
averageSpeedMs: number;
|
|
361
|
-
/** Whether the estimate is reliable (enough speed samples + non-zero speed) */
|
|
362
|
-
isReliable: boolean;
|
|
363
|
-
}
|
|
364
|
-
interface ETAOptions {
|
|
365
|
-
/**
|
|
366
|
-
* Number of speed samples in the rolling average window.
|
|
367
|
-
* More samples = smoother but slower to react. Default: 6.
|
|
368
|
-
*/
|
|
369
|
-
speedWindowSize?: number;
|
|
370
|
-
/**
|
|
371
|
-
* Minimum speed (m/s) below which we use the last reliable speed
|
|
372
|
-
* rather than 0 (avoids Infinity ETA while stopped). Default: 0.5 m/s (~2 km/h).
|
|
373
|
-
*/
|
|
374
|
-
minimumSpeedMs?: number;
|
|
375
|
-
/**
|
|
376
|
-
* Speed to assume if no reliable samples yet. Default: 8.3 m/s (30 km/h).
|
|
377
|
-
*/
|
|
378
|
-
fallbackSpeedMs?: number;
|
|
379
|
-
}
|
|
380
|
-
declare class ETAEngine {
|
|
381
|
-
private readonly options;
|
|
382
|
-
private speedSamples;
|
|
383
|
-
private lastReliableSpeed;
|
|
384
|
-
constructor(options?: ETAOptions);
|
|
385
|
-
/**
|
|
386
|
-
* Feed a NavCore state snapshot and receive an ETA estimate.
|
|
387
|
-
* Call this on every GPS update tick.
|
|
388
|
-
*/
|
|
389
|
-
update(state: NavCoreState): ETAResult;
|
|
390
|
-
/** Reset the speed window (call on new route or significant GPS gap) */
|
|
391
|
-
reset(): void;
|
|
392
|
-
private rollingAverage;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* GeofencingEngine
|
|
397
|
-
*
|
|
398
|
-
* Circular and polygon geofences — zero server dependency.
|
|
399
|
-
* Uses haversine distance for circles and ray-casting for polygons.
|
|
400
|
-
*
|
|
401
|
-
* FEATURE: GEOFENCING
|
|
402
|
-
*
|
|
403
|
-
* @example
|
|
404
|
-
* ```typescript
|
|
405
|
-
* const geo = new GeofencingEngine({ dwellThresholdMs: 5000 });
|
|
406
|
-
* geo.addCircle('school-zone', 'School Zone', [2.347, 48.859], 50, { speedLimit: 30 });
|
|
407
|
-
* geo.addPolygon('exam-area', 'Exam District', [[2.34, 48.85], [2.36, 48.85], [2.36, 48.87], [2.34, 48.87]]);
|
|
408
|
-
*
|
|
409
|
-
* engine.on('update', ({ snappedCoord }) => {
|
|
410
|
-
* const events = geo.update(snappedCoord);
|
|
411
|
-
* for (const evt of events) {
|
|
412
|
-
* // evt.name is the human-readable zone name
|
|
413
|
-
* console.log(`${evt.type}: ${evt.name} (${evt.id})`);
|
|
414
|
-
* }
|
|
415
|
-
* });
|
|
416
|
-
* ```
|
|
417
|
-
*/
|
|
418
|
-
|
|
419
|
-
type GeofenceType = 'circle' | 'polygon';
|
|
420
|
-
type GeofenceEventType = 'enter' | 'exit' | 'dwell';
|
|
421
|
-
interface GeofenceEvent {
|
|
422
|
-
/** Geofence ID that triggered the event */
|
|
423
|
-
id: string;
|
|
424
|
-
/** Human-readable name of the geofence */
|
|
425
|
-
name: string;
|
|
426
|
-
type: GeofenceEventType;
|
|
427
|
-
/** Coordinate that triggered the event */
|
|
428
|
-
coord: Coordinate;
|
|
429
|
-
/** Milliseconds spent inside (for dwell events) */
|
|
430
|
-
dwellMs?: number;
|
|
431
|
-
/** Custom metadata passed at creation */
|
|
432
|
-
meta?: unknown;
|
|
433
|
-
}
|
|
434
|
-
interface ZoneDefinition {
|
|
435
|
-
id: string;
|
|
436
|
-
name: string;
|
|
437
|
-
type: 'circle' | 'polygon';
|
|
438
|
-
center?: Coordinate;
|
|
439
|
-
radiusMeters?: number;
|
|
440
|
-
coords?: Coordinate[];
|
|
441
|
-
meta?: unknown;
|
|
442
|
-
}
|
|
443
|
-
interface GeofencingOptions {
|
|
444
|
-
/**
|
|
445
|
-
* Milliseconds a vehicle must remain inside a geofence before a 'dwell' event fires.
|
|
446
|
-
* Default: 10000ms (10 seconds).
|
|
447
|
-
*/
|
|
448
|
-
dwellThresholdMs?: number;
|
|
449
|
-
/** Initial zones to register on creation */
|
|
450
|
-
zones?: ZoneDefinition[];
|
|
451
|
-
/** Callback for all geofence events */
|
|
452
|
-
onEvent?: (event: GeofenceEvent) => void;
|
|
453
|
-
}
|
|
454
|
-
declare class GeofencingEngine {
|
|
455
|
-
private fences;
|
|
456
|
-
private states;
|
|
457
|
-
private readonly options;
|
|
458
|
-
constructor(options?: GeofencingOptions);
|
|
459
|
-
/** Add a circular geofence. Returns the ID. */
|
|
460
|
-
addCircle(id: string, name: string, center: Coordinate, radiusM: number, meta?: unknown): string;
|
|
461
|
-
/**
|
|
462
|
-
* Add a polygon geofence. Coords are [lng, lat] pairs.
|
|
463
|
-
* The polygon is auto-closed (first == last not required).
|
|
464
|
-
*/
|
|
465
|
-
addPolygon(id: string, name: string, coords: Coordinate[], meta?: unknown): string;
|
|
466
|
-
/** Remove a geofence by ID */
|
|
467
|
-
remove(id: string): void;
|
|
468
|
-
/** Remove all geofences */
|
|
469
|
-
clear(): void;
|
|
470
|
-
/** List all registered geofence IDs */
|
|
471
|
-
listIds(): string[];
|
|
472
|
-
/**
|
|
473
|
-
* Feed the current coordinate and receive a (possibly empty) list of events.
|
|
474
|
-
* Call this on every NavCore state update.
|
|
475
|
-
*/
|
|
476
|
-
update(coord: Coordinate): GeofenceEvent[];
|
|
477
|
-
/** Check if a coordinate is currently inside a named geofence */
|
|
478
|
-
isInside(id: string, coord: Coordinate): boolean;
|
|
479
|
-
private contains;
|
|
480
|
-
/**
|
|
481
|
-
* Ray-casting point-in-polygon algorithm.
|
|
482
|
-
* Works in [lng, lat] space (sufficient for small areas; use projected
|
|
483
|
-
* coords for very large polygons spanning many degrees).
|
|
484
|
-
*/
|
|
485
|
-
private pointInPolygon;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
export { DeadReckoningEngine, DeviationDetector, ETAEngine, type ETAOptions, type ETAResult, type GeofenceEvent, type GeofenceEventType, type GeofenceType, GeofencingEngine, type GeofencingOptions, InstructionEditor, type InstructionType, ParallelRoadResolver, type ParallelRoadResolverOptions, type ResolvedCandidate, type RichInstruction, type SnapCandidate, type VoiceCue, type VoicePriority, VoiceTriggerEngine, type VoiceTriggerOptions, type ZoneDefinition, calculateDynamicCorridor, computeDeadReckoningOnRoute };
|
|
1
|
+
export { D as DeadReckoningEngine, a as DeviationDetector, E as ETAEngine, b as ETAOptions, c as ETAResult, G as GeofenceEvent, d as GeofenceEventType, e as GeofenceType, f as GeofencingEngine, g as GeofencingOptions, I as InstructionEditor, h as InstructionType, P as ParallelRoadResolver, i as ParallelRoadResolverOptions, R as ResolvedCandidate, j as RichInstruction, S as SnapCandidate, V as VoiceCue, k as VoicePriority, l as VoiceTriggerEngine, m as VoiceTriggerOptions, n as advancePredictor, o as applyBearingSmoothing, p as applyGpsCorrection, q as blendBearing, r as calculateDynamicCorridor, s as calculateRobustBearing, t as computeDeadReckoningOnRoute, u as getSpeedAdaptiveAlpha, v as pointAtOffset, w as validateProgressUpdate } from './index.pro-C5J1xlzt.js';
|
|
2
|
+
export { d as LifecycleContext, e as LifecycleEvent, i as NavInstruction, j as NavLifecycleState, k as NavigationFSM, O as OffRouteContext, l as OffRouteEvent, m as OffRouteFSM, n as OffRouteState, R as RejoinTarget, T as TempRoute, r as resolveNextInstruction } from './NavCore-qf5_0qsR.js';
|
package/dist/pro.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';function f(c,e,t,i){let n=(t-c)*(Math.PI/180),r=(i-e)*(Math.PI/180),o=Math.sin(n/2)*Math.sin(n/2)+Math.cos(c*(Math.PI/180))*Math.cos(t*(Math.PI/180))*Math.sin(r/2)*Math.sin(r/2);return 6371e3*(2*Math.atan2(Math.sqrt(o),Math.sqrt(1-o)))}function x(c,e,t,i){let n=i/6371e3,r=t*(Math.PI/180),o=c*(Math.PI/180),s=e*(Math.PI/180),a=Math.asin(Math.sin(o)*Math.cos(n)+Math.cos(o)*Math.sin(n)*Math.cos(r));return [((s+Math.atan2(Math.sin(r)*Math.sin(n)*Math.cos(o),Math.cos(n)-Math.sin(o)*Math.sin(a)))*180/Math.PI+540)%360-180,a*180/Math.PI]}var l={DEAD_RECKONING:"dead_reckoning",COMPOUND_DEVIATION:"compound_deviation",VOICE_TRIGGERS:"voice_triggers",ETA:"eta",GEOFENCING:"geofencing",NAV_AI:"nav_ai"};var h=class h{constructor(e){this.status="MISSING";this.payload=null;this.enabledFeatures=new Set;this.warnedFeatures=new Set;e&&(this.tryValidateSync(e)||this.validate(e)),this.promoteToActive();}static getActive(){return h.activeManager}static clearActive(){h.activeManager=null;}promoteToActive(){let e=h.activeManager;(e===null||e.status!=="VALID"||this.status==="VALID")&&(h.activeManager=this);}tryValidateSync(e){return e==="eyJ0IjoicHJvIiwiZXhwIjo0OTMyNzAzMTU2MDAwLCJiaWQiOiJkZXYuYnlwYXNzIiwiZiI6WyIqIl19.MEQCIH4E4QNu9PuVsXHSnYmcqpCLk4QitiIH9hhY0Zm+YO5gAiAE7X3c47YQLUj7WPSGKw9Y7W2kBUR5GCnOMBwdBsYGgg=="?typeof __DEV__<"u"&&__DEV__||typeof window<"u"&&(window.location?.hostname==="localhost"||window.location?.hostname==="127.0.0.1"||window.location?.hostname?.startsWith("192.168.")||window.location?.hostname?.endsWith(".local"))||typeof process<"u"&&(process.env?.NODE_ENV==="development"||process.env?.NODE_ENV==="test"||process.env?.VITEST==="true"||process.env?.JEST_WORKER_ID!==void 0)?(this.status="VALID",this.payload={t:"pro",exp:4932703156e3,bid:"dev.bypass",f:["*"]},this.extractFeatures(),console.log("\u{1F511} [NavCore] Development bypass key validated synchronously. Plan: pro"),true):(console.warn("[NavCore] Development bypass key is restricted to local development environments."),this.status="INVALID",this.payload=null,true):false}async validate(e){if(this.tryValidateSync(e))return this.status;try{let[t,i]=e.split(".");if(!t||!i)return this.status="INVALID",this.status;let n=atob(t);if(this.payload=JSON.parse(n),!this.payload||this.payload.exp<Date.now())return this.status="EXPIRED",this.status;if(!await this.verifySignature(t,i))return this.status="INVALID",this.status;if(this.payload?.bid==="dev.bypass"&&!(typeof __DEV__<"u"&&__DEV__||typeof window<"u"&&(window.location?.hostname==="localhost"||window.location?.hostname==="127.0.0.1"||window.location?.hostname?.startsWith("192.168.")||window.location?.hostname?.endsWith(".local"))||typeof process<"u"&&(process.env?.NODE_ENV==="development"||process.env?.NODE_ENV==="test"||process.env?.VITEST==="true"||process.env?.JEST_WORKER_ID!==void 0)))return console.warn("[NavCore] Development bypass key is restricted to local development environments."),this.status="INVALID",this.payload=null,this.status;this.status="VALID",this.extractFeatures(),this.promoteToActive(),console.log(`\u{1F511} [NavCore] License validation complete. Plan: ${this.isProEnabled()?"pro":"core"}`);}catch{this.status="INVALID";}return this.status}extractFeatures(){if(this.enabledFeatures.clear(),!!this.payload){if(Array.isArray(this.payload.f))for(let e of this.payload.f)this.enabledFeatures.add(e);this.payload.t==="pro"&&this.enabledFeatures.add("*");}}hasFeature(e){return this.status!=="VALID"?false:this.enabledFeatures.has("*")?true:this.enabledFeatures.has(e)}warnOnce(e){this.warnedFeatures.has(e)||(console.warn(`[NavCore] Feature '${e}' requires a Pro license`),this.warnedFeatures.add(e));}isProEnabled(){return this.hasFeature("*")}isAIEnabled(){return this.hasFeature(l.NAV_AI)}getStatus(){return this.status}getPayload(){return this.payload}getEnabledFeatures(){return Array.from(this.enabledFeatures)}async verifySignature(e,t){try{if(typeof crypto>"u"||!crypto.subtle)return !1;let n=new TextEncoder().encode(e),r=Uint8Array.from(atob(t),a=>a.charCodeAt(0)),o=Uint8Array.from(atob(h.PUBLIC_KEY_SPKI),a=>a.charCodeAt(0)),s=await crypto.subtle.importKey("spki",o,{name:"ECDSA",namedCurve:"P-256"},!0,["verify"]);return await crypto.subtle.verify({name:"ECDSA",hash:{name:"SHA-256"}},s,r,n)}catch{return false}}};h.activeManager=null,h.PUBLIC_KEY_SPKI="MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEk6onJmJkWtRFusiSS69dcdONX4xL2Snm4AZO+dYjSynfJiL4x7trEy80MImLftknBWT/qI0z7ox4Q7VNL3te6A==";var d=h;function D(c,e,t,i,n=5e3){if(!c||e<.5)return {predictedCoord:c?.coord??[0,0],isActive:false,ageMs:0};let r=i-c.ts;if(r>n||r<0)return {predictedCoord:c.coord,isActive:false,ageMs:r};let o=e*(r/1e3);return {predictedCoord:x(c.coord[1],c.coord[0],t,o),isActive:true,ageMs:r}}var M=class{constructor(e=5e3){this.anchor=null;this.warned=false;this.maxAgeMs=e;}setAnchor(e,t,i){this.anchor={coord:e,idx:t,ts:i};}predict(e,t,i){let n=d.getActive();return !n||!n.hasFeature(l.DEAD_RECKONING)?(this.warned||(n?n.warnOnce(l.DEAD_RECKONING):console.warn(`[NavCore] Feature '${l.DEAD_RECKONING}' requires a Pro license`),this.warned=true),{predictedCoord:this.anchor?.coord??[0,0],isActive:false,ageMs:0}):(this.warned=false,D(this.anchor,e,t,i,this.maxAgeMs))}clearAnchor(){this.anchor=null;}getAnchor(){return this.anchor}};function b(c){let e=c%360;return e>180?e-360:e<-180?e+360:e}function m(c,e){let t=c[1]*(Math.PI/180),i=c[0]*(Math.PI/180),n=e[1]*(Math.PI/180),r=e[0]*(Math.PI/180),o=Math.sin(r-i)*Math.cos(n),s=Math.cos(t)*Math.sin(n)-Math.sin(t)*Math.cos(n)*Math.cos(r-i);return (Math.atan2(o,s)*180/Math.PI+360)%360}var E=class{constructor(){this.warned=false;}evaluate(e,t,i,n,r,o,s){let a=d.getActive();if(!a||!a.hasFeature(l.COMPOUND_DEVIATION))return this.warned||(a?a.warnOnce(l.COMPOUND_DEVIATION):console.warn(`[NavCore] Feature '${l.COMPOUND_DEVIATION}' requires a Pro license`),this.warned=true),{distanceToRoute:n.distance,outsideCorridor:false,bearingMismatch:false,movingAway:false,confirmed:false};this.warned=false;let u=n.distance>o,p=false;if(t!==null&&n.segmentIndex<r.length-1){let v=m(r[n.segmentIndex],r[n.segmentIndex+1]);p=Math.abs(b(t-v))>90;}let g=false;if(s!==null&&i>1.5){let v=r[Math.min(n.segmentIndex+2,r.length-1)];g=f(e[1],e[0],v[1],v[0])>s+5;}let I=u&&(p||g);return {distanceToRoute:n.distance,outsideCorridor:u,bearingMismatch:p,movingAway:g,confirmed:I}}};function N(c,e=null,t=25,i=60,n=1.2,r=.5){let o=t+c*n;return e!==null&&e>0&&(o+=e*r),Math.min(o,i)}var _={bearingPenalty:80,bearingMismatchThresholdDeg:60,inertiaPenalty:40,inertiaSegmentDist:3},C=class{constructor(e){this.warned=false;this.options={..._,...e};}resolve(e,t=null,i=null){let n=d.getActive();return (!n||!n.isProEnabled())&&(this.warned||(console.warn("[NavCore] ParallelRoadResolver and U-turn protection require a Pro license. Falling back to core distance snapping."),this.warned=true)),e.map(r=>this.score(r,t,i)).sort((r,o)=>r.score-o.score)}resolveBest(e,t=null,i=null){return e.length===0?null:this.resolve(e,t,i)[0]??null}score(e,t,i){let n=e.distanceMetres,r=null,o=false,s=d.getActive(),a=s&&s.isProEnabled();if(t!==null&&a){let u=m(e.segA,e.segB),p=Math.abs(b(t-u));r=p,p>this.options.bearingMismatchThresholdDeg&&(n+=this.options.bearingPenalty);}return i!==null&&a&&Math.abs(e.segmentIndex-i)>=this.options.inertiaSegmentDist&&(n+=this.options.inertiaPenalty,o=true),{...e,score:n,bearingDeltaDeg:r,inertiaPenaltyApplied:o}}isLikelyUTurn(e,t,i){let n=m(t,i);return Math.abs(b(e-n))>this.options.bearingMismatchThresholdDeg}};var O={earlyTriggerMeters:120,lateTriggerMeters:20,reannounceMeters:300},A=class{constructor(e){this.instructions=[];this.triggeredIds=new Set;this.reannouncedIds=new Set;this.options={...O,...e};}setInstructions(e){this.instructions=[...e],this.triggeredIds.clear(),this.reannouncedIds.clear();}update(e){let t=d.getActive();if(!t||!t.hasFeature(l.VOICE_TRIGGERS))return t?t.warnOnce(l.VOICE_TRIGGERS):console.warn(`[NavCore] Feature '${l.VOICE_TRIGGERS}' requires a Pro license`),null;let{distanceToNextInstruction:i,nextInstructionIndex:n}=e;if(n===null||i===null||n>=this.instructions.length)return null;let r=this.instructions[n],o=this.instrId(n),s=i;if(this.triggeredIds.has(o))return this.options.reannounceMeters>0&&!this.reannouncedIds.has(o)&&s<=this.options.reannounceMeters&&s>this.options.earlyTriggerMeters?(this.reannouncedIds.add(o),this.makeCue(r,s,"low")):null;if(s>this.options.earlyTriggerMeters)return null;if(s<-this.options.lateTriggerMeters)return this.triggeredIds.add(o),null;this.triggeredIds.add(o);let a=s<30?"urgent":s<60?"high":"normal";return this.makeCue(r,s,a)}onInstruction(e,t){let i=d.getActive();if(!i||!i.hasFeature(l.VOICE_TRIGGERS))return i?i.warnOnce(l.VOICE_TRIGGERS):console.warn(`[NavCore] Feature '${l.VOICE_TRIGGERS}' requires a Pro license`),{instruction:e,text:"",distanceMetres:0,priority:"normal",triggerId:"bypass"};let n=t.distanceToNextInstruction??0;return this.makeCue(e,n,"high")}reset(){this.triggeredIds.clear(),this.reannouncedIds.clear();}instrId(e){let t=this.instructions[e];return `${e}_${t?.geometryIndex??0}`}makeCue(e,t,i){return {instruction:e,text:e.text??e.instruction??"",distanceMetres:Math.max(0,t),priority:i,triggerId:`${e.geometryIndex}_${t.toFixed(0)}`}}};var G={speedWindowSize:6,minimumSpeedMs:.5,fallbackSpeedMs:8.33},R=class{constructor(e){this.speedSamples=[];this.options={...G,...e},this.lastReliableSpeed=this.options.fallbackSpeedMs;}update(e){let t=d.getActive();if(!t||!t.hasFeature(l.ETA))return t?t.warnOnce(l.ETA):console.warn(`[NavCore] Feature '${l.ETA}' requires a Pro license`),{etaSeconds:0,etaMs:0,distanceRemainingM:0,averageSpeedMs:0,isReliable:false};let i=e.distanceToDestination??0,n=e.currentSpeed??0;this.speedSamples.push(n),this.speedSamples.length>this.options.speedWindowSize&&this.speedSamples.shift();let r=this.rollingAverage();r>=this.options.minimumSpeedMs&&(this.lastReliableSpeed=r);let o=r>=this.options.minimumSpeedMs?r:this.lastReliableSpeed,s=this.speedSamples.length>=Math.ceil(this.options.speedWindowSize/2)&&o>0&&i>0,a=o>0?i/o:0;return {etaSeconds:Math.round(a),etaMs:Date.now()+a*1e3,distanceRemainingM:i,averageSpeedMs:r,isReliable:s}}reset(){this.speedSamples=[],this.lastReliableSpeed=this.options.fallbackSpeedMs;}rollingAverage(){return this.speedSamples.length===0?0:this.speedSamples.reduce((t,i)=>t+i,0)/this.speedSamples.length}};var F={dwellThresholdMs:1e4},S=class{constructor(e){this.fences=new Map;this.states=new Map;if(this.options={...F,...e},this.options.zones)for(let t of this.options.zones)t.type==="circle"&&t.center&&t.radiusMeters?this.addCircle(t.id,t.name,t.center,t.radiusMeters,t.meta):t.type==="polygon"&&t.coords&&this.addPolygon(t.id,t.name,t.coords,t.meta);}addCircle(e,t,i,n,r){return this.fences.set(e,{kind:"circle",id:e,name:t,center:i,radiusM:n,meta:r}),this.states.set(e,{inside:false,enteredAtMs:null}),e}addPolygon(e,t,i,n){if(i.length<3)throw new Error("[Geofencing] Polygon requires at least 3 coordinates");return this.fences.set(e,{kind:"polygon",id:e,name:t,coords:i,meta:n}),this.states.set(e,{inside:false,enteredAtMs:null}),e}remove(e){this.fences.delete(e),this.states.delete(e);}clear(){this.fences.clear(),this.states.clear();}listIds(){return Array.from(this.fences.keys())}update(e){let t=d.getActive();if(!t||!t.hasFeature(l.GEOFENCING))return t?t.warnOnce(l.GEOFENCING):console.warn(`[NavCore] Feature '${l.GEOFENCING}' requires a Pro license`),[];if(!e)return [];let i=[],n=Date.now();for(let[r,o]of this.fences){let s=this.states.get(r),a=this.contains(o,e);if(!s.inside&&a){s.inside=true,s.enteredAtMs=n;let u={id:r,name:o.name,type:"enter",coord:e,meta:o.meta};i.push(u),this.options.onEvent?.(u);}else if(s.inside&&!a){s.inside=false;let u=s.enteredAtMs?n-s.enteredAtMs:0;s.enteredAtMs=null;let p={id:r,name:o.name,type:"exit",coord:e,dwellMs:u,meta:o.meta};i.push(p),this.options.onEvent?.(p);}else if(s.inside&&s.enteredAtMs!==null){let u=n-s.enteredAtMs;if(u>=this.options.dwellThresholdMs){let p={id:r,name:o.name,type:"dwell",coord:e,dwellMs:u,meta:o.meta};i.push(p),this.options.onEvent?.(p),s.enteredAtMs=n;}}}return i}isInside(e,t){let i=this.fences.get(e);return i?this.contains(i,t):false}contains(e,t){return e.kind==="circle"?f(t[1],t[0],e.center[1],e.center[0])<=e.radiusM:this.pointInPolygon(t,e.coords)}pointInPolygon(e,t){let[i,n]=e,r=false,o=t.length;for(let s=0,a=o-1;s<o;a=s++){let[u,p]=t[s],[g,I]=t[a];p>n!=I>n&&i<(g-u)*(n-p)/(I-p)+u&&(r=!r);}return r}};var V=0;function y(){return `instr_${Date.now()}_${++V}`}var w=class{constructor(){this.instructions=[];}addTurn(e,t,i,n){return this.instructions.push({id:y(),type:"turn",location:e,text:t,instruction:t,geometryIndex:i,silent:n?.silent,meta:n?.meta}),this}addExamPoint(e,t,i,n){return this.instructions.push({id:y(),type:"exam_point",location:e,text:t,instruction:t,geometryIndex:i,severity:n?.severity??"info",silent:n?.silent,meta:n?.meta}),this}addCheckpoint(e,t,i){return this.instructions.push({id:y(),type:"checkpoint",location:e,text:"",instruction:"",geometryIndex:t,silent:true,meta:i}),this}addDepart(e,t="Start navigation"){let i={id:y(),type:"depart",location:e,text:t,instruction:t,geometryIndex:0};return this.instructions.unshift(i),this}addArrive(e,t="You have arrived"){return this.instructions.push({id:y(),type:"arrive",location:e,text:t,instruction:t,geometryIndex:Number.MAX_SAFE_INTEGER}),this}update(e,t){let i=this.instructions.findIndex(n=>n.id===e);if(i===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);return this.instructions[i]={...this.instructions[i],...t},this}remove(e){let t=this.instructions.findIndex(i=>i.id===e);if(t===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);return this.instructions.splice(t,1),this}reorder(e,t){let i=this.instructions.findIndex(r=>r.id===e);if(i===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);let[n]=this.instructions.splice(i,1);return this.instructions.splice(Math.max(0,t),0,n),this}sort(){return this.instructions.sort((e,t)=>e.geometryIndex-t.geometryIndex),this}clear(){return this.instructions=[],this}attachToCumulative(e){let t=d.getActive();if(!t||!t.isProEnabled())return console.warn("[NavCore] InstructionEditor requires a Pro license. Fluent editing is disabled."),this;if(e.length<2)return this;let i=[0];for(let n=1;n<e.length;n++){let r=e[n-1],o=e[n];i.push(i[n-1]+f(r[1],r[0],o[1],o[0]));}for(let n of this.instructions){let r=Math.min(n.geometryIndex,e.length-1);n.cumulativeOffset=i[r]??0,r>0&&r<e.length-1&&(n.bearingBefore=m(e[r-1],e[r]),n.bearingAfter=m(e[r],e[r+1]));}return this}toArray(){let e=d.getActive();return !e||!e.isProEnabled()?(console.warn("[NavCore] InstructionEditor.toArray() requires a Pro license. Returning empty instruction set."),[]):this.instructions.map(({id:t,type:i,severity:n,meta:r,...o})=>o)}toRichArray(){return [...this.instructions]}get size(){return this.instructions.length}};exports.DeadReckoningEngine=M;exports.DeviationDetector=E;exports.ETAEngine=R;exports.GeofencingEngine=S;exports.InstructionEditor=w;exports.ParallelRoadResolver=C;exports.VoiceTriggerEngine=A;exports.calculateDynamicCorridor=N;exports.computeDeadReckoningOnRoute=D;
|
|
1
|
+
'use strict';function h(o,e,t,n){let i=(t-o)*(Math.PI/180),r=(n-e)*(Math.PI/180),s=Math.sin(i/2)*Math.sin(i/2)+Math.cos(o*(Math.PI/180))*Math.cos(t*(Math.PI/180))*Math.sin(r/2)*Math.sin(r/2);return 6371e3*(2*Math.atan2(Math.sqrt(s),Math.sqrt(1-s)))}function P(o,e,t,n){let i=n/6371e3,r=t*(Math.PI/180),s=o*(Math.PI/180),a=e*(Math.PI/180),c=Math.asin(Math.sin(s)*Math.cos(i)+Math.cos(s)*Math.sin(i)*Math.cos(r));return [((a+Math.atan2(Math.sin(r)*Math.sin(i)*Math.cos(s),Math.cos(i)-Math.sin(s)*Math.sin(c)))*180/Math.PI+540)%360-180,c*180/Math.PI]}function w(o,e,t,n,i=5e3){if(!o||e<.5)return {predictedCoord:o?.coord??[0,0],isActive:false,ageMs:0};let r=n-o.ts;if(r>i||r<0)return {predictedCoord:o.coord,isActive:false,ageMs:r};let s=e*(r/1e3);return {predictedCoord:P(o.coord[1],o.coord[0],t,s),isActive:true,ageMs:r}}var M=class{constructor(e=5e3){this.anchor=null;this.maxAgeMs=e;}setAnchor(e,t,n){this.anchor={coord:e,idx:t,ts:n};}predict(e,t,n){return w(this.anchor,e,t,n,this.maxAgeMs)}clearAnchor(){this.anchor=null;}getAnchor(){return this.anchor}};function p(o){let e=o%360;return e>180?e-360:e<-180?e+360:e}function d(o,e){let t=o[1]*(Math.PI/180),n=o[0]*(Math.PI/180),i=e[1]*(Math.PI/180),r=e[0]*(Math.PI/180),s=Math.sin(r-n)*Math.cos(i),a=Math.cos(t)*Math.sin(i)-Math.sin(t)*Math.cos(i)*Math.cos(r-n);return (Math.atan2(s,a)*180/Math.PI+360)%360}function G(o,e,t){let n=(i,r,s)=>i+(r-i)*s;return [n(o[0],e[0],t),n(o[1],e[1],t)]}var _=class{evaluate(e,t,n,i,r,s,a){let c=i.distance>s,u=false;if(t!==null&&i.segmentIndex<r.length-1){let m=d(r[i.segmentIndex],r[i.segmentIndex+1]);u=Math.abs(p(t-m))>90;}let l=false;if(a!==null&&n>1.5){let m=r[Math.min(i.segmentIndex+2,r.length-1)];l=h(e[1],e[0],m[1],m[0])>a+5;}let f=c&&(u||l);return {distanceToRoute:i.distance,outsideCorridor:c,bearingMismatch:u,movingAway:l,confirmed:f}}};function z(o,e=null,t=25,n=60,i=1.2,r=.5,s=null,a=1.4,c=40){let u=t+o*i;e!==null&&e>0&&(u+=e*r);let l=n;if(s!==null&&s>=0&&s<=c){let f=1-Math.max(0,s/c),m=1+(a-1)*f;u*=m,l=n*a;}return Math.min(u,l)}function U(o,e=.3){return o<1.5?e*.5:o>20?Math.min(.9,e*2):e}function L(o,e,t=3){let n=Math.min(e+t,o.length-1);return e>=o.length-1||e===n?e>0?d(o[e-1],o[e]):0:d(o[e],o[n])}function y(o,e,t){let n=p(e-o);return (o+t*n+360)%360}function $(o,e,t,n,i,r=.3){let s=U(n,r),a=L(e,t);if(i!==null&&n>1.5){let c=y(o,i,s*.3);return y(c,a,s)}return y(o,a,s)}function X(o,e,t,n,i){return e<o||e>o+30||n>i?o:e}var Y=20,Z=60,K=3;function Q(o,e,t,n,i,r,s,a=0,c=false){if(!e||e.length===0||t>=e.length)return {nextIdx:t,instruction:null,wasTriggered:false};let u=t;for(;u<e.length;){let R=e[u],x=o>R.geometryIndex+30,I=R.cumulativeOffset!==void 0&&s>R.cumulativeOffset+15;if(!x&&!I)break;u++;}if(u>=e.length)return {nextIdx:u,instruction:null,wasTriggered:false};let l=e[u],f=l.cumulativeOffset!==void 0?Math.max(0,l.cumulativeOffset-s):h(i[1],i[0],l.location[1],l.location[0]),m=Math.min(Z,Math.max(Y,a*K)),E=o<=l.geometryIndex+2,F=l.geometryIndex>o+n.length*.15,V=f<=m,B=u===e.length-1,j=c&&B?o>n.length*.8:true,H=V&&E&&!F&&j,D=false;if(H){let R=true,x=l.bearingAfter??(l.geometryIndex<n.length-1?d(n[l.geometryIndex],n[l.geometryIndex+1]):null),I=l.bearingBefore??(l.geometryIndex>0?d(n[l.geometryIndex-1],n[l.geometryIndex]):null);I!==null&&x!==null&&r!==null&&Math.abs(p(x-I))>30&&Math.abs(p(r-x))>55&&(R=false),R&&(D=true);}return {nextIdx:u,instruction:l,wasTriggered:D}}var ee=0;function b(){return `instr_${Date.now()}_${++ee}`}var T=class{constructor(){this.instructions=[];}addTurn(e,t,n,i){return this.instructions.push({id:b(),type:"turn",location:e,text:t,instruction:t,geometryIndex:n,silent:i?.silent,meta:i?.meta}),this}addExamPoint(e,t,n,i){return this.instructions.push({id:b(),type:"exam_point",location:e,text:t,instruction:t,geometryIndex:n,severity:i?.severity??"info",silent:i?.silent,meta:i?.meta}),this}addCheckpoint(e,t,n){return this.instructions.push({id:b(),type:"checkpoint",location:e,text:"",instruction:"",geometryIndex:t,silent:true,meta:n}),this}addDepart(e,t="Start navigation"){let n={id:b(),type:"depart",location:e,text:t,instruction:t,geometryIndex:0};return this.instructions.unshift(n),this}addArrive(e,t="You have arrived"){return this.instructions.push({id:b(),type:"arrive",location:e,text:t,instruction:t,geometryIndex:Number.MAX_SAFE_INTEGER}),this}update(e,t){let n=this.instructions.findIndex(i=>i.id===e);if(n===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);return this.instructions[n]={...this.instructions[n],...t},this}remove(e){let t=this.instructions.findIndex(n=>n.id===e);if(t===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);return this.instructions.splice(t,1),this}reorder(e,t){let n=this.instructions.findIndex(r=>r.id===e);if(n===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);let[i]=this.instructions.splice(n,1);return this.instructions.splice(Math.max(0,t),0,i),this}sort(){return this.instructions.sort((e,t)=>e.geometryIndex-t.geometryIndex),this}clear(){return this.instructions=[],this}attachToCumulative(e){if(e.length<2)return this;let t=[0];for(let n=1;n<e.length;n++){let i=e[n-1],r=e[n];t.push(t[n-1]+h(i[1],i[0],r[1],r[0]));}for(let n of this.instructions){let i=Math.min(n.geometryIndex,e.length-1);n.cumulativeOffset=t[i]??0,i>0&&i<e.length-1&&(n.bearingBefore=d(e[i-1],e[i]),n.bearingAfter=d(e[i],e[i+1]));}return this}toArray(){return this.instructions.map(({id:e,type:t,severity:n,meta:i,...r})=>r)}toRichArray(){return [...this.instructions]}get size(){return this.instructions.length}};function te(o,e,t){return o+e*t}function ne(o,e,t=.15){let n=e-o;return o+t*n}function ie(o,e,t){if(t.length===0)return [0,0];if(o<=0)return t[0];let n=e[e.length-1]??0;if(o>=n)return t[t.length-1];let i=0,r=e.length-1;for(;i<r-1;){let l=i+r>>1;e[l]<=o?i=l:r=l;}let s=e[i],c=e[r]-s;if(c===0)return t[i];let u=(o-s)/c;return G(t[i],t[r],u)}var O=class{constructor(){this._state="IDLE";this._context={resumeIndex:null};}get state(){return this._state}get context(){return {...this._context}}dispatch(e){switch(this._state){case "IDLE":e.type==="ROUTE_LOADED"&&(this._state="OVERVIEW"),e.type==="USER_START_NAVIGATION"&&(this._state="APPROACHING_ROUTE");break;case "OVERVIEW":e.type==="USER_START_NAVIGATION"&&(this._state="APPROACHING_ROUTE");break;case "APPROACHING_ROUTE":e.type==="ROUTE_JOINED"&&(this._state="ON_ROUTE",this._context.resumeIndex=e.data.index),e.type==="NAVIGATION_FINISHED"&&(this._state="IDLE");break;case "ON_ROUTE":e.type==="NAVIGATION_FINISHED"&&(this._state="IDLE"),e.type==="ROUTE_JOINED"&&(this._context.resumeIndex=e.data.index);break}return (e.type==="RESET"||e.type==="CANCEL_NAVIGATION")&&(this._state="IDLE",this._context={resumeIndex:null}),{state:this._state,context:{...this._context}}}reset(){this._state="IDLE",this._context={resumeIndex:null};}};var g={deviationSegmentIdx:null,deviationStartMs:null,tempRoute:null,rejoinTarget:null,lastRerouteMs:null,rejoinedAtMs:null,rejoinDwellFrames:0,failureReason:null},k=2e3,re=3,S=class{constructor(){this._state="ON_ROUTE";this._context={...g};}get state(){return this._state}get context(){return {...this._context}}dispatch(e,t){let n=t??Date.now();switch(this._state){case "ON_ROUTE":e.type==="DEVIATION_SUSPECTED"&&(this._state="DEVIATING",this._context.deviationSegmentIdx=e.deviationSegmentIdx,this._context.deviationStartMs=n);break;case "DEVIATING":e.type==="DEVIATION_CONFIRMED"&&(this._context.deviationStartMs!==null?n-this._context.deviationStartMs:0)>=k&&(this._state="OFF_ROUTE",this._context.deviationSegmentIdx=e.deviationSegmentIdx),(e.type==="RESUME"||e.type==="RESET")&&(this._state="ON_ROUTE",e.type==="RESET"?this._context={...g}:(this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null));break;case "OFF_ROUTE":e.type==="REROUTE_STARTED"&&(this._state="REROUTING",this._context.lastRerouteMs=n),e.type==="RESUME"&&(this._state="ON_ROUTE",this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null),e.type==="RESET"&&(this._state="ON_ROUTE",this._context={...g});break;case "REROUTING":e.type==="REROUTE_SUCCESS"&&(this._state="REJOINING",this._context.tempRoute=e.tempRoute,this._context.rejoinTarget=e.rejoinTarget),e.type==="REROUTE_FAILED"&&(this._state="FAILED",this._context.failureReason=e.reason);break;case "REJOINING":e.type==="REJOIN_DETECTED"&&this._context.rejoinDwellFrames++,(e.type==="REJOIN_CONFIRMED"||this._context.rejoinDwellFrames>=re)&&(this._state="REJOINED",this._context.rejoinedAtMs=n,this._context.rejoinDwellFrames=0),e.type==="RESUME"&&(this._state="ON_ROUTE",this._context.tempRoute=null,this._context.rejoinTarget=null,this._context.rejoinedAtMs=null,this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null,this._context.rejoinDwellFrames=0),e.type==="RESET"&&(this._state="ON_ROUTE",this._context={...g});break;case "REJOINED":e.type==="RESUME"&&(this._state="ON_ROUTE",this._context.tempRoute=null,this._context.rejoinTarget=null,this._context.rejoinedAtMs=null,this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null),e.type==="RESET"&&(this._state="ON_ROUTE",this._context={...g});break;case "FAILED":e.type==="RESUME"&&(this._state="ON_ROUTE",this._context.failureReason=null,this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null),e.type==="RESET"&&(this._state="ON_ROUTE",this._context={...g});break}return {state:this._state,context:{...this._context}}}reset(){this._state="ON_ROUTE",this._context={...g};}isDeviationConfirmed(e=Date.now()){return this._context.deviationStartMs?e-this._context.deviationStartMs>=k:false}};var oe={bearingPenalty:80,bearingMismatchThresholdDeg:60,inertiaPenalty:40,inertiaSegmentDist:3},C=class{constructor(e){this.options={...oe,...e};}resolve(e,t=null,n=null){return e.map(i=>this.score(i,t,n)).sort((i,r)=>i.score-r.score)}resolveBest(e,t=null,n=null){return e.length===0?null:this.resolve(e,t,n)[0]??null}score(e,t,n){let i=e.distanceMetres,r=null,s=false;if(t!==null){let a=d(e.segA,e.segB),c=Math.abs(p(t-a));r=c,c>this.options.bearingMismatchThresholdDeg&&(i+=this.options.bearingPenalty);}return n!==null&&Math.abs(e.segmentIndex-n)>=this.options.inertiaSegmentDist&&(i+=this.options.inertiaPenalty,s=true),{...e,score:i,bearingDeltaDeg:r,inertiaPenaltyApplied:s}}isLikelyUTurn(e,t,n){let i=d(t,n);return Math.abs(p(e-i))>this.options.bearingMismatchThresholdDeg}};var se={earlyTriggerMeters:120,lateTriggerMeters:20,reannounceMeters:300},v=class{constructor(e){this.instructions=[];this.triggeredIds=new Set;this.reannouncedIds=new Set;this.options={...se,...e};}setInstructions(e){this.instructions=[...e],this.triggeredIds.clear(),this.reannouncedIds.clear();}update(e){let{distanceToNextInstruction:t,nextInstructionIndex:n}=e;if(n===null||t===null||n>=this.instructions.length)return null;let i=this.instructions[n],r=this.instrId(n),s=t;if(this.triggeredIds.has(r))return this.options.reannounceMeters>0&&!this.reannouncedIds.has(r)&&s<=this.options.reannounceMeters&&s>this.options.earlyTriggerMeters?(this.reannouncedIds.add(r),this.makeCue(i,s,"low")):null;if(s>this.options.earlyTriggerMeters)return null;if(s<-this.options.lateTriggerMeters)return this.triggeredIds.add(r),null;this.triggeredIds.add(r);let a=s<30?"urgent":s<60?"high":"normal";return this.makeCue(i,s,a)}onInstruction(e,t){let n=t.distanceToNextInstruction??0;return this.makeCue(e,n,"high")}reset(){this.triggeredIds.clear(),this.reannouncedIds.clear();}instrId(e){let t=this.instructions[e];return `${e}_${t?.geometryIndex??0}`}makeCue(e,t,n){return {instruction:e,text:e.text??e.instruction??"",distanceMetres:Math.max(0,t),priority:n,triggerId:`${e.geometryIndex}_${t.toFixed(0)}`}}};var ae={speedWindowSize:6,minimumSpeedMs:.5,fallbackSpeedMs:8.33},A=class{constructor(e){this.speedSamples=[];this.options={...ae,...e},this.lastReliableSpeed=this.options.fallbackSpeedMs;}update(e){let t=e.distanceToDestination??0,n=e.currentSpeed??0;this.speedSamples.push(n),this.speedSamples.length>this.options.speedWindowSize&&this.speedSamples.shift();let i=this.rollingAverage();i>=this.options.minimumSpeedMs&&(this.lastReliableSpeed=i);let r=i>=this.options.minimumSpeedMs?i:this.lastReliableSpeed,s=this.speedSamples.length>=Math.ceil(this.options.speedWindowSize/2)&&r>0&&t>0,a=r>0?t/r:0;return {etaSeconds:Math.round(a),etaMs:Date.now()+a*1e3,distanceRemainingM:t,averageSpeedMs:i,isReliable:s}}reset(){this.speedSamples=[],this.lastReliableSpeed=this.options.fallbackSpeedMs;}rollingAverage(){return this.speedSamples.length===0?0:this.speedSamples.reduce((t,n)=>t+n,0)/this.speedSamples.length}};var ce={dwellThresholdMs:1e4},N=class{constructor(e){this.fences=new Map;this.states=new Map;if(this.options={...ce,...e},this.options.zones)for(let t of this.options.zones)t.type==="circle"&&t.center&&t.radiusMeters?this.addCircle(t.id,t.name,t.center,t.radiusMeters,t.meta):t.type==="polygon"&&t.coords&&this.addPolygon(t.id,t.name,t.coords,t.meta);}addCircle(e,t,n,i,r){return this.fences.set(e,{kind:"circle",id:e,name:t,center:n,radiusM:i,meta:r}),this.states.set(e,{inside:false,enteredAtMs:null}),e}addPolygon(e,t,n,i){if(n.length<3)throw new Error("[Geofencing] Polygon requires at least 3 coordinates");return this.fences.set(e,{kind:"polygon",id:e,name:t,coords:n,meta:i}),this.states.set(e,{inside:false,enteredAtMs:null}),e}remove(e){this.fences.delete(e),this.states.delete(e);}clear(){this.fences.clear(),this.states.clear();}listIds(){return Array.from(this.fences.keys())}update(e){if(!e)return [];let t=[],n=Date.now();for(let[i,r]of this.fences){let s=this.states.get(i),a=this.contains(r,e);if(!s.inside&&a){s.inside=true,s.enteredAtMs=n;let c={id:i,name:r.name,type:"enter",coord:e,meta:r.meta};t.push(c),this.options.onEvent?.(c);}else if(s.inside&&!a){s.inside=false;let c=s.enteredAtMs?n-s.enteredAtMs:0;s.enteredAtMs=null;let u={id:i,name:r.name,type:"exit",coord:e,dwellMs:c,meta:r.meta};t.push(u),this.options.onEvent?.(u);}else if(s.inside&&s.enteredAtMs!==null){let c=n-s.enteredAtMs;if(c>=this.options.dwellThresholdMs){let u={id:i,name:r.name,type:"dwell",coord:e,dwellMs:c,meta:r.meta};t.push(u),this.options.onEvent?.(u),s.enteredAtMs=n;}}}return t}isInside(e,t){let n=this.fences.get(e);return n?this.contains(n,t):false}contains(e,t){return e.kind==="circle"?h(t[1],t[0],e.center[1],e.center[0])<=e.radiusM:this.pointInPolygon(t,e.coords)}pointInPolygon(e,t){let[n,i]=e,r=false,s=t.length;for(let a=0,c=s-1;a<s;c=a++){let[u,l]=t[a],[f,m]=t[c];l>i!=m>i&&n<(f-u)*(i-l)/(m-l)+u&&(r=!r);}return r}};exports.DeadReckoningEngine=M;exports.DeviationDetector=_;exports.ETAEngine=A;exports.GeofencingEngine=N;exports.InstructionEditor=T;exports.NavigationFSM=O;exports.OffRouteFSM=S;exports.ParallelRoadResolver=C;exports.VoiceTriggerEngine=v;exports.advancePredictor=te;exports.applyBearingSmoothing=$;exports.applyGpsCorrection=ne;exports.blendBearing=y;exports.calculateDynamicCorridor=z;exports.calculateRobustBearing=L;exports.computeDeadReckoningOnRoute=w;exports.getSpeedAdaptiveAlpha=U;exports.pointAtOffset=ie;exports.resolveNextInstruction=Q;exports.validateProgressUpdate=X;
|
package/dist/pro.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function f(c,e,t,i){let n=(t-c)*(Math.PI/180),r=(i-e)*(Math.PI/180),o=Math.sin(n/2)*Math.sin(n/2)+Math.cos(c*(Math.PI/180))*Math.cos(t*(Math.PI/180))*Math.sin(r/2)*Math.sin(r/2);return 6371e3*(2*Math.atan2(Math.sqrt(o),Math.sqrt(1-o)))}function x(c,e,t,i){let n=i/6371e3,r=t*(Math.PI/180),o=c*(Math.PI/180),s=e*(Math.PI/180),a=Math.asin(Math.sin(o)*Math.cos(n)+Math.cos(o)*Math.sin(n)*Math.cos(r));return [((s+Math.atan2(Math.sin(r)*Math.sin(n)*Math.cos(o),Math.cos(n)-Math.sin(o)*Math.sin(a)))*180/Math.PI+540)%360-180,a*180/Math.PI]}var l={DEAD_RECKONING:"dead_reckoning",COMPOUND_DEVIATION:"compound_deviation",VOICE_TRIGGERS:"voice_triggers",ETA:"eta",GEOFENCING:"geofencing",NAV_AI:"nav_ai"};var h=class h{constructor(e){this.status="MISSING";this.payload=null;this.enabledFeatures=new Set;this.warnedFeatures=new Set;e&&(this.tryValidateSync(e)||this.validate(e)),this.promoteToActive();}static getActive(){return h.activeManager}static clearActive(){h.activeManager=null;}promoteToActive(){let e=h.activeManager;(e===null||e.status!=="VALID"||this.status==="VALID")&&(h.activeManager=this);}tryValidateSync(e){return e==="eyJ0IjoicHJvIiwiZXhwIjo0OTMyNzAzMTU2MDAwLCJiaWQiOiJkZXYuYnlwYXNzIiwiZiI6WyIqIl19.MEQCIH4E4QNu9PuVsXHSnYmcqpCLk4QitiIH9hhY0Zm+YO5gAiAE7X3c47YQLUj7WPSGKw9Y7W2kBUR5GCnOMBwdBsYGgg=="?typeof __DEV__<"u"&&__DEV__||typeof window<"u"&&(window.location?.hostname==="localhost"||window.location?.hostname==="127.0.0.1"||window.location?.hostname?.startsWith("192.168.")||window.location?.hostname?.endsWith(".local"))||typeof process<"u"&&(process.env?.NODE_ENV==="development"||process.env?.NODE_ENV==="test"||process.env?.VITEST==="true"||process.env?.JEST_WORKER_ID!==void 0)?(this.status="VALID",this.payload={t:"pro",exp:4932703156e3,bid:"dev.bypass",f:["*"]},this.extractFeatures(),console.log("\u{1F511} [NavCore] Development bypass key validated synchronously. Plan: pro"),true):(console.warn("[NavCore] Development bypass key is restricted to local development environments."),this.status="INVALID",this.payload=null,true):false}async validate(e){if(this.tryValidateSync(e))return this.status;try{let[t,i]=e.split(".");if(!t||!i)return this.status="INVALID",this.status;let n=atob(t);if(this.payload=JSON.parse(n),!this.payload||this.payload.exp<Date.now())return this.status="EXPIRED",this.status;if(!await this.verifySignature(t,i))return this.status="INVALID",this.status;if(this.payload?.bid==="dev.bypass"&&!(typeof __DEV__<"u"&&__DEV__||typeof window<"u"&&(window.location?.hostname==="localhost"||window.location?.hostname==="127.0.0.1"||window.location?.hostname?.startsWith("192.168.")||window.location?.hostname?.endsWith(".local"))||typeof process<"u"&&(process.env?.NODE_ENV==="development"||process.env?.NODE_ENV==="test"||process.env?.VITEST==="true"||process.env?.JEST_WORKER_ID!==void 0)))return console.warn("[NavCore] Development bypass key is restricted to local development environments."),this.status="INVALID",this.payload=null,this.status;this.status="VALID",this.extractFeatures(),this.promoteToActive(),console.log(`\u{1F511} [NavCore] License validation complete. Plan: ${this.isProEnabled()?"pro":"core"}`);}catch{this.status="INVALID";}return this.status}extractFeatures(){if(this.enabledFeatures.clear(),!!this.payload){if(Array.isArray(this.payload.f))for(let e of this.payload.f)this.enabledFeatures.add(e);this.payload.t==="pro"&&this.enabledFeatures.add("*");}}hasFeature(e){return this.status!=="VALID"?false:this.enabledFeatures.has("*")?true:this.enabledFeatures.has(e)}warnOnce(e){this.warnedFeatures.has(e)||(console.warn(`[NavCore] Feature '${e}' requires a Pro license`),this.warnedFeatures.add(e));}isProEnabled(){return this.hasFeature("*")}isAIEnabled(){return this.hasFeature(l.NAV_AI)}getStatus(){return this.status}getPayload(){return this.payload}getEnabledFeatures(){return Array.from(this.enabledFeatures)}async verifySignature(e,t){try{if(typeof crypto>"u"||!crypto.subtle)return !1;let n=new TextEncoder().encode(e),r=Uint8Array.from(atob(t),a=>a.charCodeAt(0)),o=Uint8Array.from(atob(h.PUBLIC_KEY_SPKI),a=>a.charCodeAt(0)),s=await crypto.subtle.importKey("spki",o,{name:"ECDSA",namedCurve:"P-256"},!0,["verify"]);return await crypto.subtle.verify({name:"ECDSA",hash:{name:"SHA-256"}},s,r,n)}catch{return false}}};h.activeManager=null,h.PUBLIC_KEY_SPKI="MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEk6onJmJkWtRFusiSS69dcdONX4xL2Snm4AZO+dYjSynfJiL4x7trEy80MImLftknBWT/qI0z7ox4Q7VNL3te6A==";var d=h;function D(c,e,t,i,n=5e3){if(!c||e<.5)return {predictedCoord:c?.coord??[0,0],isActive:false,ageMs:0};let r=i-c.ts;if(r>n||r<0)return {predictedCoord:c.coord,isActive:false,ageMs:r};let o=e*(r/1e3);return {predictedCoord:x(c.coord[1],c.coord[0],t,o),isActive:true,ageMs:r}}var M=class{constructor(e=5e3){this.anchor=null;this.warned=false;this.maxAgeMs=e;}setAnchor(e,t,i){this.anchor={coord:e,idx:t,ts:i};}predict(e,t,i){let n=d.getActive();return !n||!n.hasFeature(l.DEAD_RECKONING)?(this.warned||(n?n.warnOnce(l.DEAD_RECKONING):console.warn(`[NavCore] Feature '${l.DEAD_RECKONING}' requires a Pro license`),this.warned=true),{predictedCoord:this.anchor?.coord??[0,0],isActive:false,ageMs:0}):(this.warned=false,D(this.anchor,e,t,i,this.maxAgeMs))}clearAnchor(){this.anchor=null;}getAnchor(){return this.anchor}};function b(c){let e=c%360;return e>180?e-360:e<-180?e+360:e}function m(c,e){let t=c[1]*(Math.PI/180),i=c[0]*(Math.PI/180),n=e[1]*(Math.PI/180),r=e[0]*(Math.PI/180),o=Math.sin(r-i)*Math.cos(n),s=Math.cos(t)*Math.sin(n)-Math.sin(t)*Math.cos(n)*Math.cos(r-i);return (Math.atan2(o,s)*180/Math.PI+360)%360}var E=class{constructor(){this.warned=false;}evaluate(e,t,i,n,r,o,s){let a=d.getActive();if(!a||!a.hasFeature(l.COMPOUND_DEVIATION))return this.warned||(a?a.warnOnce(l.COMPOUND_DEVIATION):console.warn(`[NavCore] Feature '${l.COMPOUND_DEVIATION}' requires a Pro license`),this.warned=true),{distanceToRoute:n.distance,outsideCorridor:false,bearingMismatch:false,movingAway:false,confirmed:false};this.warned=false;let u=n.distance>o,p=false;if(t!==null&&n.segmentIndex<r.length-1){let v=m(r[n.segmentIndex],r[n.segmentIndex+1]);p=Math.abs(b(t-v))>90;}let g=false;if(s!==null&&i>1.5){let v=r[Math.min(n.segmentIndex+2,r.length-1)];g=f(e[1],e[0],v[1],v[0])>s+5;}let I=u&&(p||g);return {distanceToRoute:n.distance,outsideCorridor:u,bearingMismatch:p,movingAway:g,confirmed:I}}};function N(c,e=null,t=25,i=60,n=1.2,r=.5){let o=t+c*n;return e!==null&&e>0&&(o+=e*r),Math.min(o,i)}var _={bearingPenalty:80,bearingMismatchThresholdDeg:60,inertiaPenalty:40,inertiaSegmentDist:3},C=class{constructor(e){this.warned=false;this.options={..._,...e};}resolve(e,t=null,i=null){let n=d.getActive();return (!n||!n.isProEnabled())&&(this.warned||(console.warn("[NavCore] ParallelRoadResolver and U-turn protection require a Pro license. Falling back to core distance snapping."),this.warned=true)),e.map(r=>this.score(r,t,i)).sort((r,o)=>r.score-o.score)}resolveBest(e,t=null,i=null){return e.length===0?null:this.resolve(e,t,i)[0]??null}score(e,t,i){let n=e.distanceMetres,r=null,o=false,s=d.getActive(),a=s&&s.isProEnabled();if(t!==null&&a){let u=m(e.segA,e.segB),p=Math.abs(b(t-u));r=p,p>this.options.bearingMismatchThresholdDeg&&(n+=this.options.bearingPenalty);}return i!==null&&a&&Math.abs(e.segmentIndex-i)>=this.options.inertiaSegmentDist&&(n+=this.options.inertiaPenalty,o=true),{...e,score:n,bearingDeltaDeg:r,inertiaPenaltyApplied:o}}isLikelyUTurn(e,t,i){let n=m(t,i);return Math.abs(b(e-n))>this.options.bearingMismatchThresholdDeg}};var O={earlyTriggerMeters:120,lateTriggerMeters:20,reannounceMeters:300},A=class{constructor(e){this.instructions=[];this.triggeredIds=new Set;this.reannouncedIds=new Set;this.options={...O,...e};}setInstructions(e){this.instructions=[...e],this.triggeredIds.clear(),this.reannouncedIds.clear();}update(e){let t=d.getActive();if(!t||!t.hasFeature(l.VOICE_TRIGGERS))return t?t.warnOnce(l.VOICE_TRIGGERS):console.warn(`[NavCore] Feature '${l.VOICE_TRIGGERS}' requires a Pro license`),null;let{distanceToNextInstruction:i,nextInstructionIndex:n}=e;if(n===null||i===null||n>=this.instructions.length)return null;let r=this.instructions[n],o=this.instrId(n),s=i;if(this.triggeredIds.has(o))return this.options.reannounceMeters>0&&!this.reannouncedIds.has(o)&&s<=this.options.reannounceMeters&&s>this.options.earlyTriggerMeters?(this.reannouncedIds.add(o),this.makeCue(r,s,"low")):null;if(s>this.options.earlyTriggerMeters)return null;if(s<-this.options.lateTriggerMeters)return this.triggeredIds.add(o),null;this.triggeredIds.add(o);let a=s<30?"urgent":s<60?"high":"normal";return this.makeCue(r,s,a)}onInstruction(e,t){let i=d.getActive();if(!i||!i.hasFeature(l.VOICE_TRIGGERS))return i?i.warnOnce(l.VOICE_TRIGGERS):console.warn(`[NavCore] Feature '${l.VOICE_TRIGGERS}' requires a Pro license`),{instruction:e,text:"",distanceMetres:0,priority:"normal",triggerId:"bypass"};let n=t.distanceToNextInstruction??0;return this.makeCue(e,n,"high")}reset(){this.triggeredIds.clear(),this.reannouncedIds.clear();}instrId(e){let t=this.instructions[e];return `${e}_${t?.geometryIndex??0}`}makeCue(e,t,i){return {instruction:e,text:e.text??e.instruction??"",distanceMetres:Math.max(0,t),priority:i,triggerId:`${e.geometryIndex}_${t.toFixed(0)}`}}};var G={speedWindowSize:6,minimumSpeedMs:.5,fallbackSpeedMs:8.33},R=class{constructor(e){this.speedSamples=[];this.options={...G,...e},this.lastReliableSpeed=this.options.fallbackSpeedMs;}update(e){let t=d.getActive();if(!t||!t.hasFeature(l.ETA))return t?t.warnOnce(l.ETA):console.warn(`[NavCore] Feature '${l.ETA}' requires a Pro license`),{etaSeconds:0,etaMs:0,distanceRemainingM:0,averageSpeedMs:0,isReliable:false};let i=e.distanceToDestination??0,n=e.currentSpeed??0;this.speedSamples.push(n),this.speedSamples.length>this.options.speedWindowSize&&this.speedSamples.shift();let r=this.rollingAverage();r>=this.options.minimumSpeedMs&&(this.lastReliableSpeed=r);let o=r>=this.options.minimumSpeedMs?r:this.lastReliableSpeed,s=this.speedSamples.length>=Math.ceil(this.options.speedWindowSize/2)&&o>0&&i>0,a=o>0?i/o:0;return {etaSeconds:Math.round(a),etaMs:Date.now()+a*1e3,distanceRemainingM:i,averageSpeedMs:r,isReliable:s}}reset(){this.speedSamples=[],this.lastReliableSpeed=this.options.fallbackSpeedMs;}rollingAverage(){return this.speedSamples.length===0?0:this.speedSamples.reduce((t,i)=>t+i,0)/this.speedSamples.length}};var F={dwellThresholdMs:1e4},S=class{constructor(e){this.fences=new Map;this.states=new Map;if(this.options={...F,...e},this.options.zones)for(let t of this.options.zones)t.type==="circle"&&t.center&&t.radiusMeters?this.addCircle(t.id,t.name,t.center,t.radiusMeters,t.meta):t.type==="polygon"&&t.coords&&this.addPolygon(t.id,t.name,t.coords,t.meta);}addCircle(e,t,i,n,r){return this.fences.set(e,{kind:"circle",id:e,name:t,center:i,radiusM:n,meta:r}),this.states.set(e,{inside:false,enteredAtMs:null}),e}addPolygon(e,t,i,n){if(i.length<3)throw new Error("[Geofencing] Polygon requires at least 3 coordinates");return this.fences.set(e,{kind:"polygon",id:e,name:t,coords:i,meta:n}),this.states.set(e,{inside:false,enteredAtMs:null}),e}remove(e){this.fences.delete(e),this.states.delete(e);}clear(){this.fences.clear(),this.states.clear();}listIds(){return Array.from(this.fences.keys())}update(e){let t=d.getActive();if(!t||!t.hasFeature(l.GEOFENCING))return t?t.warnOnce(l.GEOFENCING):console.warn(`[NavCore] Feature '${l.GEOFENCING}' requires a Pro license`),[];if(!e)return [];let i=[],n=Date.now();for(let[r,o]of this.fences){let s=this.states.get(r),a=this.contains(o,e);if(!s.inside&&a){s.inside=true,s.enteredAtMs=n;let u={id:r,name:o.name,type:"enter",coord:e,meta:o.meta};i.push(u),this.options.onEvent?.(u);}else if(s.inside&&!a){s.inside=false;let u=s.enteredAtMs?n-s.enteredAtMs:0;s.enteredAtMs=null;let p={id:r,name:o.name,type:"exit",coord:e,dwellMs:u,meta:o.meta};i.push(p),this.options.onEvent?.(p);}else if(s.inside&&s.enteredAtMs!==null){let u=n-s.enteredAtMs;if(u>=this.options.dwellThresholdMs){let p={id:r,name:o.name,type:"dwell",coord:e,dwellMs:u,meta:o.meta};i.push(p),this.options.onEvent?.(p),s.enteredAtMs=n;}}}return i}isInside(e,t){let i=this.fences.get(e);return i?this.contains(i,t):false}contains(e,t){return e.kind==="circle"?f(t[1],t[0],e.center[1],e.center[0])<=e.radiusM:this.pointInPolygon(t,e.coords)}pointInPolygon(e,t){let[i,n]=e,r=false,o=t.length;for(let s=0,a=o-1;s<o;a=s++){let[u,p]=t[s],[g,I]=t[a];p>n!=I>n&&i<(g-u)*(n-p)/(I-p)+u&&(r=!r);}return r}};var V=0;function y(){return `instr_${Date.now()}_${++V}`}var w=class{constructor(){this.instructions=[];}addTurn(e,t,i,n){return this.instructions.push({id:y(),type:"turn",location:e,text:t,instruction:t,geometryIndex:i,silent:n?.silent,meta:n?.meta}),this}addExamPoint(e,t,i,n){return this.instructions.push({id:y(),type:"exam_point",location:e,text:t,instruction:t,geometryIndex:i,severity:n?.severity??"info",silent:n?.silent,meta:n?.meta}),this}addCheckpoint(e,t,i){return this.instructions.push({id:y(),type:"checkpoint",location:e,text:"",instruction:"",geometryIndex:t,silent:true,meta:i}),this}addDepart(e,t="Start navigation"){let i={id:y(),type:"depart",location:e,text:t,instruction:t,geometryIndex:0};return this.instructions.unshift(i),this}addArrive(e,t="You have arrived"){return this.instructions.push({id:y(),type:"arrive",location:e,text:t,instruction:t,geometryIndex:Number.MAX_SAFE_INTEGER}),this}update(e,t){let i=this.instructions.findIndex(n=>n.id===e);if(i===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);return this.instructions[i]={...this.instructions[i],...t},this}remove(e){let t=this.instructions.findIndex(i=>i.id===e);if(t===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);return this.instructions.splice(t,1),this}reorder(e,t){let i=this.instructions.findIndex(r=>r.id===e);if(i===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);let[n]=this.instructions.splice(i,1);return this.instructions.splice(Math.max(0,t),0,n),this}sort(){return this.instructions.sort((e,t)=>e.geometryIndex-t.geometryIndex),this}clear(){return this.instructions=[],this}attachToCumulative(e){let t=d.getActive();if(!t||!t.isProEnabled())return console.warn("[NavCore] InstructionEditor requires a Pro license. Fluent editing is disabled."),this;if(e.length<2)return this;let i=[0];for(let n=1;n<e.length;n++){let r=e[n-1],o=e[n];i.push(i[n-1]+f(r[1],r[0],o[1],o[0]));}for(let n of this.instructions){let r=Math.min(n.geometryIndex,e.length-1);n.cumulativeOffset=i[r]??0,r>0&&r<e.length-1&&(n.bearingBefore=m(e[r-1],e[r]),n.bearingAfter=m(e[r],e[r+1]));}return this}toArray(){let e=d.getActive();return !e||!e.isProEnabled()?(console.warn("[NavCore] InstructionEditor.toArray() requires a Pro license. Returning empty instruction set."),[]):this.instructions.map(({id:t,type:i,severity:n,meta:r,...o})=>o)}toRichArray(){return [...this.instructions]}get size(){return this.instructions.length}};export{M as DeadReckoningEngine,E as DeviationDetector,R as ETAEngine,S as GeofencingEngine,w as InstructionEditor,C as ParallelRoadResolver,A as VoiceTriggerEngine,N as calculateDynamicCorridor,D as computeDeadReckoningOnRoute};
|
|
1
|
+
function h(o,e,t,n){let i=(t-o)*(Math.PI/180),r=(n-e)*(Math.PI/180),s=Math.sin(i/2)*Math.sin(i/2)+Math.cos(o*(Math.PI/180))*Math.cos(t*(Math.PI/180))*Math.sin(r/2)*Math.sin(r/2);return 6371e3*(2*Math.atan2(Math.sqrt(s),Math.sqrt(1-s)))}function P(o,e,t,n){let i=n/6371e3,r=t*(Math.PI/180),s=o*(Math.PI/180),a=e*(Math.PI/180),c=Math.asin(Math.sin(s)*Math.cos(i)+Math.cos(s)*Math.sin(i)*Math.cos(r));return [((a+Math.atan2(Math.sin(r)*Math.sin(i)*Math.cos(s),Math.cos(i)-Math.sin(s)*Math.sin(c)))*180/Math.PI+540)%360-180,c*180/Math.PI]}function w(o,e,t,n,i=5e3){if(!o||e<.5)return {predictedCoord:o?.coord??[0,0],isActive:false,ageMs:0};let r=n-o.ts;if(r>i||r<0)return {predictedCoord:o.coord,isActive:false,ageMs:r};let s=e*(r/1e3);return {predictedCoord:P(o.coord[1],o.coord[0],t,s),isActive:true,ageMs:r}}var M=class{constructor(e=5e3){this.anchor=null;this.maxAgeMs=e;}setAnchor(e,t,n){this.anchor={coord:e,idx:t,ts:n};}predict(e,t,n){return w(this.anchor,e,t,n,this.maxAgeMs)}clearAnchor(){this.anchor=null;}getAnchor(){return this.anchor}};function p(o){let e=o%360;return e>180?e-360:e<-180?e+360:e}function d(o,e){let t=o[1]*(Math.PI/180),n=o[0]*(Math.PI/180),i=e[1]*(Math.PI/180),r=e[0]*(Math.PI/180),s=Math.sin(r-n)*Math.cos(i),a=Math.cos(t)*Math.sin(i)-Math.sin(t)*Math.cos(i)*Math.cos(r-n);return (Math.atan2(s,a)*180/Math.PI+360)%360}function G(o,e,t){let n=(i,r,s)=>i+(r-i)*s;return [n(o[0],e[0],t),n(o[1],e[1],t)]}var _=class{evaluate(e,t,n,i,r,s,a){let c=i.distance>s,u=false;if(t!==null&&i.segmentIndex<r.length-1){let m=d(r[i.segmentIndex],r[i.segmentIndex+1]);u=Math.abs(p(t-m))>90;}let l=false;if(a!==null&&n>1.5){let m=r[Math.min(i.segmentIndex+2,r.length-1)];l=h(e[1],e[0],m[1],m[0])>a+5;}let f=c&&(u||l);return {distanceToRoute:i.distance,outsideCorridor:c,bearingMismatch:u,movingAway:l,confirmed:f}}};function z(o,e=null,t=25,n=60,i=1.2,r=.5,s=null,a=1.4,c=40){let u=t+o*i;e!==null&&e>0&&(u+=e*r);let l=n;if(s!==null&&s>=0&&s<=c){let f=1-Math.max(0,s/c),m=1+(a-1)*f;u*=m,l=n*a;}return Math.min(u,l)}function U(o,e=.3){return o<1.5?e*.5:o>20?Math.min(.9,e*2):e}function L(o,e,t=3){let n=Math.min(e+t,o.length-1);return e>=o.length-1||e===n?e>0?d(o[e-1],o[e]):0:d(o[e],o[n])}function y(o,e,t){let n=p(e-o);return (o+t*n+360)%360}function $(o,e,t,n,i,r=.3){let s=U(n,r),a=L(e,t);if(i!==null&&n>1.5){let c=y(o,i,s*.3);return y(c,a,s)}return y(o,a,s)}function X(o,e,t,n,i){return e<o||e>o+30||n>i?o:e}var Y=20,Z=60,K=3;function Q(o,e,t,n,i,r,s,a=0,c=false){if(!e||e.length===0||t>=e.length)return {nextIdx:t,instruction:null,wasTriggered:false};let u=t;for(;u<e.length;){let R=e[u],x=o>R.geometryIndex+30,I=R.cumulativeOffset!==void 0&&s>R.cumulativeOffset+15;if(!x&&!I)break;u++;}if(u>=e.length)return {nextIdx:u,instruction:null,wasTriggered:false};let l=e[u],f=l.cumulativeOffset!==void 0?Math.max(0,l.cumulativeOffset-s):h(i[1],i[0],l.location[1],l.location[0]),m=Math.min(Z,Math.max(Y,a*K)),E=o<=l.geometryIndex+2,F=l.geometryIndex>o+n.length*.15,V=f<=m,B=u===e.length-1,j=c&&B?o>n.length*.8:true,H=V&&E&&!F&&j,D=false;if(H){let R=true,x=l.bearingAfter??(l.geometryIndex<n.length-1?d(n[l.geometryIndex],n[l.geometryIndex+1]):null),I=l.bearingBefore??(l.geometryIndex>0?d(n[l.geometryIndex-1],n[l.geometryIndex]):null);I!==null&&x!==null&&r!==null&&Math.abs(p(x-I))>30&&Math.abs(p(r-x))>55&&(R=false),R&&(D=true);}return {nextIdx:u,instruction:l,wasTriggered:D}}var ee=0;function b(){return `instr_${Date.now()}_${++ee}`}var T=class{constructor(){this.instructions=[];}addTurn(e,t,n,i){return this.instructions.push({id:b(),type:"turn",location:e,text:t,instruction:t,geometryIndex:n,silent:i?.silent,meta:i?.meta}),this}addExamPoint(e,t,n,i){return this.instructions.push({id:b(),type:"exam_point",location:e,text:t,instruction:t,geometryIndex:n,severity:i?.severity??"info",silent:i?.silent,meta:i?.meta}),this}addCheckpoint(e,t,n){return this.instructions.push({id:b(),type:"checkpoint",location:e,text:"",instruction:"",geometryIndex:t,silent:true,meta:n}),this}addDepart(e,t="Start navigation"){let n={id:b(),type:"depart",location:e,text:t,instruction:t,geometryIndex:0};return this.instructions.unshift(n),this}addArrive(e,t="You have arrived"){return this.instructions.push({id:b(),type:"arrive",location:e,text:t,instruction:t,geometryIndex:Number.MAX_SAFE_INTEGER}),this}update(e,t){let n=this.instructions.findIndex(i=>i.id===e);if(n===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);return this.instructions[n]={...this.instructions[n],...t},this}remove(e){let t=this.instructions.findIndex(n=>n.id===e);if(t===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);return this.instructions.splice(t,1),this}reorder(e,t){let n=this.instructions.findIndex(r=>r.id===e);if(n===-1)throw new Error(`[InstructionEditor] Instruction ${e} not found`);let[i]=this.instructions.splice(n,1);return this.instructions.splice(Math.max(0,t),0,i),this}sort(){return this.instructions.sort((e,t)=>e.geometryIndex-t.geometryIndex),this}clear(){return this.instructions=[],this}attachToCumulative(e){if(e.length<2)return this;let t=[0];for(let n=1;n<e.length;n++){let i=e[n-1],r=e[n];t.push(t[n-1]+h(i[1],i[0],r[1],r[0]));}for(let n of this.instructions){let i=Math.min(n.geometryIndex,e.length-1);n.cumulativeOffset=t[i]??0,i>0&&i<e.length-1&&(n.bearingBefore=d(e[i-1],e[i]),n.bearingAfter=d(e[i],e[i+1]));}return this}toArray(){return this.instructions.map(({id:e,type:t,severity:n,meta:i,...r})=>r)}toRichArray(){return [...this.instructions]}get size(){return this.instructions.length}};function te(o,e,t){return o+e*t}function ne(o,e,t=.15){let n=e-o;return o+t*n}function ie(o,e,t){if(t.length===0)return [0,0];if(o<=0)return t[0];let n=e[e.length-1]??0;if(o>=n)return t[t.length-1];let i=0,r=e.length-1;for(;i<r-1;){let l=i+r>>1;e[l]<=o?i=l:r=l;}let s=e[i],c=e[r]-s;if(c===0)return t[i];let u=(o-s)/c;return G(t[i],t[r],u)}var O=class{constructor(){this._state="IDLE";this._context={resumeIndex:null};}get state(){return this._state}get context(){return {...this._context}}dispatch(e){switch(this._state){case "IDLE":e.type==="ROUTE_LOADED"&&(this._state="OVERVIEW"),e.type==="USER_START_NAVIGATION"&&(this._state="APPROACHING_ROUTE");break;case "OVERVIEW":e.type==="USER_START_NAVIGATION"&&(this._state="APPROACHING_ROUTE");break;case "APPROACHING_ROUTE":e.type==="ROUTE_JOINED"&&(this._state="ON_ROUTE",this._context.resumeIndex=e.data.index),e.type==="NAVIGATION_FINISHED"&&(this._state="IDLE");break;case "ON_ROUTE":e.type==="NAVIGATION_FINISHED"&&(this._state="IDLE"),e.type==="ROUTE_JOINED"&&(this._context.resumeIndex=e.data.index);break}return (e.type==="RESET"||e.type==="CANCEL_NAVIGATION")&&(this._state="IDLE",this._context={resumeIndex:null}),{state:this._state,context:{...this._context}}}reset(){this._state="IDLE",this._context={resumeIndex:null};}};var g={deviationSegmentIdx:null,deviationStartMs:null,tempRoute:null,rejoinTarget:null,lastRerouteMs:null,rejoinedAtMs:null,rejoinDwellFrames:0,failureReason:null},k=2e3,re=3,S=class{constructor(){this._state="ON_ROUTE";this._context={...g};}get state(){return this._state}get context(){return {...this._context}}dispatch(e,t){let n=t??Date.now();switch(this._state){case "ON_ROUTE":e.type==="DEVIATION_SUSPECTED"&&(this._state="DEVIATING",this._context.deviationSegmentIdx=e.deviationSegmentIdx,this._context.deviationStartMs=n);break;case "DEVIATING":e.type==="DEVIATION_CONFIRMED"&&(this._context.deviationStartMs!==null?n-this._context.deviationStartMs:0)>=k&&(this._state="OFF_ROUTE",this._context.deviationSegmentIdx=e.deviationSegmentIdx),(e.type==="RESUME"||e.type==="RESET")&&(this._state="ON_ROUTE",e.type==="RESET"?this._context={...g}:(this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null));break;case "OFF_ROUTE":e.type==="REROUTE_STARTED"&&(this._state="REROUTING",this._context.lastRerouteMs=n),e.type==="RESUME"&&(this._state="ON_ROUTE",this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null),e.type==="RESET"&&(this._state="ON_ROUTE",this._context={...g});break;case "REROUTING":e.type==="REROUTE_SUCCESS"&&(this._state="REJOINING",this._context.tempRoute=e.tempRoute,this._context.rejoinTarget=e.rejoinTarget),e.type==="REROUTE_FAILED"&&(this._state="FAILED",this._context.failureReason=e.reason);break;case "REJOINING":e.type==="REJOIN_DETECTED"&&this._context.rejoinDwellFrames++,(e.type==="REJOIN_CONFIRMED"||this._context.rejoinDwellFrames>=re)&&(this._state="REJOINED",this._context.rejoinedAtMs=n,this._context.rejoinDwellFrames=0),e.type==="RESUME"&&(this._state="ON_ROUTE",this._context.tempRoute=null,this._context.rejoinTarget=null,this._context.rejoinedAtMs=null,this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null,this._context.rejoinDwellFrames=0),e.type==="RESET"&&(this._state="ON_ROUTE",this._context={...g});break;case "REJOINED":e.type==="RESUME"&&(this._state="ON_ROUTE",this._context.tempRoute=null,this._context.rejoinTarget=null,this._context.rejoinedAtMs=null,this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null),e.type==="RESET"&&(this._state="ON_ROUTE",this._context={...g});break;case "FAILED":e.type==="RESUME"&&(this._state="ON_ROUTE",this._context.failureReason=null,this._context.deviationSegmentIdx=null,this._context.deviationStartMs=null),e.type==="RESET"&&(this._state="ON_ROUTE",this._context={...g});break}return {state:this._state,context:{...this._context}}}reset(){this._state="ON_ROUTE",this._context={...g};}isDeviationConfirmed(e=Date.now()){return this._context.deviationStartMs?e-this._context.deviationStartMs>=k:false}};var oe={bearingPenalty:80,bearingMismatchThresholdDeg:60,inertiaPenalty:40,inertiaSegmentDist:3},C=class{constructor(e){this.options={...oe,...e};}resolve(e,t=null,n=null){return e.map(i=>this.score(i,t,n)).sort((i,r)=>i.score-r.score)}resolveBest(e,t=null,n=null){return e.length===0?null:this.resolve(e,t,n)[0]??null}score(e,t,n){let i=e.distanceMetres,r=null,s=false;if(t!==null){let a=d(e.segA,e.segB),c=Math.abs(p(t-a));r=c,c>this.options.bearingMismatchThresholdDeg&&(i+=this.options.bearingPenalty);}return n!==null&&Math.abs(e.segmentIndex-n)>=this.options.inertiaSegmentDist&&(i+=this.options.inertiaPenalty,s=true),{...e,score:i,bearingDeltaDeg:r,inertiaPenaltyApplied:s}}isLikelyUTurn(e,t,n){let i=d(t,n);return Math.abs(p(e-i))>this.options.bearingMismatchThresholdDeg}};var se={earlyTriggerMeters:120,lateTriggerMeters:20,reannounceMeters:300},v=class{constructor(e){this.instructions=[];this.triggeredIds=new Set;this.reannouncedIds=new Set;this.options={...se,...e};}setInstructions(e){this.instructions=[...e],this.triggeredIds.clear(),this.reannouncedIds.clear();}update(e){let{distanceToNextInstruction:t,nextInstructionIndex:n}=e;if(n===null||t===null||n>=this.instructions.length)return null;let i=this.instructions[n],r=this.instrId(n),s=t;if(this.triggeredIds.has(r))return this.options.reannounceMeters>0&&!this.reannouncedIds.has(r)&&s<=this.options.reannounceMeters&&s>this.options.earlyTriggerMeters?(this.reannouncedIds.add(r),this.makeCue(i,s,"low")):null;if(s>this.options.earlyTriggerMeters)return null;if(s<-this.options.lateTriggerMeters)return this.triggeredIds.add(r),null;this.triggeredIds.add(r);let a=s<30?"urgent":s<60?"high":"normal";return this.makeCue(i,s,a)}onInstruction(e,t){let n=t.distanceToNextInstruction??0;return this.makeCue(e,n,"high")}reset(){this.triggeredIds.clear(),this.reannouncedIds.clear();}instrId(e){let t=this.instructions[e];return `${e}_${t?.geometryIndex??0}`}makeCue(e,t,n){return {instruction:e,text:e.text??e.instruction??"",distanceMetres:Math.max(0,t),priority:n,triggerId:`${e.geometryIndex}_${t.toFixed(0)}`}}};var ae={speedWindowSize:6,minimumSpeedMs:.5,fallbackSpeedMs:8.33},A=class{constructor(e){this.speedSamples=[];this.options={...ae,...e},this.lastReliableSpeed=this.options.fallbackSpeedMs;}update(e){let t=e.distanceToDestination??0,n=e.currentSpeed??0;this.speedSamples.push(n),this.speedSamples.length>this.options.speedWindowSize&&this.speedSamples.shift();let i=this.rollingAverage();i>=this.options.minimumSpeedMs&&(this.lastReliableSpeed=i);let r=i>=this.options.minimumSpeedMs?i:this.lastReliableSpeed,s=this.speedSamples.length>=Math.ceil(this.options.speedWindowSize/2)&&r>0&&t>0,a=r>0?t/r:0;return {etaSeconds:Math.round(a),etaMs:Date.now()+a*1e3,distanceRemainingM:t,averageSpeedMs:i,isReliable:s}}reset(){this.speedSamples=[],this.lastReliableSpeed=this.options.fallbackSpeedMs;}rollingAverage(){return this.speedSamples.length===0?0:this.speedSamples.reduce((t,n)=>t+n,0)/this.speedSamples.length}};var ce={dwellThresholdMs:1e4},N=class{constructor(e){this.fences=new Map;this.states=new Map;if(this.options={...ce,...e},this.options.zones)for(let t of this.options.zones)t.type==="circle"&&t.center&&t.radiusMeters?this.addCircle(t.id,t.name,t.center,t.radiusMeters,t.meta):t.type==="polygon"&&t.coords&&this.addPolygon(t.id,t.name,t.coords,t.meta);}addCircle(e,t,n,i,r){return this.fences.set(e,{kind:"circle",id:e,name:t,center:n,radiusM:i,meta:r}),this.states.set(e,{inside:false,enteredAtMs:null}),e}addPolygon(e,t,n,i){if(n.length<3)throw new Error("[Geofencing] Polygon requires at least 3 coordinates");return this.fences.set(e,{kind:"polygon",id:e,name:t,coords:n,meta:i}),this.states.set(e,{inside:false,enteredAtMs:null}),e}remove(e){this.fences.delete(e),this.states.delete(e);}clear(){this.fences.clear(),this.states.clear();}listIds(){return Array.from(this.fences.keys())}update(e){if(!e)return [];let t=[],n=Date.now();for(let[i,r]of this.fences){let s=this.states.get(i),a=this.contains(r,e);if(!s.inside&&a){s.inside=true,s.enteredAtMs=n;let c={id:i,name:r.name,type:"enter",coord:e,meta:r.meta};t.push(c),this.options.onEvent?.(c);}else if(s.inside&&!a){s.inside=false;let c=s.enteredAtMs?n-s.enteredAtMs:0;s.enteredAtMs=null;let u={id:i,name:r.name,type:"exit",coord:e,dwellMs:c,meta:r.meta};t.push(u),this.options.onEvent?.(u);}else if(s.inside&&s.enteredAtMs!==null){let c=n-s.enteredAtMs;if(c>=this.options.dwellThresholdMs){let u={id:i,name:r.name,type:"dwell",coord:e,dwellMs:c,meta:r.meta};t.push(u),this.options.onEvent?.(u),s.enteredAtMs=n;}}}return t}isInside(e,t){let n=this.fences.get(e);return n?this.contains(n,t):false}contains(e,t){return e.kind==="circle"?h(t[1],t[0],e.center[1],e.center[0])<=e.radiusM:this.pointInPolygon(t,e.coords)}pointInPolygon(e,t){let[n,i]=e,r=false,s=t.length;for(let a=0,c=s-1;a<s;c=a++){let[u,l]=t[a],[f,m]=t[c];l>i!=m>i&&n<(f-u)*(i-l)/(m-l)+u&&(r=!r);}return r}};export{M as DeadReckoningEngine,_ as DeviationDetector,A as ETAEngine,N as GeofencingEngine,T as InstructionEditor,O as NavigationFSM,S as OffRouteFSM,C as ParallelRoadResolver,v as VoiceTriggerEngine,te as advancePredictor,$ as applyBearingSmoothing,ne as applyGpsCorrection,y as blendBearing,z as calculateDynamicCorridor,L as calculateRobustBearing,w as computeDeadReckoningOnRoute,U as getSpeedAdaptiveAlpha,ie as pointAtOffset,Q as resolveNextInstruction,X as validateProgressUpdate};
|